GuoYuFu123 il y a 6 ans
commit
048fc8b8f7
100 fichiers modifiés avec 17093 ajouts et 0 suppressions
  1. 12 0
      .babelrc
  2. 9 0
      .editorconfig
  3. 14 0
      .gitignore
  4. 10 0
      .postcssrc.js
  5. 96 0
      README.md
  6. 18 0
      STANDARD.md
  7. 41 0
      build/build.js
  8. 54 0
      build/check-versions.js
  9. BIN
      build/logo.png
  10. 101 0
      build/utils.js
  11. 22 0
      build/vue-loader.conf.js
  12. 91 0
      build/webpack.base.conf.js
  13. 95 0
      build/webpack.dev.conf.js
  14. 145 0
      build/webpack.prod.conf.js
  15. 8 0
      config/dev.env.js
  16. 123 0
      config/index.js
  17. 5 0
      config/prod.env.js
  18. 11 0
      index.html
  19. 76 0
      package.json
  20. 11 0
      src/App.vue
  21. 205 0
      src/api/alarm/alarm.js
  22. 10 0
      src/api/database/hbase.js
  23. 12 0
      src/api/database/redis.js
  24. 72 0
      src/api/fm/rules.js
  25. 83 0
      src/api/fm/sop.js
  26. 5 0
      src/api/scan/config.js
  27. 30 0
      src/api/scan/fetch.js
  28. 50 0
      src/api/scan/httpUtil.js
  29. 764 0
      src/api/scan/request.js
  30. 6 0
      src/api/system/admin.js
  31. 21 0
      src/api/system/role.js
  32. 15 0
      src/api/system/user.js
  33. 477 0
      src/assets/css/jsmind.css
  34. 44 0
      src/assets/graphy/SGraphy/SCanvas.js
  35. 30 0
      src/assets/graphy/SGraphy/SGraphyEnums.js
  36. 396 0
      src/assets/graphy/SGraphy/SGraphyItem.js
  37. 201 0
      src/assets/graphy/SGraphy/SGraphyScene.js
  38. 280 0
      src/assets/graphy/SGraphy/SGraphyView.js
  39. 53 0
      src/assets/graphy/SGraphy/SMouseEvent.js
  40. 70 0
      src/assets/graphy/SGraphy/items/SGraphyCircleItem.js
  41. 201 0
      src/assets/graphy/SGraphy/items/SGraphyClockItem.js
  42. 59 0
      src/assets/graphy/SGraphy/items/SGraphyImageItem.js
  43. 66 0
      src/assets/graphy/SGraphy/items/SGraphyLineItem.js
  44. 76 0
      src/assets/graphy/SGraphy/items/SGraphyPillarItems.js
  45. 257 0
      src/assets/graphy/SGraphy/items/SGraphyPolygonItem.js
  46. 100 0
      src/assets/graphy/SGraphy/items/SGraphyRectItem.js
  47. 51 0
      src/assets/graphy/SGraphy/items/SGraphyTextItems.js
  48. 66 0
      src/assets/graphy/SGraphy/items/SGraphyVirtualItem.js
  49. 40 0
      src/assets/graphy/SGraphy/types/SPoint.js
  50. 169 0
      src/assets/graphy/SGraphy/types/SRect.js
  51. 13 0
      src/assets/graphy/index.js
  52. 1378 0
      src/assets/js/chosen.jquery.min.js
  53. 327 0
      src/assets/js/handsontable-chosen-editor.js
  54. 70 0
      src/assets/js/items/SGraphyCircleItem.js
  55. 59 0
      src/assets/js/items/SGraphyImageItem.js
  56. 66 0
      src/assets/js/items/SGraphyLineItem.js
  57. 76 0
      src/assets/js/items/SGraphyPillarItems.js
  58. 257 0
      src/assets/js/items/SGraphyPolygonItem.js
  59. 100 0
      src/assets/js/items/SGraphyRectItem.js
  60. 51 0
      src/assets/js/items/SGraphyTextItems.js
  61. 66 0
      src/assets/js/items/SGraphyVirtualItem.js
  62. 2934 0
      src/assets/js/jsmind.js
  63. BIN
      src/assets/logo.png
  64. 44 0
      src/assets/node-templete/SGraphy/SCanvas.js
  65. 30 0
      src/assets/node-templete/SGraphy/SGraphyEnums.js
  66. 396 0
      src/assets/node-templete/SGraphy/SGraphyItem.js
  67. 201 0
      src/assets/node-templete/SGraphy/SGraphyScene.js
  68. 280 0
      src/assets/node-templete/SGraphy/SGraphyView.js
  69. 53 0
      src/assets/node-templete/SGraphy/SMouseEvent.js
  70. 201 0
      src/assets/node-templete/SGraphy/items/SGraphyClockItem.js
  71. 58 0
      src/assets/node-templete/SGraphy/items/SGraphyLineItem.js
  72. 57 0
      src/assets/node-templete/SGraphy/items/SGraphyPolygonItem.js
  73. 64 0
      src/assets/node-templete/SGraphy/items/SGraphyRectItem.js
  74. 61 0
      src/assets/node-templete/SGraphy/items/SGraphyVirtualItem.js
  75. 40 0
      src/assets/node-templete/SGraphy/types/SPoint.js
  76. 169 0
      src/assets/node-templete/SGraphy/types/SRect.js
  77. 9 0
      src/assets/node-templete/index.js
  78. 37 0
      src/assets/pako/pako.js
  79. 120 0
      src/assets/scss/point/point_config/common.scss
  80. 473 0
      src/assets/scss/point/point_config/reset.scss
  81. 473 0
      src/assets/scss/reset.scss
  82. 117 0
      src/components/alarm/common/AlarmClass.vue
  83. 237 0
      src/components/alarm/common/AlarmItem.vue
  84. 121 0
      src/components/alarm/common/CheckBox.vue
  85. 153 0
      src/components/alarm/common/ObjLocation.vue
  86. 313 0
      src/components/alarm/common/ObjType.vue
  87. 45 0
      src/components/alarm/common/utils.js
  88. 627 0
      src/components/business_space/business/handsontable.vue
  89. 766 0
      src/components/business_space/dialogs/addDialog/businessDialog.vue
  90. 344 0
      src/components/business_space/dialogs/changeRea.vue
  91. 203 0
      src/components/business_space/dialogs/detailsDialog.vue
  92. 148 0
      src/components/business_space/dialogs/facilityDialog.vue
  93. 150 0
      src/components/business_space/dialogs/influenceDialog.vue
  94. 50 0
      src/components/business_space/dialogs/list/filesDialog.vue
  95. 162 0
      src/components/business_space/dialogs/list/firm.vue
  96. 187 0
      src/components/business_space/dialogs/list/guaranteeDialog.vue
  97. 184 0
      src/components/business_space/dialogs/list/insurerDialog.vue
  98. 184 0
      src/components/business_space/dialogs/list/maintainerDialog.vue
  99. 88 0
      src/components/business_space/dialogs/list/paramDetails.vue
  100. 0 0
      src/components/business_space/dialogs/list/picDialog.vue

+ 12 - 0
.babelrc

@@ -0,0 +1,12 @@
+{
+  "presets": [
+    ["env", {
+      "modules": false,
+      "targets": {
+        "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
+      }
+    }],
+    "stage-2"
+  ],
+  "plugins": ["transform-vue-jsx", "transform-runtime"]
+}

+ 9 - 0
.editorconfig

@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+.DS_Store
+node_modules/
+/dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln

+ 10 - 0
.postcssrc.js

@@ -0,0 +1,10 @@
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+  "plugins": {
+    "postcss-import": {},
+    "postcss-url": {},
+    // to edit target browsers: use "browserslist" field in package.json
+    "autoprefixer": {}
+  }
+}

+ 96 - 0
README.md

@@ -0,0 +1,96 @@
+# sagacloud-admin
+
+> A Vue.js project
+
+## Build Setup
+
+```bash
+# install dependencies
+npm install
+npm install vuex axios element-ui echarts font-awesome
+
+# serve with hot reload at localhost:8080
+npm run dev
+npm install less less-loader node-sass sass-loader --save-dev
+
+# build for production with minification
+npm run build
+
+# build for production and view the bundle analyzer report
+npm run build --report
+```
+
+For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
+
+#### 目录结构
+
+```
+├─api                       服务端接口API
+│
+├─components                通用组件
+│
+├─data                      静态数据
+│      menus.js             页面菜单文件,在这里新增菜单
+│
+├─framework                 页面框架, 不用修改
+│
+├─plugins                   通用插件
+│
+├─router                    路由
+│      index.js
+│      sagacloud.js         业务路由文件,在这里新增路由
+│
+├─store                     VUEX
+│      index.js
+│
+├─utils                     通用工具类
+│      authutils.js         认证工具, 不要修改
+│      basicutils.js
+│      httputils.js
+│      storage.js
+│
+└─views                     业务视图主目录, 这里创建新视图
+    ├─dasboard
+    └─system
+```
+
+#### 工具类说明
+
+/src/utils/basicutils.js
+
+```
+    formatDateByPattern(date, pattern) // 日期格式化
+    indexInArray(arr, key, val)        // 找出数组arr中第一个属性key的值等于val的元素的下标
+    itemInArray(arr, key, val)         // 找出数组arr中第一个属性key的值等于val的元素
+    deleteInArray(arr, key, val)       // 从数组中将属性key的值等于val的所有元素删除
+```
+
+/src/utils/httputils.js // 一般在服务端接口 API 中使用,不建议在别的地方使用,有授权标记
+
+```
+    getCookie(name)                    // 获取cookie值
+    getJson(url, params)               // 发送GET请求
+    postJson(url, data)                // 发送POST请求
+```
+
+/src/utils/storage.js
+
+```
+    set(key, value, fn)                // 在浏览器缓存中设置数据, 如果value是对象会转化成json
+    get(key, fn)                       // 在浏览器缓存中取出数据, 取出数据后会尝试js对象返回
+    remove(key)                        // 在浏览器缓存中删除数据
+```
+
+#### 设置面包屑方法
+
+新方法
+
+```
+在路由router中配置meta.breadcrumbs,在进入路由页面时会自动解析为面包屑
+```
+
+下面的方法不再推荐使用
+
+```
+    this.$store.dispatch('setBreadcrumb', [{ label: 'Demo' }, { label: 'Form' }])
+```

+ 18 - 0
STANDARD.md

@@ -0,0 +1,18 @@
+#### 色彩
+```
+文字色彩:
+
+    默认
+
+    table前搜索文案色彩class:color2
+
+    table中活跃按钮使用ele文字按钮text
+
+border颜色:
+
+    border的颜色使用class:border-color1
+
+划过手指添加class: pointer
+
+除了必须的大小调整,否则任何ele组件不添加size属性
+```

+ 41 - 0
build/build.js

@@ -0,0 +1,41 @@
+'use strict'
+require('./check-versions')()
+
+process.env.NODE_ENV = 'production'
+
+const ora = require('ora')
+const rm = require('rimraf')
+const path = require('path')
+const chalk = require('chalk')
+const webpack = require('webpack')
+const config = require('../config')
+const webpackConfig = require('./webpack.prod.conf')
+
+const spinner = ora('building for production...')
+spinner.start()
+
+rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
+  if (err) throw err
+  webpack(webpackConfig, (err, stats) => {
+    spinner.stop()
+    if (err) throw err
+    process.stdout.write(stats.toString({
+      colors: true,
+      modules: false,
+      children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
+      chunks: false,
+      chunkModules: false
+    }) + '\n\n')
+
+    if (stats.hasErrors()) {
+      console.log(chalk.red('  Build failed with errors.\n'))
+      process.exit(1)
+    }
+
+    console.log(chalk.cyan('  Build complete.\n'))
+    console.log(chalk.yellow(
+      '  Tip: built files are meant to be served over an HTTP server.\n' +
+      '  Opening index.html over file:// won\'t work.\n'
+    ))
+  })
+})

+ 54 - 0
build/check-versions.js

@@ -0,0 +1,54 @@
+'use strict'
+const chalk = require('chalk')
+const semver = require('semver')
+const packageConfig = require('../package.json')
+const shell = require('shelljs')
+
+function exec (cmd) {
+  return require('child_process').execSync(cmd).toString().trim()
+}
+
+const versionRequirements = [
+  {
+    name: 'node',
+    currentVersion: semver.clean(process.version),
+    versionRequirement: packageConfig.engines.node
+  }
+]
+
+if (shell.which('npm')) {
+  versionRequirements.push({
+    name: 'npm',
+    currentVersion: exec('npm --version'),
+    versionRequirement: packageConfig.engines.npm
+  })
+}
+
+module.exports = function () {
+  const warnings = []
+
+  for (let i = 0; i < versionRequirements.length; i++) {
+    const mod = versionRequirements[i]
+
+    if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
+      warnings.push(mod.name + ': ' +
+        chalk.red(mod.currentVersion) + ' should be ' +
+        chalk.green(mod.versionRequirement)
+      )
+    }
+  }
+
+  if (warnings.length) {
+    console.log('')
+    console.log(chalk.yellow('To use this template, you must update following to modules:'))
+    console.log()
+
+    for (let i = 0; i < warnings.length; i++) {
+      const warning = warnings[i]
+      console.log('  ' + warning)
+    }
+
+    console.log()
+    process.exit(1)
+  }
+}

BIN
build/logo.png


+ 101 - 0
build/utils.js

@@ -0,0 +1,101 @@
+'use strict'
+const path = require('path')
+const config = require('../config')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const packageConfig = require('../package.json')
+
+exports.assetsPath = function (_path) {
+  const assetsSubDirectory = process.env.NODE_ENV === 'production'
+    ? config.build.assetsSubDirectory
+    : config.dev.assetsSubDirectory
+
+  return path.posix.join(assetsSubDirectory, _path)
+}
+
+exports.cssLoaders = function (options) {
+  options = options || {}
+
+  const cssLoader = {
+    loader: 'css-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  const postcssLoader = {
+    loader: 'postcss-loader',
+    options: {
+      sourceMap: options.sourceMap
+    }
+  }
+
+  // generate loader string to be used with extract text plugin
+  function generateLoaders (loader, loaderOptions) {
+    const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
+
+    if (loader) {
+      loaders.push({
+        loader: loader + '-loader',
+        options: Object.assign({}, loaderOptions, {
+          sourceMap: options.sourceMap
+        })
+      })
+    }
+
+    // Extract CSS when that option is specified
+    // (which is the case during production build)
+    if (options.extract) {
+      return ExtractTextPlugin.extract({
+        use: loaders,
+        fallback: 'vue-style-loader'
+      })
+    } else {
+      return ['vue-style-loader'].concat(loaders)
+    }
+  }
+
+  // https://vue-loader.vuejs.org/en/configurations/extract-css.html
+  return {
+    css: generateLoaders(),
+    postcss: generateLoaders(),
+    less: generateLoaders('less'),
+    sass: generateLoaders('sass', { indentedSyntax: true }),
+    scss: generateLoaders('sass'),
+    stylus: generateLoaders('stylus'),
+    styl: generateLoaders('stylus')
+  }
+}
+
+// Generate loaders for standalone style files (outside of .vue)
+exports.styleLoaders = function (options) {
+  const output = []
+  const loaders = exports.cssLoaders(options)
+
+  for (const extension in loaders) {
+    const loader = loaders[extension]
+    output.push({
+      test: new RegExp('\\.' + extension + '$'),
+      use: loader
+    })
+  }
+
+  return output
+}
+
+exports.createNotifierCallback = () => {
+  const notifier = require('node-notifier')
+
+  return (severity, errors) => {
+    if (severity !== 'error') return
+
+    const error = errors[0]
+    const filename = error.file && error.file.split('!').pop()
+
+    notifier.notify({
+      title: packageConfig.name,
+      message: severity + ': ' + error.name,
+      subtitle: filename || '',
+      icon: path.join(__dirname, 'logo.png')
+    })
+  }
+}

+ 22 - 0
build/vue-loader.conf.js

@@ -0,0 +1,22 @@
+'use strict'
+const utils = require('./utils')
+const config = require('../config')
+const isProduction = process.env.NODE_ENV === 'production'
+const sourceMapEnabled = isProduction
+  ? config.build.productionSourceMap
+  : config.dev.cssSourceMap
+
+module.exports = {
+  loaders: utils.cssLoaders({
+    sourceMap: sourceMapEnabled,
+    extract: isProduction
+  }),
+  cssSourceMap: sourceMapEnabled,
+  cacheBusting: config.dev.cacheBusting,
+  transformToRequire: {
+    video: ['src', 'poster'],
+    source: 'src',
+    img: 'src',
+    image: 'xlink:href'
+  }
+}

+ 91 - 0
build/webpack.base.conf.js

@@ -0,0 +1,91 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const config = require('../config')
+const vueLoaderConfig = require('./vue-loader.conf')
+const webpack = require('webpack')
+
+function resolve (dir) {
+  return path.join(__dirname, '..', dir)
+}
+
+
+
+module.exports = {
+  context: path.resolve(__dirname, '../'),
+  entry: {
+    app: './src/main.js'
+  },
+  output: {
+    path: config.build.assetsRoot,
+    filename: '[name].js',
+    publicPath: process.env.NODE_ENV === 'production'
+      ? config.build.assetsPublicPath
+      : config.dev.assetsPublicPath
+  },
+  resolve: {
+    extensions: ['.js', '.vue', '.json'],
+    alias: {
+      'vue$': 'vue/dist/vue.esm.js',
+      '@': resolve('src'),
+        'jquery': 'jquery',
+    }
+  },
+    // 增加一个plugins
+    plugins: [
+        new webpack.ProvidePlugin({
+            $: "jquery",
+            jQuery: "jquery"
+        })
+    ],
+  module: {
+    rules: [
+      {
+        test: /\.vue$/,
+        loader: 'vue-loader',
+        options: vueLoaderConfig
+      },
+      {
+        test: /\.js$/,
+        loader: 'babel-loader',
+        include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
+      },
+      {
+        test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('img/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('media/[name].[hash:7].[ext]')
+        }
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000,
+          name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
+        }
+      }
+    ]
+  },
+  node: {
+    // prevent webpack from injecting useless setImmediate polyfill because Vue
+    // source contains it (although only uses it if it's native).
+    setImmediate: false,
+    // prevent webpack from injecting mocks to Node native modules
+    // that does not make sense for the client
+    dgram: 'empty',
+    fs: 'empty',
+    net: 'empty',
+    tls: 'empty',
+    child_process: 'empty'
+  }
+}

+ 95 - 0
build/webpack.dev.conf.js

@@ -0,0 +1,95 @@
+'use strict'
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const path = require('path')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
+const portfinder = require('portfinder')
+
+const HOST = process.env.HOST
+const PORT = process.env.PORT && Number(process.env.PORT)
+
+const devWebpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
+  },
+  // cheap-module-eval-source-map is faster for development
+  devtool: config.dev.devtool,
+
+  // these devServer options should be customized in /config/index.js
+  devServer: {
+    clientLogLevel: 'warning',
+    historyApiFallback: {
+      rewrites: [
+        { from: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
+      ],
+    },
+    hot: true,
+    contentBase: false, // since we use CopyWebpackPlugin.
+    compress: true,
+    host: HOST || config.dev.host,
+    port: PORT || config.dev.port,
+    open: config.dev.autoOpenBrowser,
+    overlay: config.dev.errorOverlay
+      ? { warnings: false, errors: true }
+      : false,
+    publicPath: config.dev.assetsPublicPath,
+    proxy: config.dev.proxyTable,
+    quiet: true, // necessary for FriendlyErrorsPlugin
+    watchOptions: {
+      poll: config.dev.poll,
+    }
+  },
+  plugins: [
+    new webpack.DefinePlugin({
+      'process.env': require('../config/dev.env')
+    }),
+    new webpack.HotModuleReplacementPlugin(),
+    new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
+    new webpack.NoEmitOnErrorsPlugin(),
+    // https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: 'index.html',
+      template: 'index.html',
+      inject: true
+    }),
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.dev.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+module.exports = new Promise((resolve, reject) => {
+  portfinder.basePort = process.env.PORT || config.dev.port
+  portfinder.getPort((err, port) => {
+    if (err) {
+      reject(err)
+    } else {
+      // publish the new Port, necessary for e2e tests
+      process.env.PORT = port
+      // add port to devServer config
+      devWebpackConfig.devServer.port = port
+
+      // Add FriendlyErrorsPlugin
+      devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
+        compilationSuccessInfo: {
+          messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
+        },
+        onErrors: config.dev.notifyOnErrors
+        ? utils.createNotifierCallback()
+        : undefined
+      }))
+
+      resolve(devWebpackConfig)
+    }
+  })
+})

+ 145 - 0
build/webpack.prod.conf.js

@@ -0,0 +1,145 @@
+'use strict'
+const path = require('path')
+const utils = require('./utils')
+const webpack = require('webpack')
+const config = require('../config')
+const merge = require('webpack-merge')
+const baseWebpackConfig = require('./webpack.base.conf')
+const CopyWebpackPlugin = require('copy-webpack-plugin')
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+const ExtractTextPlugin = require('extract-text-webpack-plugin')
+const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
+const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
+
+const env = require('../config/prod.env')
+
+const webpackConfig = merge(baseWebpackConfig, {
+  module: {
+    rules: utils.styleLoaders({
+      sourceMap: config.build.productionSourceMap,
+      extract: true,
+      usePostCSS: true
+    })
+  },
+  devtool: config.build.productionSourceMap ? config.build.devtool : false,
+  output: {
+    path: config.build.assetsRoot,
+    filename: utils.assetsPath('js/[name].[chunkhash].js'),
+    chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
+  },
+  plugins: [
+    // http://vuejs.github.io/vue-loader/en/workflow/production.html
+    new webpack.DefinePlugin({
+      'process.env': env
+    }),
+    new UglifyJsPlugin({
+      uglifyOptions: {
+        compress: {
+          warnings: false
+        }
+      },
+      sourceMap: config.build.productionSourceMap,
+      parallel: true
+    }),
+    // extract css into its own file
+    new ExtractTextPlugin({
+      filename: utils.assetsPath('css/[name].[contenthash].css'),
+      // Setting the following option to `false` will not extract CSS from codesplit chunks.
+      // Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
+      // It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`, 
+      // increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
+      allChunks: true,
+    }),
+    // Compress extracted CSS. We are using this plugin so that possible
+    // duplicated CSS from different components can be deduped.
+    new OptimizeCSSPlugin({
+      cssProcessorOptions: config.build.productionSourceMap
+        ? { safe: true, map: { inline: false } }
+        : { safe: true }
+    }),
+    // generate dist index.html with correct asset hash for caching.
+    // you can customize output by editing /index.html
+    // see https://github.com/ampedandwired/html-webpack-plugin
+    new HtmlWebpackPlugin({
+      filename: config.build.index,
+      template: 'index.html',
+      inject: true,
+      minify: {
+        removeComments: true,
+        collapseWhitespace: true,
+        removeAttributeQuotes: true
+        // more options:
+        // https://github.com/kangax/html-minifier#options-quick-reference
+      },
+      // necessary to consistently work with multiple chunks via CommonsChunkPlugin
+      chunksSortMode: 'dependency'
+    }),
+    // keep module.id stable when vendor modules does not change
+    new webpack.HashedModuleIdsPlugin(),
+    // enable scope hoisting
+    new webpack.optimize.ModuleConcatenationPlugin(),
+    // split vendor js into its own file
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'vendor',
+      minChunks (module) {
+        // any required modules inside node_modules are extracted to vendor
+        return (
+          module.resource &&
+          /\.js$/.test(module.resource) &&
+          module.resource.indexOf(
+            path.join(__dirname, '../node_modules')
+          ) === 0
+        )
+      }
+    }),
+    // extract webpack runtime and module manifest to its own file in order to
+    // prevent vendor hash from being updated whenever app bundle is updated
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'manifest',
+      minChunks: Infinity
+    }),
+    // This instance extracts shared chunks from code splitted chunks and bundles them
+    // in a separate chunk, similar to the vendor chunk
+    // see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
+    new webpack.optimize.CommonsChunkPlugin({
+      name: 'app',
+      async: 'vendor-async',
+      children: true,
+      minChunks: 3
+    }),
+
+    // copy custom static assets
+    new CopyWebpackPlugin([
+      {
+        from: path.resolve(__dirname, '../static'),
+        to: config.build.assetsSubDirectory,
+        ignore: ['.*']
+      }
+    ])
+  ]
+})
+
+if (config.build.productionGzip) {
+  const CompressionWebpackPlugin = require('compression-webpack-plugin')
+
+  webpackConfig.plugins.push(
+    new CompressionWebpackPlugin({
+      asset: '[path].gz[query]',
+      algorithm: 'gzip',
+      test: new RegExp(
+        '\\.(' +
+        config.build.productionGzipExtensions.join('|') +
+        ')$'
+      ),
+      threshold: 10240,
+      minRatio: 0.8
+    })
+  )
+}
+
+if (config.build.bundleAnalyzerReport) {
+  const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
+  webpackConfig.plugins.push(new BundleAnalyzerPlugin())
+}
+
+module.exports = webpackConfig

+ 8 - 0
config/dev.env.js

@@ -0,0 +1,8 @@
+'use strict'
+const merge = require('webpack-merge')
+const prodEnv = require('./prod.env')
+
+module.exports = merge(prodEnv, {
+    NODE_ENV: '"development"',
+    SSO_SERVER: '"http://192.168.20.101:8004"'
+})

+ 123 - 0
config/index.js

@@ -0,0 +1,123 @@
+'use strict'
+// Template version: 1.3.1
+// see http://vuejs-templates.github.io/webpack for documentation.
+
+const path = require('path')
+
+module.exports = {
+    dev: {
+        // Paths
+        assetsSubDirectory: 'static',
+        assetsPublicPath: '/',
+        proxyTable: {
+            '/admin': {
+                target: 'http://localhost:52009',
+                changeOrigin: true,
+                pathRewrite: {
+                    // "^/apis": ""
+                }
+            },
+            '/data-platform-3': {
+                // 目标 API 地址
+                target: 'http://192.168.20.225:8080/',
+                // 如果要代理 websockets
+                ws: true,
+                // 将主机标头的原点更改为目标URL
+                changeOrigin: false
+            },
+            '/business-space': {
+                // 目标 API 地址
+                target: 'http://192.168.20.225:8080/',
+                // 如果要代理 websockets
+                ws: true,
+                // 将主机标头的原点更改为目标URL
+                changeOrigin: false
+            },
+            '/pointconfig': {
+                // 目标 API 地址
+                target: 'http://172.16.0.141:8080/',
+                // 如果要代理 websockets
+                ws: true,
+                // 将主机标头的原点更改为目标URL
+                changeOrigin: false
+            },
+            '/venders': {
+                // 目标 API 地址
+                target: 'http://192.168.20.225:8080/',
+                // 如果要代理 websockets
+                ws: true,
+                // 将主机标头的原点更改为目标URL
+                changeOrigin: false
+            },
+            '/ScanBuilding': {
+                // 目标 API 地址
+                target: 'http://172.16.0.141:8080/',
+                // 如果要代理 websockets
+                ws: true,
+                // 将主机标头的原点更改为目标URL
+                changeOrigin: false
+            },
+            '/image-service': {
+                // 目标 API 地址
+                target: 'http://192.168.20.225:8080/',
+                // 如果要代理 websockets
+                ws: true,
+                // 将主机标头的原点更改为目标URL
+                changeOrigin: false
+            }
+        },
+
+        // Various Dev Server settings
+        host: '0.0.0.0', // can be overwritten by process.env.HOST
+        port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
+        autoOpenBrowser: false,
+        errorOverlay: true,
+        notifyOnErrors: true,
+        poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
+
+        /**
+         * Source Maps
+         */
+
+        // https://webpack.js.org/configuration/devtool/#development
+        devtool: 'cheap-module-eval-source-map',
+
+        // If you have problems debugging vue-files in devtools,
+        // set this to false - it *may* help
+        // https://vue-loader.vuejs.org/en/options.html#cachebusting
+        cacheBusting: true,
+
+        cssSourceMap: true
+    },
+
+    build: {
+        // Template for index.html
+        index: path.resolve(__dirname, '../dist/index.html'),
+
+        // Paths
+        assetsRoot: path.resolve(__dirname, '../dist'),
+        assetsSubDirectory: 'static',
+        assetsPublicPath: '/',
+
+        /**
+         * Source Maps
+         */
+
+        productionSourceMap: true,
+        // https://webpack.js.org/configuration/devtool/#production
+        devtool: '#source-map',
+
+        // Gzip off by default as many popular static hosts such as
+        // Surge or Netlify already gzip all static assets for you.
+        // Before setting to `true`, make sure to:
+        // npm install --save-dev compression-webpack-plugin
+        productionGzip: false,
+        productionGzipExtensions: ['js', 'css'],
+
+        // Run the build command with an extra argument to
+        // View the bundle analyzer report after build finishes:
+        // `npm run build --report`
+        // Set to `true` or `false` to always turn it on or off
+        bundleAnalyzerReport: process.env.npm_config_report
+    }
+}

+ 5 - 0
config/prod.env.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+    NODE_ENV: '"production"',
+    SSO_SERVER: '"http://sso.sagacloud.cn"'
+}

+ 11 - 0
index.html

@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
+    <title>sagacloud-admin</title>
+  </head>
+  <body>
+    <div id="app"></div>
+  </body>
+</html>

+ 76 - 0
package.json

@@ -0,0 +1,76 @@
+{
+  "name": "sagacloud-admin",
+  "version": "1.0.0",
+  "description": "A Vue.js project",
+  "author": "yaoll <yaolinlin@sagacloud.cn>",
+  "private": true,
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
+    "start": "npm run dev",
+    "build": "node build/build.js"
+  },
+  "dependencies": {
+    "axios": "^0.18.0",
+    "echarts": "^4.1.0",
+    "element-ui": "^2.6.1",
+    "font-awesome": "^4.7.0",
+    "handsontable-pro": "^6.2.3",
+    "jquery": "^3.3.1",
+    "vue": "^2.5.2",
+    "vue-axios": "^2.1.4",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.1.0"
+  },
+  "devDependencies": {
+    "ajv": "^6.9.1",
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-helper-vue-jsx-merge-props": "^2.0.3",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-syntax-jsx": "^6.18.0",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-plugin-transform-vue-jsx": "^3.5.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "chalk": "^2.0.1",
+    "copy-webpack-plugin": "^4.0.1",
+    "css-loader": "^0.28.0",
+    "extract-text-webpack-plugin": "^3.0.0",
+    "file-loader": "^1.1.4",
+    "friendly-errors-webpack-plugin": "^1.6.1",
+    "html-webpack-plugin": "^2.30.1",
+    "less": "^3.9.0",
+    "less-loader": "^4.1.0",
+    "node-notifier": "^5.1.2",
+    "node-sass": "^4.11.0",
+    "optimize-css-assets-webpack-plugin": "^3.2.0",
+    "ora": "^1.2.0",
+    "portfinder": "^1.0.13",
+    "postcss-import": "^11.0.0",
+    "postcss-loader": "^2.0.8",
+    "postcss-url": "^7.2.1",
+    "rimraf": "^2.6.0",
+    "sass-loader": "^7.1.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "swiper": "^4.0.0",
+    "uglifyjs-webpack-plugin": "^1.1.1",
+    "url-loader": "^0.5.8",
+    "vue-loader": "^13.3.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.5.2",
+    "webpack": "^3.6.0",
+    "webpack-bundle-analyzer": "^2.9.0",
+    "webpack-dev-server": "^2.9.1",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 6.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}

+ 11 - 0
src/App.vue

@@ -0,0 +1,11 @@
+<template>
+    <div id='app'>
+        <router-view/>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'App'
+}
+</script>

+ 205 - 0
src/api/alarm/alarm.js

@@ -0,0 +1,205 @@
+/**
+@author:fugy
+@date:2019.1.10
+@info:alarm-api
+ */
+import httputils from '@/utils/httputils'
+
+const api = {
+    //获取对象类型
+    getObjectType() {
+        return new Promise((resolve, reject) => {
+            httputils.getJson(
+                '/server/dataplatform/dict/query/equipment_all',
+                null,
+                res => {
+                    if (res.result === 'success') {
+                        let dataArr = res.data
+                        objTypeArr = []
+                        let sysArr = []
+                        let devArr = []
+                        dataArr.forEach(item => {
+                            objTypeArr.push(item)
+                            let sysObj = {
+                                label: item.name,
+                                value: item.code,
+                                type: item.type
+                            }
+                            let devObj = {
+                                label: item.name,
+                                value: item.code,
+                                type: item.type
+                            }
+                            if (item.content) {
+                                sysObj.children = []
+                                devObj.children = []
+                                item.content.forEach(ele => {
+                                    objTypeArr.push(ele)
+                                    let sysObj1 = {
+                                        label: ele.name,
+                                        value: ele.code,
+                                        type: ele.type
+                                    }
+                                    let devObj1 = {
+                                        label: ele.name,
+                                        value: ele.code,
+                                        type: ele.type
+                                    }
+                                    if (ele.content) {
+                                        devObj1.children = []
+                                        ele.content.forEach(strip => {
+                                            objTypeArr.push(strip)
+                                            let devObj2 = {
+                                                label: strip.name,
+                                                value: strip.code,
+                                                type: strip.type
+                                            }
+                                            devObj1.children.push(devObj2)
+                                        })
+                                    } else {
+                                        devObj1 = {
+                                            label: ele.name,
+                                            value: ele.code,
+                                            type: ele.type,
+                                            disabled: true
+                                        }
+                                    }
+                                    sysObj.children.push(sysObj1)
+                                    devObj.children.push(devObj1)
+                                })
+                            }
+                            sysArr.push(sysObj)
+                            devArr.push(devObj)
+                        })
+                        let resObj = {
+                            sysArr,
+                            devArr
+                        }
+                        objTypeDevArr = devArr
+                        resolve(resObj)
+                    } else {
+                        reject('fail')
+                    }
+                },
+                fail => {
+                    reject(fail)
+                },
+                err => {
+                    reject(err)
+                }
+            )
+        })
+    },
+    // 获取对象类的改造成一个整的数组
+    getObjTypeNameFromCode(objType, category, spaceNameObj) {
+        if (objType == 'building') {
+            return { name: '建筑' }
+        } else if (objType == 'floor') {
+            return { name: '楼层' }
+        } else if (objType == 'space') {
+            let name
+            if (category) {
+                name = spaceNameObj[category]
+            } else {
+                name = '空间'
+            }
+
+            return { name: name }
+        } else {
+            let arr = objTypeArr.filter((ele, index) => {
+                return ele.code == category
+            })
+            if (arr.length) {
+                return arr[0]
+            } else {
+                return { name: '---' }
+            }
+        }
+    },
+    // 获取对象类中的空间级联
+    getObjTypeSpace() {
+        return new Promise((resolve, reject) => {
+            httputils.getJson('/server/dataplatform/dict/query/space', {}, res => {
+                if (res.result == "success") {
+                    resolve(res.data)
+                } else {
+                    resolve([])
+                }
+            })
+        })
+    },
+    // 获取固定和项目报警的list数据
+    getListData(params) {
+        return new Promise((resolve, reject) => {
+            httputils.postJson(
+                '/server/alarm/config/queryConfig',
+                params,
+                res => {
+                    resolve(res)
+                },
+                fail => {
+                    reject(fail)
+                },
+                err => {
+                    reject(err)
+                }
+            )
+        })
+    },
+    // 通过根据Id批量获取各物理实体
+    getApplyObjInfo(projectId, data) {
+        return new Promise((resolve, reject) => {
+            httputils.postJson('/server/dataplatform/object/batchquery?projectId=' + projectId, data, res => {
+                if (res.Result === "success") {
+                    resolve(res.Content || []);
+                }
+            }, fail => {
+                reject(fail);
+            }, err => {
+                reject(err);
+            })
+        })
+    },
+  //查询报警分类
+  alarmClassList(params) {
+    return new Promise(resolve => {
+      httputils.postJson('/server/alarm/config/category/query', params, resp => {
+        if (resp.result == 'success' && resp.count) {
+          resolve(resp.content)
+        } else {
+          resolve([])
+        }
+      })
+    })
+  },
+  //创建报警分类
+  createAlarmClass(params) {
+    return new Promise(resolve => {
+      httputils.postJson('/server/alarm/config/category/create', params, resp => {
+        resolve(resp)
+      })
+    })
+  },
+  /***************断数报警*************************************************** */
+  //获取断数报警通知人员
+  getNumUserNotice(params) {
+    return new Promise(resolve => {
+        httputils.postJson('/server/alarm/notifyuser/queryBrokenAlarmNotifyUser', params, resp => {
+            if(resp.result == 'success' && resp.count) {
+                resolve(resp.content)
+            } else {
+                resolve([])
+            }
+        })
+    })
+  },
+  //更新断数报警的通知方式
+  updateNumUserNotice(params) {
+    return new Promise(resolve => {
+        httputils.postJson('/server/alarm/brokenalarmnotifyuser/update', params, resp => {
+            resolve(resp)
+        })
+    })
+  }
+}
+export default api

+ 10 - 0
src/api/database/hbase.js

@@ -0,0 +1,10 @@
+import httputils from '@/utils/httputils'
+export default {
+    tables() {
+        return httputils.getJson('/admin/hbase/tables')
+    },
+
+    query(criteria) {
+        return httputils.postJson('/admin/hbase/query', criteria)
+    }
+}

+ 12 - 0
src/api/database/redis.js

@@ -0,0 +1,12 @@
+import httputils from '@/utils/httputils'
+export default {
+    query(criteria) {
+        return httputils.postJson('/admin/redis/query', criteria)
+    },
+    save(redisData) {
+        return httputils.postJson('/admin/redis/save', redisData)
+    },
+    delete(redisData) {
+        return httputils.postJson('/admin/redis/delete', redisData)
+    }
+}

+ 72 - 0
src/api/fm/rules.js

@@ -0,0 +1,72 @@
+/**
+@author:fugy
+@date:2019.1.16
+@info:项目抢单规则设定=project-rule-api
+ */
+const planHz = [
+    { id: 'd', name: '每日' },
+    { id: 'w', name: '每周' },
+    { id: 'm', name: '每月' },
+    { id: 'y', name: '每年' }
+]
+import httputils from '@/utils/httputils'
+const api = {
+    getLevelDef() {
+        return { value: 'id', label: 'name', children: 'content' }
+    },
+    //获取级别列表
+    getLevelList() {
+        return new Promise((resolve, reject) => {
+            httputils.getJson('/server/workorder/engineer/level/def/list', null, resp => {
+                let result = []
+                resp.content.forEach(item => {
+                    let temp = { id: item.skillId, name: item.name, content: [] }
+                    if (item.content) {
+                        item.content.forEach(level => {
+                            temp.content.push({ id: level.levelId, name: level.name })
+                        })
+                    }
+                    result.push(temp)
+                })
+                resolve(result)
+            })
+        })
+    },
+    // 获取工单类型列表
+    getOrderType() {
+        return new Promise(resolve => {
+            httputils.getJson('/server/workorder/rule/queryWoTypeList', {}, resp => {
+                if (resp.result == 'success' && resp.count) {
+                    resolve(resp.content)
+                } else {
+                    resolve([])
+                }
+            })
+        })
+    },
+    //获取计划频率
+    getPlanHz() {
+        return planHz
+    },
+    //获取项目抢单规则的列表数据
+    getListData(params) {
+        return new Promise(resolve => {
+            httputils.postJson('/server/workorder/rule/query', params, resp => {
+                if (resp.result == 'success' && resp.content.length) {
+                    resolve(resp.content)
+                } else {
+                    resolve([])
+                }
+            })
+        })
+    },
+    //配置当前的工单的规则
+    updateWorkOrder(params) {
+        return new Promise(resolve => {
+            httputils.postJson('/server/workorder/rule/createOrUpdate', params, resp => {
+                resolve(resp)
+            })
+        })
+    }
+}
+export default api

+ 83 - 0
src/api/fm/sop.js

@@ -0,0 +1,83 @@
+/**
+@author:fugy
+@date:2018.10.22
+@info:通用问题对应集团的sop的api
+ */
+import httputils from '@/utils/httputils';
+let priorityArr = [
+    //优先级数组
+    {
+        label: 'S',
+        value: 'S'
+    },
+    {
+        label: 'A',
+        value: 'A'
+    },
+    {
+        label: 'B',
+        value: 'B'
+    },
+    {
+        label: 'C',
+        value: 'C'
+    },
+    {
+        label: 'D',
+        value: 'D'
+    }
+];
+let problemArr = [
+    //问题级别数组
+    {
+        label: '电器',
+        value: '电器'
+    },
+    {
+        label: '建筑',
+        value: '建筑'
+    },
+    {
+        label: '家具',
+        value: '家具'
+    },
+    {
+        label: '其他',
+        value: '其他'
+    }
+]
+const api = {
+    // 获取sop的列表
+    getListData: function(params) {
+        return new Promise((resolve, reject) => {
+			httputils.postJson('/server/workorder/generalProblem/problem/query', params, res => {
+                resolve(res)
+			})
+		})
+    },
+    // 获取sop问题的列表数据
+    getSopList(params) {
+        return new Promise((resolve, reject) => {
+			httputils.postJson('/server/workorder/generalProblem/sop/list', params, res => {
+                resolve(res)
+			})
+		})
+    },
+    // 保存配置sop
+    setSop(params) {
+        return new Promise((resolve, reject) => {
+			httputils.postJson('/server/workorder/generalProblem/sop/set', params, res => {
+                resolve(res)
+			})
+		})
+    },
+    // 优先级数组
+    getPriorityArr: function () {
+        return priorityArr
+    },
+    // 获取问题类型
+    getProblemArr: function () {
+        return problemArr
+    },
+}
+export default api;

+ 5 - 0
src/api/scan/config.js

@@ -0,0 +1,5 @@
+export const api = '/ScanBuilding';
+export const sass = 'sass';
+export const physics = '/data-platform-3';
+export const business = '/business-space'
+export const venders = '/venders'

+ 30 - 0
src/api/scan/fetch.js

@@ -0,0 +1,30 @@
+import Vue from 'vue'
+import axios from 'axios'
+import vueAxios from 'vue-axios'
+
+Vue.use(vueAxios, axios)
+    //创建实例
+
+const service = axios.create({
+    timeout: 3000000, //请求时间超出
+    withCredentials: true, //是否跨站点访问控制请求
+})
+
+//request拦截
+service.interceptors.request.use(config => {
+    return config
+}, error => {
+    console.log(error)
+    Promise.reject(error)
+})
+
+//response拦截器
+service.interceptors.response.use(
+    response => response,
+    error => {
+        console.log('err' + error)
+        return Promise.reject(error)
+    }
+)
+
+export default service

+ 50 - 0
src/api/scan/httpUtil.js

@@ -0,0 +1,50 @@
+import { Message } from 'element-ui';
+import fetch from './fetch'
+
+function successResponse(vm, response, success, failed) {
+    let resp = response.data;
+    let result = resp.result ? resp.result : resp.Result;
+
+    if (result === 'success') {
+        if (success) {
+            success(resp);
+        }
+    } else {
+        let msg = resp.message ? resp.message : resp.ResultMsg || resp.resultMsg;
+        Message.error({ message: msg });
+        if (failed) {
+            failed(resp);
+        }
+    }
+
+}
+
+function errorResponse(vm, response, err) {
+    let json = JSON.stringify(response)
+    json = JSON.parse(json)
+    console.error(response)
+    if (err) {
+        Message.error({ message: '接口:' + json.config.url + '请求错误' })
+    } else {
+        Message.error({ message: '请求错误' });
+    }
+}
+
+export default {
+    getJson: function(url, data, success, failed, err) {
+        let vm = this;
+        fetch({ url: url, method: 'get', params: data }).then((response) => {
+            successResponse(vm, response, success, failed)
+        }).catch(error => {
+            errorResponse(vm, error, err);
+        });
+    },
+    postJson: function(url, data, success, failed, err) {
+        let vm = this;
+        fetch({ url: url, method: 'post', data: data }).then((response) => {
+            successResponse(vm, response, success, failed)
+        }).catch(error => {
+            errorResponse(vm, error, err);
+        });
+    }
+}

+ 764 - 0
src/api/scan/request.js

@@ -0,0 +1,764 @@
+import fetch from './fetch'
+import { api, physics, business, venders } from './config'
+import http from './httpUtil'
+let arithmetic = '/arithmetic'
+
+var Comming = 'revit'
+var ProjId = 'Pj1101080001'
+var UserId = '25518428919955458'
+    //获取打印标签模板test
+export function getTest() {
+    return fetch({ method: 'GET', url: `${api}/service/template/note_template` })
+}
+
+//查看扫楼日志接口
+
+export function getBuildLog(
+    param
+) {
+    let data = {
+        "BeginTime": param.startTime,
+        "Comming": Comming,
+        "EndTime": param.endTime,
+        "Filter": param.filter,
+        "PageNum": param.pageNum,
+        "PageSize": param.pageSize,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/user_log/query`, data })
+}
+
+//下载扫楼日志接口
+
+export function dowmloadLog(
+    param
+) {
+    let data = {
+        "BeginTime": param.startTime,
+        "Comming": Comming,
+        "EndTime": param.endTime,
+        "Filter": param.filter,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/user_log/export`, data })
+}
+
+//获取扫楼用户表格
+export function getUser(
+    param
+) {
+    let data = {
+        "Comming": Comming,
+        "Filter": param.filter,
+        "PageNum": param.pageNum,
+        "PageSize": param.pageSize,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/user/query`, data })
+}
+
+//修改扫楼用户
+export function loadUser(
+    param
+) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "UserList": param.UserList
+    }
+    return fetch({ method: 'POST', url: `${api}/service/user/update`, data })
+}
+
+//删除扫楼用户
+export function delUser(
+    param
+) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "UserList": param.UserList
+    }
+    return fetch({ method: 'POST', url: `${api}/service/user/delete`, data })
+}
+
+//添加扫楼用户
+export function addUser(
+    param
+) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "User": param.User
+    }
+    return fetch({ method: 'POST', url: `${api}/service/user/create`, data })
+}
+
+//根据项目ID获得建筑列表
+export function getBuildSelect(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+    }
+    return fetch({ method: 'POST', url: `${api}/service/building/query`, data })
+}
+
+//根据建筑ID获取楼层系信息
+export function getFloor(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "BuildId": param.BuildId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/building/floor`, data })
+}
+
+//获取异常资产接口
+export function getErrAssets(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "BuildId": param.BuildId,
+        "PageNum": param.PageNum,
+        "PageSize": param.PageSize,
+        "Type": param.Type,
+        "CodeType": param.CodeType
+    }
+    if (param.BuildId == 'all') {
+        delete data.BuildId
+    }
+
+    if (!param.CodeType) {
+        delete data.CodeType
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/exception`, data })
+}
+
+//删除资产接口
+export function delErrAssets(param, paramList) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "FmList": paramList
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/delete`, data })
+}
+
+//根据选择的建筑楼层时间查询点位标签
+export function getPT(param) {
+    let data = {
+        "Comming": Comming,
+        "BeginTime": param.BeginTime,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "BeginTime": param.BeginTime,
+        "EndTime": param.EndTime,
+        "BuildId": param.BuildId,
+        "FloorId": param.FloorId
+    }
+    if (data.FloorId == 'all') {
+        delete data.FloorId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/point_group`, data })
+}
+
+//点位标签查看详情
+export function getPoint(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "PointId": param.PointId,
+    }
+    return fetch({ method: 'POST', url: `${api}/service/point/query`, data })
+}
+
+//点位标签详情修改
+export function updatePoint(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "PointList": [param.PointList],
+    }
+    return fetch({ method: 'POST', url: `${api}/service/point/update`, data })
+}
+
+//点击点位标签获取tab设备族列表 
+export function getTabFamily(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId,
+        "BeginTime": param.BeginTime,
+        "EndTime": param.EndTime,
+        "FloorId": param.FloorId,
+        "Checked": param.Checked,
+        "BuildId": param.BuildId,
+        "PointId": param.PointId
+    }
+    if (data.PointId == 'all') {
+        delete data.PointId
+    }
+    if (data.FloorId == 'all') {
+        delete data.FloorId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/family_list`, data })
+}
+
+//获取表格数据的title
+export function getTableHeader(param) {
+    let data = {
+        "AppType": Comming,
+        "Code": param.code,
+        "ProjId": param.ProjId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/dict/infocode_query`, data })
+}
+
+//获取表格主体内容
+export function getTableMain(param) {
+    let data = {
+        "BeginTime": param.BeginTime,
+        "BuildId": param.BuildId,
+        "Checked": param.Checked,
+        "Comming": Comming,
+        "EndTime": param.EndTime,
+        "Family": param.Family,
+        "FloorId": param.FloorId,
+        "PageNum": param.PageNum,
+        "PageSize": param.PageSize,
+        "PointId": param.PointId,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    if (data.PointId == 'all') {
+        delete data.PointId
+    }
+    if (data.Checked === '') {
+        delete data.Checked
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/query`, data })
+}
+
+export function getPointList(param, success) {
+    let url = `${api}/service/point/queryByIdList`
+    let data = param
+    data.Comming = Comming
+    http.postJson(url, data, success)
+}
+
+export function getdelMain(param) {
+    let data = {
+        "BeginTime": param.BeginTime,
+        "BuildId": param.BuildId,
+        "Checked": param.Checked,
+        "CodeType": param.CodeType,
+        "Comming": Comming,
+        "EndTime": param.EndTime,
+        "Family": param.Family,
+        "FloorId": param.FloorId,
+        "PageNum": param.PageNum,
+        "PageSize": param.PageSize,
+        "PointId": param.PointId,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    if (data.PointId == 'all') {
+        delete data.PointId
+    }
+    if (data.Checked === '') {
+        delete data.Checked
+    }
+    if (data.FloorId == 'all') {
+        delete data.FloorId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/queryRevit`, data })
+}
+
+export function getTestEquire() {
+    let data = {
+        "Comming": "revit",
+        "FloorId": "Fl110108000327d8f006c39f49feb40e24a7ef223232",
+        "ProjId": "Pj1101080003",
+        "UserId": "string"
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/query`, data })
+}
+
+//批量更新表格数据
+export function upDateTableMain(param, paramList) {
+    let data = {
+        "Comming": Comming,
+        "FmList": paramList,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/update`, data })
+}
+
+//获取所有设备族/service/dict/equipment_family
+export function getAllFamily() {
+    return fetch({ method: 'GET', url: `${api}/service/dict/equipment_family` })
+}
+
+//获取当前默认楼层/service/fm/default_floor
+export function defaultFloor(param) {
+    let data = {
+        "Comming": Comming,
+        "ProjId": param.ProjId,
+        "UserId": param.UserId
+    }
+    return fetch({ method: 'POST', url: `${api}/service/fm/default_floor`, data })
+}
+
+//物理世界新建岗位(设备系统)
+export function createPost(param) {
+    let data = {
+        "building_id": param.BuildId,
+        "floor_id": param.BIMID,
+        "equipment_category": param.category,
+        "infos": {
+            "BIMID": [
+                { "value": param.BIMID }
+            ],
+            "BIMLocation": [
+                { "value": param.BIMLocation }
+            ]
+        }
+    }
+    return fetch({ method: 'POST', url: `${physics}/godhand/equipment/create?projectId=${param.perjectId}&secret=${param.secret}`, data })
+}
+
+
+//物理世界新建岗位(部件)
+export function createComponent(param) {
+    let data = {
+        "building_id": param.BuildId,
+        "component_category": param.category,
+        "floor_id": param.BIMID,
+        "infos": {
+            "BIMID": [
+                { "value": param.BIMID }
+            ],
+            "BIMLocation": [
+                { "value": param.BIMLocation }
+            ]
+        }
+    }
+    return fetch({ method: 'POST', url: `${physics}/component/create?projectId=${param.perjectId}&secret=${param.secret}`, data })
+}
+
+//通过岗位id查询元空间id
+export function getSpaceId(param) {
+    let data = {
+        "criterias": param.list
+    }
+    return fetch({ method: 'POST', url: `${physics}/relation/query?projectId=${param.perjectId}&secret=${param.secret}`, data })
+}
+
+//通过元空间id查询元空间名称
+export function getSpaceName(param) {
+    let data = {
+        "criterias": param.list
+    }
+    return fetch({ method: 'POST', url: `${physics}/object/batch_query?projectId=${param.perjectId}&secret=${param.secret}`, data })
+}
+
+
+//设备族修改显示接口
+export function setHeader(param) {
+    let data = {
+        "Comming": Comming,
+        "InfoType": param.FamilyId,
+        "InfopointCode": param.code,
+        "ProjId": param.perjectId,
+        "UserId": param.UserId,
+        "Visible": param.Visible
+    }
+    return fetch({ method: 'POST', url: `${api}/service/dict/infopoint_visible`, data })
+}
+
+//获取头部接口
+export function getHeader(param) {
+    let data = {
+            "AppType": Comming,
+            "Code": param.code,
+            "ProjId": param.perjectId
+        }
+        // return fetch({ method: 'POST', url: `${api}/service/dict/equipment_family_info_header`, data })
+    return fetch({ method: 'POST', url: `${api}/service/dict/infocode_query_header`, data })
+}
+
+
+/** 业务空间相关 */
+
+// 业务空间
+export function getSpaceHeader(param) {
+    return fetch({ method: 'GET', url: `${physics}/infocode/query_property?projectId=${param.ProjId}&type=${param.code ? param.code : 'space'}&enrich=true` })
+}
+
+
+//获取某个业务空间下的
+export function getAllbusiness(param) {
+    let data = null
+    if (!!param.id) {
+        data = {
+            "criteria": {
+                "id": param.id,
+                "type": param.type
+            }
+        }
+    } else {
+        data = param.data
+    }
+    return fetch({ method: "POST", url: `${physics}/object/subset_query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//通过BIMID获取物理世界id
+export function formBIMToPri(param) {
+    let data = {
+        "criteria": {
+            "type": param.type,
+            "BIMID": param.ids
+        }
+    }
+    return fetch({ method: "POST", url: `${physics}/fast/object/in_query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//获取业务空间楼层的关系
+export function getSpaceFloor(param) {
+    return fetch({ method: "POST", url: `${business}/floor/tree?projectId=${param.ProjId}&secret=${param.secret}` })
+}
+
+//创建业务空间
+export function createBusiness(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${business}/space/create?projectId=${param.ProjId}&secret=${param.secret}`, data })
+        // return fetch({ method: "POST", url: `/test/space/create?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//批量创建业务空间
+export function createBusinessList(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${business}/space/batch/create?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//更新业务空间
+export function updateBusiness(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${business}/object/batch_update?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//模糊搜索设备类型
+export function searchCode(param) {
+    let data = param
+    return fetch({ method: "POST", url: `${physics}/dict/query/equipment_all`, data })
+}
+
+//获取所有设备类型
+export function getEqCode() {
+    return fetch({ method: "GET", url: `${physics}/dict/query/equipment_all` })
+}
+
+//批量删除业务空间
+export function delManySpace(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${physics}/space/batch/delete?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//添加关系
+export function createRelation(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${physics}/relation/create?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//获取图实例关系
+export function getGraphyId(param) {
+    let data = {
+        "graph_type": param.type
+    }
+    return fetch({ method: "POST", url: `${physics}/relation/graph_instance/get?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//查询业务空间
+export function getBussines(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${physics}/object/outside_query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+        // return fetch({ method: "POST", url: `/test/object/outside_query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//替代前一个接口
+export function getBussines2(param) {
+    let data = param.data
+    return fetch({ method: "POST", url: `${physics}/fast/object/in_query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//查询业务空间下的元空间
+export function getRelation(param) {
+    let data = param.criterias
+    return fetch({ method: 'POST', url: `${physics}/relation/query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//查询业务空间下的设备
+export function getSpaceEquip(param) {
+    let data = param.data
+    return fetch({ method: 'POST', url: `${business}/space/equip/query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//删除关系实例
+export function deleteRelation(param) {
+    let data = param.data
+    return fetch({ method: 'POST', url: `${physics}/relation/delete?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//查询是否关联元空间的业务空间
+export function isSpaceBuss(param) {
+    let data = param.data
+    return fetch({ method: 'POST', url: `${business}/floor/space/query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//查询所有提示
+export function getAllMess(param) {
+    let data = {}
+    return fetch({ method: 'POST', url: `${physics}/space/affected/query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//删除提示
+export function deleteNotice(param) {
+    let data = param.data
+    return fetch({ method: 'POST', url: `${physics}/space/affected/delete_notice?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+//模糊查询楼层下的设备
+export function getEquipSearch(param) {
+    let data = param.data
+    return fetch({ method: 'POST', url: `${business}/floor/equip/query?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+export function getBussTypes(success) {
+    http.getJson(`${physics}/infocode/business_space_type/query`, {}, success)
+}
+
+export function getBussTypes2() {
+    return fetch({ method: 'GET', url: `${physics}/infocode/business_space_type/query` })
+}
+
+
+
+
+//验证元空间是否相邻
+export function isAbut(param) {
+    let data = param.data
+    return fetch({ method: 'POST', url: `${business}/space/abut?projectId=${param.ProjId}&secret=${param.secret}`, data })
+}
+
+/**算法接口 */
+export function strDiff(param) {
+    let data = param
+    return fetch({ method: 'POST', url: `${arithmetic}/str_diff`, data })
+}
+
+
+/********* 台账部分  厂商库接口 */
+//获取供应商列表
+export function getManuLib(param, success) {
+    let data = param
+    http.postJson(`${venders}/supplier/query`, data, success)
+}
+
+//查询所有供应商信息
+export function getLib(param) {
+    let data = param
+    return fetch({ method: 'POST', url: `${venders}/auxiliary/query/all`, data })
+}
+
+//查询资产的list
+export function getPropertyList(param, success) {
+    let data = param.data
+    let url = `${physics}/property/relation_query?projectId=${param.ProjId}&secret=${param.secret}`
+    http.postJson(url, data, success)
+}
+
+//获取资产种族编码query/equipment_family
+export function getEquipmentFamily(success) {
+    let url = `${physics}/dict/query/equipment_family`
+    http.getJson(url, {}, success)
+}
+
+//删除资产
+export function delProperty(param, success) {
+    let url = `${physics}/property/delete?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//获取制定供应商下的供应合同编号
+export function getListForSupplier(param, success) {
+    let url = `${physics}/venders-dp/supplier/contract/query?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//获取保单列表
+export function getListForGuarantee(param, success) {
+    let url = `${physics}/venders-dp/insurance/contract/query`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//新建保单
+export function createGuarantee(param, success) {
+    let url = `${venders}/insurance/contract/create `
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//创建资产
+export function createProperty(param, success) {
+    let url = `${physics}/property/create?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//获取项目内[岗位]和[绑定岗位的资产]的数量
+export function getNumber(param, success) {
+    let url = `${business}/equip/count?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//获取项目内有实例的所有种族
+export function getFamilyList(param, success) {
+    let url = `${business}/equip/type/list?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//删除系统、设备
+export function delSystem(param, success) {
+    let url = `${physics}/equipment/delete?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+
+//创建系统设备
+export function createSystem(param, success) {
+    let url = `${physics}/equipment/create?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+
+//获取台账资产列表
+export function getBillPropList(param, success) {
+    let url = `${business}/property/list?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//台账修改资产信息
+export function updateProperty(param, success) {
+    let url = `${physics}/property/update?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//台账获取设备列表
+export function getBillEquipList(param, success) {
+    let url = `${business}/equip/list?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//台账获取系统列表
+export function getBillSystemList(param, success) {
+    let url = `${business}/system/list?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//台账获取系统关联的设备
+export function getSysToEq(param, success) {
+    let url = `${physics}/object/rel_query?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//台账查询型号
+export function getSpecList(param, success) {
+    let url = `${venders}/manufacturer/specification/query_spec`
+    let data = param
+    http.postJson(url, data, success)
+}
+
+//台账根据设备id创建岗位
+export function EqToAssets(param, success) {
+    let url = `${physics}/property/create_by_equip?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//获取设定参数和动态参数
+export function BeatchQueryParam(param, success) {
+    let url = `${physics}/parameter/batch_query_param?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+export function BatchCreateSome(param, success) {
+    let url = `${physics}/equipment/batch/create?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+export function delBatchList(param, success) {
+    let url = `${physics}/equipment/batch/delete?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+
+//扫楼创建资产
+export function createFmList(param, success) {
+    let url = `${api}/service/fm/create`
+    param.Comming = Comming
+    let data = param
+    http.postJson(url, data, success)
+}
+
+export function createAssetsList(param, success) {
+    let url = `${api}/service/fm/create_list`
+    param.Comming = Comming
+    let data = param
+    http.postJson(url, data, success)
+}
+
+export function getListForBuss(param, success) {
+    let url = `${business}/space/ispace/equip/query?projectId=${param.ProjId}&secret=${param.secret}`
+    let data = param.data
+    http.postJson(url, data, success)
+}
+
+//获取产品list
+export function getProductList(param, success) {
+    let url = `${venders}/manufacturer/product/queryByType`
+    http.postJson(url, param, success)
+}
+
+//完全匹配的厂商信息
+export function getBasicMatch(param) {
+    console.log(param, "param")
+    let data = param
+    return fetch({ method: 'POST', url: `${venders}/basic/autoFillId`, data })
+}

+ 6 - 0
src/api/system/admin.js

@@ -0,0 +1,6 @@
+import httputils from '@/utils/httputils'
+export default {
+    loadUserInfo() {
+        return httputils.getJson('/admin/userInfo')
+    }
+}

+ 21 - 0
src/api/system/role.js

@@ -0,0 +1,21 @@
+import httputils from '@/utils/httputils'
+export default {
+    query(criteria) {
+        return httputils.postJson('/admin/role/query', criteria)
+    },
+    create(role) {
+        return httputils.postJson('/admin/role/create', role)
+    },
+    update(role) {
+        return httputils.postJson('/admin/role/update', role)
+    },
+    delete(role) {
+        return httputils.postJson('/admin/role/delete', role)
+    },
+    getOperations(id) {
+        return httputils.getJson('/admin/role/getOperations?id=' + id)
+    },
+    setOperations(data) {
+        return httputils.postJson('/admin/role/setOperations', data)
+    }
+}

+ 15 - 0
src/api/system/user.js

@@ -0,0 +1,15 @@
+import httputils from '@/utils/httputils'
+export default {
+  query(criteria) {
+    return httputils.postJson('/admin/user/query', criteria)
+  },
+  create(user) {
+    return httputils.postJson('/admin/user/create', user)
+  },
+  update(user) {
+    return httputils.postJson('/admin/user/update', user)
+  },
+  delete(user) {
+    return httputils.postJson('/admin/user/delete', user)
+  }
+}

+ 477 - 0
src/assets/css/jsmind.css

@@ -0,0 +1,477 @@
+/*
+ * Released under BSD License
+ * Copyright (c) 2014-2015 hizzgdev@163.com
+ * 
+ * Project Home:
+ *   https://github.com/hizzgdev/jsmind/
+ */
+
+
+/* important section */
+
+.jsmind-inner {
+    position: relative;
+    overflow: auto;
+    width: 100%;
+    height: 100%;
+}
+
+
+/*box-shadow:0 0 2px #000;*/
+
+.jsmind-inner {
+    moz-user-select: -moz-none;
+    -moz-user-select: none;
+    -o-user-select: none;
+    -khtml-user-select: none;
+    -webkit-user-select: none;
+    -ms-user-select: none;
+    user-select: none;
+}
+
+
+/* z-index:1 */
+
+canvas {
+    position: absolute;
+    z-index: 1;
+}
+
+
+/* z-index:2 */
+
+jmnodes {
+    position: absolute;
+    z-index: 2;
+    background-color: rgba(0, 0, 0, 0);
+}
+
+
+/*background color is necessary*/
+
+jmnode {
+    position: absolute;
+    cursor: default;
+    max-width: 400px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+jmexpander {
+    position: absolute;
+    width: 11px;
+    height: 11px;
+    display: block;
+    overflow: hidden;
+    line-height: 12px;
+    font-size: 12px;
+    text-align: center;
+    border-radius: 6px;
+    border-width: 1px;
+    border-style: solid;
+    cursor: pointer;
+}
+
+
+/* default theme */
+
+jmnode {
+    padding: 10px;
+    background-color: #fff;
+    color: #333;
+    border-radius: 5px;
+    box-shadow: 1px 1px 1px #666;
+    font: 16px/1.125 Verdana, Arial, Helvetica, sans-serif;
+}
+
+jmnode:hover {
+    box-shadow: 2px 2px 8px #000;
+    background-color: #ebebeb;
+    color: #333;
+}
+
+jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+    box-shadow: 2px 2px 8px #000;
+}
+
+jmnode.root {
+    font-size: 24px;
+}
+
+jmexpander {
+    border-color: gray;
+}
+
+jmexpander:hover {
+    border-color: #000;
+}
+
+@media screen and (max-device-width: 1024px) {
+    jmnode {
+        padding: 5px;
+        border-radius: 3px;
+        font-size: 14px;
+    }
+    jmnode.root {
+        font-size: 21px;
+    }
+}
+
+
+/* primary theme */
+
+jmnodes.theme-primary jmnode {
+    background-color: #428bca;
+    color: #fff;
+    border-color: #357ebd;
+}
+
+jmnodes.theme-primary jmnode:hover {
+    background-color: #3276b1;
+    border-color: #285e8e;
+}
+
+jmnodes.theme-primary jmnode.selected {
+    background-color: #f1c40f;
+    color: #fff;
+}
+
+jmnodes.theme-primary jmnode.root {}
+
+jmnodes.theme-primary jmexpander {}
+
+jmnodes.theme-primary jmexpander:hover {}
+
+
+/* warning theme */
+
+jmnodes.theme-warning jmnode {
+    background-color: #f0ad4e;
+    border-color: #eea236;
+    color: #fff;
+}
+
+jmnodes.theme-warning jmnode:hover {
+    background-color: #ed9c28;
+    border-color: #d58512;
+}
+
+jmnodes.theme-warning jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-warning jmnode.root {}
+
+jmnodes.theme-warning jmexpander {}
+
+jmnodes.theme-warning jmexpander:hover {}
+
+
+/* danger theme */
+
+jmnodes.theme-danger jmnode {
+    background-color: #d9534f;
+    border-color: #d43f3a;
+    color: #fff;
+}
+
+jmnodes.theme-danger jmnode:hover {
+    background-color: #d2322d;
+    border-color: #ac2925;
+}
+
+jmnodes.theme-danger jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-danger jmnode.root {}
+
+jmnodes.theme-danger jmexpander {}
+
+jmnodes.theme-danger jmexpander:hover {}
+
+
+/* success theme */
+
+jmnodes.theme-success jmnode {
+    background-color: #5cb85c;
+    border-color: #4cae4c;
+    color: #fff;
+}
+
+jmnodes.theme-success jmnode:hover {
+    background-color: #47a447;
+    border-color: #398439;
+}
+
+jmnodes.theme-success jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-success jmnode.root {}
+
+jmnodes.theme-success jmexpander {}
+
+jmnodes.theme-success jmexpander:hover {}
+
+
+/* info theme */
+
+jmnodes.theme-info jmnode {
+    background-color: #5dc0de;
+    border-color: #46b8da;
+    ;
+    color: #fff;
+}
+
+jmnodes.theme-info jmnode:hover {
+    background-color: #39b3d7;
+    border-color: #269abc;
+}
+
+jmnodes.theme-info jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-info jmnode.root {}
+
+jmnodes.theme-info jmexpander {}
+
+jmnodes.theme-info jmexpander:hover {}
+
+
+/* greensea theme */
+
+jmnodes.theme-greensea jmnode {
+    background-color: #1abc9c;
+    color: #fff;
+}
+
+jmnodes.theme-greensea jmnode:hover {
+    background-color: #16a085;
+}
+
+jmnodes.theme-greensea jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-greensea jmnode.root {}
+
+jmnodes.theme-greensea jmexpander {}
+
+jmnodes.theme-greensea jmexpander:hover {}
+
+
+/* nephrite theme */
+
+jmnodes.theme-nephrite jmnode {
+    background-color: #2ecc71;
+    color: #fff;
+}
+
+jmnodes.theme-nephrite jmnode:hover {
+    background-color: #27ae60;
+}
+
+jmnodes.theme-nephrite jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-nephrite jmnode.root {}
+
+jmnodes.theme-nephrite jmexpander {}
+
+jmnodes.theme-nephrite jmexpander:hover {}
+
+
+/* belizehole theme */
+
+jmnodes.theme-belizehole jmnode {
+    background-color: #3498db;
+    color: #fff;
+}
+
+jmnodes.theme-belizehole jmnode:hover {
+    background-color: #2980b9;
+}
+
+jmnodes.theme-belizehole jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-belizehole jmnode.root {}
+
+jmnodes.theme-belizehole jmexpander {}
+
+jmnodes.theme-belizehole jmexpander:hover {}
+
+
+/* wisteria theme */
+
+jmnodes.theme-wisteria jmnode {
+    background-color: #9b59b6;
+    color: #fff;
+}
+
+jmnodes.theme-wisteria jmnode:hover {
+    background-color: #8e44ad;
+}
+
+jmnodes.theme-wisteria jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-wisteria jmnode.root {}
+
+jmnodes.theme-wisteria jmexpander {}
+
+jmnodes.theme-wisteria jmexpander:hover {}
+
+
+/* asphalt theme */
+
+jmnodes.theme-asphalt jmnode {
+    background-color: #34495e;
+    color: #fff;
+}
+
+jmnodes.theme-asphalt jmnode:hover {
+    background-color: #2c3e50;
+}
+
+jmnodes.theme-asphalt jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-asphalt jmnode.root {}
+
+jmnodes.theme-asphalt jmexpander {}
+
+jmnodes.theme-asphalt jmexpander:hover {}
+
+
+/* orange theme */
+
+jmnodes.theme-orange jmnode {
+    background-color: #f1c40f;
+    color: #fff;
+}
+
+jmnodes.theme-orange jmnode:hover {
+    background-color: #f39c12;
+}
+
+jmnodes.theme-orange jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-orange jmnode.root {}
+
+jmnodes.theme-orange jmexpander {}
+
+jmnodes.theme-orange jmexpander:hover {}
+
+
+/* pumpkin theme */
+
+jmnodes.theme-pumpkin jmnode {
+    background-color: #e67e22;
+    color: #fff;
+}
+
+jmnodes.theme-pumpkin jmnode:hover {
+    background-color: #d35400;
+}
+
+jmnodes.theme-pumpkin jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-pumpkin jmnode.root {}
+
+jmnodes.theme-pumpkin jmexpander {}
+
+jmnodes.theme-pumpkin jmexpander:hover {}
+
+
+/* pomegranate theme */
+
+jmnodes.theme-pomegranate jmnode {
+    background-color: #e74c3c;
+    color: #fff;
+}
+
+jmnodes.theme-pomegranate jmnode:hover {
+    background-color: #c0392b;
+}
+
+jmnodes.theme-pomegranate jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-pomegranate jmnode.root {}
+
+jmnodes.theme-pomegranate jmexpander {}
+
+jmnodes.theme-pomegranate jmexpander:hover {}
+
+
+/* clouds theme */
+
+jmnodes.theme-clouds jmnode {
+    background-color: #ecf0f1;
+    color: #333;
+}
+
+jmnodes.theme-clouds jmnode:hover {
+    background-color: #bdc3c7;
+}
+
+jmnodes.theme-clouds jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-clouds jmnode.root {}
+
+jmnodes.theme-clouds jmexpander {}
+
+jmnodes.theme-clouds jmexpander:hover {}
+
+
+/* asbestos theme */
+
+jmnodes.theme-asbestos jmnode {
+    background-color: #95a5a6;
+    color: #fff;
+}
+
+jmnodes.theme-asbestos jmnode:hover {
+    background-color: #7f8c8d;
+}
+
+jmnodes.theme-asbestos jmnode.selected {
+    background-color: #11f;
+    color: #fff;
+}
+
+jmnodes.theme-asbestos jmnode.root {}
+
+jmnodes.theme-asbestos jmexpander {}
+
+jmnodes.theme-asbestos jmexpander:hover {}

+ 44 - 0
src/assets/graphy/SGraphy/SCanvas.js

@@ -0,0 +1,44 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * Canvas功能扩展
+ *
+ * @author  Andy
+ */
+Object.assign(CanvasRenderingContext2D.prototype, {
+  /**
+   * 绘制线段
+   *
+   * @param   x0    起点x坐标
+   * @param   y0    起点y坐标
+   * @param   x1    终点x坐标
+   * @param   y1    终点y坐标
+   */
+  drawLine (x0, y0, x1, y1) {
+    this.beginPath()
+    this.moveTo(x0, y0)
+    this.lineTo(x1, y1)
+    this.stroke()
+  } // Function drawLine()
+}) // Class CanvasRenderingContext2D

+ 30 - 0
src/assets/graphy/SGraphy/SGraphyEnums.js

@@ -0,0 +1,30 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+const SGraphyItemFlag = {
+  ItemIsMovable: 1,
+  ItemIsSelectable: 2
+}
+
+if (Object.freeze)
+  Object.freeze(SGraphyItemFlag)

+ 396 - 0
src/assets/graphy/SGraphy/SGraphyItem.js

@@ -0,0 +1,396 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+import SPoint from './types/SPoint'
+import SRect from './types/SRect'
+import SMouseEvent from './SMouseEvent'
+
+const sortItemZOrder = Symbol('sortItemZOrder')
+const toChildMouseEvent = Symbol('toChildMouseEvent')
+const grabItem = Symbol('grabItem')
+const releaseItem = Symbol('releaseItem')
+
+/**
+ * SGraphyItem
+ *
+ * @author  Andy
+ */
+export default class SGraphyItem {
+    /**
+     * 构造函数
+     */
+    constructor(parent) {
+            this.name = 'item'
+            this._scene = null
+            this._parent = parent
+            this.children = []
+
+            /** Z轴顺序 */
+            this.zOrder = 0
+                /** item位置 */
+            this._pos = new SPoint(0, 0)
+                /** 缩放比例 */
+            this._scale = new SPoint(1, 1)
+                /** 是否可见 */
+            this._isVisible = true
+
+            /** 鼠标按下时位置 */
+            this._mouseDownPos = new SPoint(4, 21)
+            this._isMove = false
+            this.canMove = false
+        } // Function constructor()
+
+    // ===================================================================================================================
+    // 属性
+    /** parent属性存值函数 */
+    get parent() {
+        return this._parent
+    }
+    set parent(value) {
+            if (this._parent === value) {
+                return
+            }
+            if (this._parent != null) { // 如果原parent不为空
+                // 将节点从原parent节点中摘除
+                let i = this._parent.children.indexOf(this)
+                this._parent.children.splice(i, 1)
+            }
+            this._parent = value
+
+            if (this._parent != null) { // 如果新parent不为空
+                // 将节点加入到新parent节点中
+                this._parent.children.push(this)
+                this._parent.children.sort(this[sortItemZOrder])
+            }
+            this._parent = value
+        } // Function set Parent()
+
+    /** scene属性 */
+    get scene() {
+        if (this._parent != null) {
+            return this._parent.scene
+        } else {
+            return this._scene
+        }
+    }
+
+    /** pos属性 */
+    get pos() {
+        return this._pos
+    }
+    set pos(value) {
+        this._pos = value
+    }
+
+    /** scale属性 */
+    get scale() {
+        return this._scale
+    }
+    set scale(value) {
+        this._scale = value
+    }
+
+    /** visible属性 */
+    get visible() {
+        return this._isVisible
+    }
+    set visible(value) {
+        this._isVisible = value
+    }
+
+    // ===================================================================================================================
+    // 函数
+    /**
+     * Item对象边界区域
+     *
+     * @return  SRect
+     */
+    boundingRect() {
+            return new SRect(0, 0, 10, 10)
+        } // Function boundingRect()
+
+    /**
+     * Item绘制操作
+     *
+     * @param   canvas        画布对象
+     * @param   rect          绘制区域
+     */
+    onDraw(canvas, rect) {
+            for (let item of this.children) {
+                // 保存画布状态
+                canvas.save()
+                    // item位移到指定位置绘制
+                canvas.translate(item.pos.x, item.pos.y)
+                    // 设置绘制区域
+                    // canvas.clip(item.boundingRect())
+                    // 绘制item
+                item.onDraw(canvas, rect)
+                    // 恢复画布状态
+                canvas.restore()
+            }
+        } // Function onDraw()
+
+    /**
+     * 更新Item
+     */
+    update() {
+            // TODO: PLX
+            // scene?.update()
+        } // Function update()
+
+    /**
+     * 移动item到指定位置
+     *
+     * @param   x           新位置的x坐标
+     * @param   y           新位置的y坐标
+     */
+    moveTo(x, y) {
+            this.pos = new SPoint(x, y)
+        } // moveTo()
+
+    /**
+     * 判断item是否包含点x,y
+     *
+     * @param   x       横坐标(当前item)
+     * @param   y       纵坐标(当前item)
+     *
+     * @return  boolean
+     */
+    contains(x, y) {
+            return this.boundingRect().contains(x - this.pos.x, y - this.pos.y)
+        } // Function contains()
+
+    /**
+     * 获得item的路径节点列表。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
+     *
+     * @return  *[]
+     */
+    itemPath() {
+            if (this.parent != null) {
+                let list = this.parent.itemPath()
+                list.push(this)
+                return list
+            }
+
+            return [this]
+        } // Function itemPath()
+
+    /**
+     * 将场景中的xy坐标转换成item坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
+     *
+     * @param   x       场景中的横坐标
+     * @param   y       场景中的纵坐标
+     *
+     * @return  在item中的坐标
+     */
+    mapFromScene(x, y) {
+            let list = this.itemPath()
+            let x0 = x
+            let y0 = y
+            for (let item of list) {
+                x0 = (x0 - item.pos.x) / item.scale.x
+                y0 = (y0 - item.pos.y) / item.scale.y
+            }
+
+            return new SPoint(x0, y0)
+        } // Function mapFromScene()
+
+    /**
+     * 将item中的xy坐标转换成场景坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
+     *
+     * @param   x       item中的横坐标
+     * @param   y       item中的纵坐标
+     *
+     * @return  在场景中的坐标
+     */
+    mapToScene(x, y) {
+            if (this.parent == null) {
+                return new SPoint(x, y)
+            }
+
+            return this.parent.mapToScene(x * this.scale.x + this.pos.x, y * this.scale.y + this.pos.y)
+        } // Function mapToScene()
+
+    // ===================================================================================================================
+    // 事件
+    /**
+     * 鼠标单击事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onClick(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onClick(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            return false
+        } // Function onClick()
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onDbClick(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onDbClick(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            return false
+        } // Function onClick()
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(e) {
+            // console.log(e)
+
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onMouseDown(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            if (this.canMove) {
+                this._mouseDownPos = new SPoint(e.x, e.y)
+                this._isMove = true
+                this[grabItem](this)
+                    // console.log(this.scene.grabItem)
+                return true
+            }
+            return false
+        } // Function onMouseDown()
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseMove(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onMouseMove(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            if (e.buttons & SMouseEvent.LEFT_BUTTON && this.canMove && this._isMove) {
+                this.moveTo(this.pos.x + e.x - this._mouseDownPos.x, this.pos.y + e.y - this._mouseDownPos.y)
+            }
+
+            return false
+        } // Function onMouseMove()
+
+    /**
+     * 释放鼠标事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseUp(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onMouseUp(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            this._isMove = false
+            this[releaseItem]()
+            return false
+        } // Function onMouseUp()
+
+    // ===================================================================================================================
+    // 私有方法
+    /**
+     * 按ZOrder排序
+     *
+     * @param   a   比较元素1
+     * @param   b   比较元素2
+     * @return {number}
+     */
+    [sortItemZOrder](a, b) {
+        return a.zOrder - b.zOrder
+    } // Function sortItemZOrder()
+
+    /**
+     * 鼠标事件转子对象鼠标事件
+     *
+     * @param   child   子对象e
+     * @param   e       事件参数
+     * @return  {}
+     */
+    [toChildMouseEvent](child, e) {
+        let ce = {...e }
+        ce.x = (e.x - child.pos.x) / child.scale.x
+        ce.y = (e.y - child.pos.y) / child.scale.y
+        return ce
+    } // Function toChildMouseEvent()
+
+    /**
+     * 锁定item
+     *
+     * @param   item    被锁定的item
+     */
+    [grabItem](item) {
+        if (this.scene != null) {
+            this.scene.grabItem = item
+        }
+    } // Function grabItem
+
+    /**
+     * 释放被锁定的item
+     */
+    [releaseItem]() {
+        if (this.scene != null) {
+            this.scene.grabItem = null
+        }
+    } // Function grabItem
+} // Class SGraphyItem

+ 201 - 0
src/assets/graphy/SGraphy/SGraphyScene.js

@@ -0,0 +1,201 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+import SPoint from './types/SPoint'
+import SRect from './types/SRect'
+import SGraphyItem from './SGraphyItem'
+
+const toGrabItemMotionEvent = Symbol('toGrabItemMotionEvent')
+
+/**
+ * SGraphy图形引擎场景类
+ *
+ * @author  Andy
+ */
+export default class SGraphyScene {
+    /**
+     * 构造函数
+     */
+    constructor() {
+            this.view = null
+            console.log(this, 'this')
+            this.root = new SGraphyItem(null)
+            this.root._scene = this
+            this.pos = new SPoint(0, 0)
+            this.scale = new SPoint(1, 1)
+            this.grabItem = null
+        } // Function constructor()
+
+    /**
+     * 添加item对象到场景。
+     *
+     * @param   item        添加的对象
+     */
+    addItem(item) {
+            item.parent = this.root
+        } // Function addItem()
+
+    /**
+     * 从场景中移除Item。
+     *
+     * @param   item        被移除的对象
+     */
+    removeItem(item) {
+            item.parent = null
+        } // Function removeItem()
+
+    /**
+     * 绘制场景
+     *
+     * @param   canvas      画布
+     * @param   rect        更新绘制区域
+     */
+    drawScene(canvas, rect) {
+            this.root.onDraw(canvas, rect)
+        } // Function drawScene()
+
+    /**
+     * 绘制背景
+     *
+     * @param   canvas      画布
+     * @param   rect        更新绘制区域
+     */
+    drawBackground(canvas, rect) {
+            // DO NOTHING
+        } // Function drawBackground()
+
+    /**
+     * 绘制前景
+     *
+     * @param   canvas      画布
+     * @param   rect        更新绘制区域
+     */
+    drawForeground(canvas, rect) {
+            // DO NOTHING
+        } // Function drawForeground()
+
+    /**
+     * 返回场景的item边界。即所有item边界的并集。
+     *
+     * @return  SRect
+     */
+    worldRect() {
+            let rect = null
+
+            for (let item of this.root.children) { // 依次取item列中的所有item。将所有item的边界做并焦处理。
+                if (rect == null) {
+                    rect = item.boundingRect().adjusted(item.pos)
+                } else {
+                    rect.union(item.boundingRect().adjusted(item.pos))
+                }
+            }
+
+            return rect
+        } // Function worldsRect()
+
+    /**
+     * 更新
+     */
+    update() {} // Function update()
+        // ===================================================================================================================
+        // 事件
+        /**
+         * 鼠标单击事件
+         *
+         * @param   e   保存事件参数
+         * @return  boolean
+         */
+    onClick(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onClick(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onClick(e)
+        } // onClick
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onDbClick(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onDbClick(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onDbClick(e)
+        } // onClick
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onMouseDown(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onMouseDown(e)
+        } // onMouseDown
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseMove(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onMouseMove(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onMouseMove(e)
+        } // onMouseMove
+
+    /**
+     * 释放鼠标事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseUp(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onMouseUp(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onMouseUp(e)
+        } // onMouseUp
+
+    /**
+     * 转换场景事件坐标到指定Item坐标事件
+     *
+     * @param   item        指定的item对象
+     * @param   e           场景事件
+     * @return  {}
+     */
+    [toGrabItemMotionEvent](item, e) {
+        let se = {...e }
+        let p = item.mapFromScene(e.x, e.y)
+        se.x = p.x
+        se.y = p.y
+        return se
+    } // Function toGrabItemMotionEvent()
+} // Class SGraphyScene

+ 280 - 0
src/assets/graphy/SGraphy/SGraphyView.js

@@ -0,0 +1,280 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+import './SCanvas'
+import SPoint from './types/SPoint'
+import SMouseEvent from './SMouseEvent'
+
+const bindEvent = Symbol('bindEvent')
+const toSceneMotionEvent = Symbol('toSceneMotionEvent')
+    /**
+     * Graphy图形引擎视图类
+     *
+     * @author  Andy
+     */
+export default class SGraphyView {
+    /**
+     * 构造函数
+     *
+     * @param   id    Canvas对象ID
+     */
+    constructor(id, scene) {
+            this.canvasView = document.getElementById(id)
+            this.canvas = this.canvasView.getContext('2d')
+            this[bindEvent](this.canvasView)
+            this.scene = scene
+            this.pos = new SPoint(10, 0)
+            this.scale = 1
+            this.minScale = 0.004
+            this.maxScale = 0.5
+            this._midKeyX = 0
+            this._midKeyY = 0
+            this.wheelZoom = 1.05
+
+            window.requestAnimationFrame(this.onDraw.bind(this))
+        } // Function constructor()
+
+    /**
+     * 绑定canvas事件
+     *
+     * @param   canvas      canvas对象
+     * @private
+     */
+    [bindEvent](canvasView) {
+        canvasView.onclick = this.onClick.bind(this)
+        canvasView.ondblclick = this.onDbClick.bind(this)
+        canvasView.onmousedown = this.onMouseDown.bind(this)
+        canvasView.onmousemove = this.onMouseMove.bind(this)
+        canvasView.onmouseup = this.onMouseUp.bind(this)
+        canvasView.onmousewheel = this.onMouseWheel.bind(this)
+        canvasView.onresize = this.onResize.bind(this)
+    } // Function [bindEvent]()
+
+    /**
+     * 获取canvas的宽度  
+     */
+
+    get width() {
+        return this.canvasView.width
+    }
+
+    get height() {
+        return this.canvasView.height
+    }
+
+    /**
+     * 将场景中的xy坐标转换成视图坐标。
+     *
+     * @param   x       场景中的横坐标
+     * @param   y       场景中的纵坐标
+     *
+     * @return  SPoint
+     */
+    mapFromScene(x, y = null) {
+            if (typeof(x) === 'object') { // 如果传入的是SPoint对象
+                return new SPoint(x.x * this.scale + this.pos.x, x.y * this.scale + this.pos.y)
+            }
+
+            return new SPoint(x * this.scale + this.pos.x, y * this.scale + this.pos.y)
+        } // Function mapFromScene()
+
+    /**
+     * 将item中的xy坐标转换成场景坐标。
+     *
+     * @param   x       item中的横坐标
+     * @param   y       item中的纵坐标
+     * @return  SPoint
+     */
+    mapToScene(x, y = null) {
+            if (typeof(x) === 'object') { // 如果传入的是SPoint对象
+                return new SPoint((x.x - this.pos.x) / this.scale, (x.y - this.pos.y) / this.scale)
+            }
+
+            return new SPoint((x - this.pos.x) / this.scale, (y - this.pos.y) / this.scale)
+        } // Function mapToScene()
+
+    /**
+     * 缩放视图时计算视图的位置与缩放比例
+     *
+     * @param   zoom        缩放比例
+     * @param   x0          缩放计算的中心点X坐标
+     * @param   y0          缩放计算的中心点Y坐标
+     */
+    scaleByPoint(zoom, x0, y0) {
+            let z = zoom
+                /**
+                 * 缩放比例在最小比例和最大比例范围内
+                 */
+            if (this.scale * zoom >= this.maxScale) { // 大于最大缩放比例
+                z = this.maxScale / this.scale
+                this.scale = this.maxScale
+            } else if (this.scale * zoom <= this.minScale) { // 小于最小绽放比例
+                z = this.minScale / this.scale
+                this.scale = this.minScale
+            } else {
+                this.scale *= zoom
+            }
+
+            this.pos.x = x0 - (x0 - this.pos.x) * z
+            this.pos.y = y0 - (y0 - this.pos.y) * z
+
+            // EventBus.getDefault().post(SGraphyViewZoomEvent(this, scale))
+            // EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
+            // return
+        } // Function scaleByPoint()
+        // ===================================================================================================================
+        // 事件
+    onDraw() {
+            this.canvas.save()
+            this.canvas.clearRect(0, 0, this.canvasView.width, this.canvasView.height)
+            this.canvas.restore()
+
+            if (this.scene != null) {
+                // 绘制背景
+                this.canvas.save()
+                this.scene.drawBackground(this.canvas)
+                this.canvas.restore()
+
+                // 绘制场景
+                this.canvas.save()
+                this.canvas.translate(this.pos.x, this.pos.y)
+                this.canvas.scale(this.scale, this.scale)
+                this.scene.drawScene(this.canvas)
+                this.canvas.restore()
+
+                // 绘制前景
+                this.canvas.save()
+                this.scene.drawForeground(this.canvas)
+                this.canvas.restore()
+            }
+
+            window.requestAnimationFrame(this.onDraw.bind(this))
+        } // Function onDraw()
+
+    /**
+     * 鼠标单击事件
+     *
+     * @param   e   保存事件参数
+     */
+    onClick(e) {
+            if (this.scene != null) {
+                let se = this[toSceneMotionEvent](e)
+                this.scene.onClick(se)
+            }
+        } // Function onClick()
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param   e   保存事件参数
+     */
+    onDbClick(e) {
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onDbClick(ce)
+            }
+        } // Function onDbClick()
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseDown(e) {
+            let se = new SMouseEvent(e)
+            if (se.buttons & SMouseEvent.MIDDLE_BUTTON) { // 如果按下中键
+                this._midKeyX = e.x
+                this._midKeyY = e.y
+            }
+
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onMouseDown(ce)
+            }
+        } // Function onMouseDown()
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseMove(e) {
+            let se = new SMouseEvent(e)
+            if (se.buttons & SMouseEvent.MIDDLE_BUTTON) { // 如果按下中键,则移动视图
+                this.pos.x += e.x - this._midKeyX
+                this.pos.y += e.y - this._midKeyY
+                this._midKeyX = e.x
+                this._midKeyY = e.y
+                return
+            }
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onMouseMove(ce)
+            }
+        } // Function onMouseMove()
+
+    /**
+     * 释放鼠标事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseUp(e) {
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onMouseUp(ce)
+            }
+        } // Function onMouseUp()
+
+    /**
+     * 鼠标滚轮事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseWheel(e) {
+            let se = new SMouseEvent(e)
+            if (e.wheelDelta < 0) {
+                this.scaleByPoint(1 / this.wheelZoom, se.x, se.y)
+            } else {
+                this.scaleByPoint(this.wheelZoom, se.x, se.y)
+            }
+        } // Function onMouseWheel()
+
+    /**
+     * View大小发生变化事件
+     *
+     * @param   e   保存事件参数
+     */
+    onResize(e) {} // Function onResize()
+
+    /**
+     * MotionEvent转场景对象MotionEvent
+     *
+     * @param   e       MotionEvent
+     * @return  子对象MotionEvent
+     */
+    [toSceneMotionEvent](e) {
+        let se = new SMouseEvent(e)
+        se.x = (se.x - this.pos.x) / this.scale
+        se.y = (se.y - this.pos.y) / this.scale
+        return se
+    } // Function toSceneMotionEvent()
+} // Class SGraphyView

+ 53 - 0
src/assets/graphy/SGraphy/SMouseEvent.js

@@ -0,0 +1,53 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * 鼠标事件
+ *
+ * @author  Andy
+ */
+export default class SMouseEvent {
+  /**
+   * 构造函数
+   *
+   * @param   e       系统鼠标事件
+   */
+  constructor(e) {
+    let bbox          = e.srcElement.getBoundingClientRect()
+    this.type         = e.type
+    this.x            = e.clientX - bbox.left
+    this.y            = e.clientY - bbox.top
+    this.screenX      = e.screenX
+    this.screenY      = e.screenY
+    this.clientX      = e.clientX
+    this.clientY      = e.clientY
+    this.altKey       = e.altKey
+    this.ctrlKey      = e.ctrlKey
+    this.buttons      = e.buttons
+    this.wheelDelta   = e.wheelDelta
+  } // Function constructor()
+} // Class MouseEvent
+
+SMouseEvent.LEFT_BUTTON   = 0x01
+SMouseEvent.RIGHT_BUTTON  = 0x02
+SMouseEvent.MIDDLE_BUTTON = 0x04

+ 70 - 0
src/assets/graphy/SGraphy/items/SGraphyCircleItem.js

@@ -0,0 +1,70 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+export default class SGraphyCircleItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param X  圆中心点X
+     * @param Y  圆中心点Y
+     * @param Radius   圆的半径
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     */
+    constructor(X, Y, color, Radius, isSolid, name, parent = null) {
+        super(parent)
+        this.X = X
+        this.Y = Y
+        this.color = color
+        this.Radius = Radius
+        this.isSolid = isSolid
+        this.minX = this.X - this.Radius
+        this.minY = this.Y - this.Radius
+        this.maxX = this.X + this.Radius
+        this.maxY = this.Y + this.Radius
+        this.sAngle = null || 0
+        this.eAngle = null || 2 * Math.PI
+        this.name = name
+        this.type = 6
+        this.lineWidth = null
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        canvas.lineWidth = this.lineWidth || 240
+        canvas.strokeStyle = this.color || '#000'
+        canvas.fillStyle = this.color || '#000'
+        canvas.beginPath();
+        canvas.arc(this.X, this.Y, this.Radius, this.sAngle, this.eAngle);
+        if (!!this.isSolid) {
+            canvas.fillStyle = this.color; //填充颜色,默认是黑色
+            canvas.fill(); //画实心圆
+        }
+        canvas.stroke()
+        if (!!this.name) {
+            canvas.font = "oblique small-caps bold " + this.lineWidth * 10 + "px Arial";
+            // canvas.font = "oblique small-caps bold " + 10 + "px Arial";
+            canvas.fillStyle = 'green'
+            canvas.fillText(this.name, this.X, this.Y);
+        }
+    }
+}

+ 201 - 0
src/assets/graphy/SGraphy/items/SGraphyClockItem.js

@@ -0,0 +1,201 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect'
+
+/** 定义符号,用于定义私有成员变晴儿 */
+const drawScale     = Symbol('drawScale')
+const drawScaleText = Symbol('drawScaleText')
+const drawHour      = Symbol('drawHour')
+const drawMinute    = Symbol('drawMinute')
+const drawSecond    = Symbol('drawSecond')
+
+/**
+ * SGraphy引擎时钟Item
+ *
+ * @author  Andy
+ */
+export default class SGraphyClockItem extends SGraphyItem {
+  /**
+   * 构造函数
+   *
+   * @param   parent    指向父对象
+   */
+  constructor(width, height, parent = null) {
+    super(parent)
+    this.name = 'ClockItem'
+    this.width = width
+    this.height = height
+    /** 是否显示刻度 */
+    this.isShowScale = true
+    /** 刻度颜色 */
+    this.scaleColor = '#000'
+    /** 刻度文本颜色 */
+    this.textColor = '#000'
+    /** 时针颜色 */
+    this.hourColor = '#000'
+    /** 分针颜色 */
+    this.minuteColor = '#000'
+    /** 秒针颜色 */
+    this.secondColor = '#F00'
+    /** 是否显示钞针 */
+    this.isShowSecond = true
+    /** 是否平滑移动秒针 */
+    this.isSmooth = true
+    /** 边缘宽度 */
+    this.padding = 100.0
+  } // Function constructor()
+
+  /**
+   * Item对象边界区域
+   *
+   * @return  SRect
+   */
+  boundingRect() {
+    return new SRect(0, 0, this.width / 2, this.height / 2)
+  } // Function boundingRect()
+
+  /**
+   * 时钟半径,只读属性
+   *
+   * @return  number
+   */
+  get radius() {
+    return Math.min(this.width, this.height) / 2.0
+  } // getter radius()
+
+  /**
+   * 绘制时钟
+   *
+   * @param   canvas      画布
+   * @param   rect        绘制区域
+   */
+  onDraw(canvas, rect) {
+    canvas.translate(this.width / 2, this.height / 2)
+    canvas.arc(0, 0, this.radius, 0, 2 * Math.PI)
+    let t = new Date()
+
+    this[drawScale](canvas)
+    this[drawHour](canvas, t.getHours(), t.getMinutes(), t.getSeconds())
+    this[drawMinute](canvas, t.getMinutes(), t.getSeconds())
+    this[drawSecond](canvas, t.getSeconds() + t.getMilliseconds() / 1000.0)
+  } // Function onDraw()
+
+  /**
+   * 绘制刻度
+   *
+   * @param   canvas      画布
+   */
+  [drawScale](canvas) {
+    let scaleLength = Math.max(this.radius / 10.0, 2.0)
+    let scaleLength1 = scaleLength * 1.2
+    let strokeWidth = Math.max(this.radius / 100.0, 2.0)
+    let strokeWidth1 = strokeWidth * 2.0
+
+    canvas.save()
+    canvas.strokeStyle = this.scaleColor
+
+    for (let i = 1; i <= 12; i++) {          // 12小时刻度
+      canvas.lineWidth = strokeWidth1
+      canvas.drawLine(0, -this.radius, 0, -this.radius + scaleLength1)
+
+      if (this.radius >= 40) {              // 如果半度大于40显示分钟刻度
+        canvas.rotate(6 * Math.PI / 180)
+        for (let j = 1; j <= 4; j++) {       // 分钟刻度
+          canvas.lineWidth = strokeWidth
+          canvas.drawLine(0, -this.radius, 0, -this.radius + scaleLength)
+          canvas.rotate(6 * Math.PI / 180)
+        }
+      } else {
+        canvas.rotate(30 * Math.PI / 180)
+      }
+    }
+
+    canvas.restore()
+  } // Function drawScale()
+
+  /**
+   * 绘制刻度数字
+   *
+   * @param   canvas      画布
+   */
+  [drawScaleText](canvas) {
+
+  } // Function drawScaleText()
+
+  /**
+   * 绘制时针
+   *
+   * @param   canvas      画布
+   * @param   hour        时
+   * @param   minute      分
+   * @param   second      秒
+   */
+  [drawHour](canvas, hour, minute, second) {
+    canvas.save()
+    canvas.lineCap = 'round'
+    canvas.lineWidth = Math.max(this.radius / 30.0, 4.0)
+    canvas.strokeStyle = this.hourColor
+    canvas.rotate((hour * 30.0 + minute * 30.0 / 60 + second * 30.0 / 60 / 60) * Math.PI / 180)
+    canvas.drawLine(0, this.radius / 10.0, 0, -this.radius / 2.0)
+    canvas.restore()
+  } // Function drawHour()
+
+  /**
+   * 绘制分针
+   *
+   * @param   canvas      画布
+   * @param   minute      分
+   * @param   second      秒
+   */
+  [drawMinute](canvas, minute, second) {
+    canvas.save()
+    canvas.lineCap = 'round'
+    canvas.lineWidth = Math.max(this.radius / 40.0, 4.0)
+    canvas.strokeStyle = this.minuteColor
+    canvas.rotate((minute * 6 + second * 6 / 60.0) * Math.PI / 180.0)
+    canvas.drawLine(0, this.radius / 10.0, 0, -this.radius * 2.0 / 3.0)
+    canvas.restore()
+  } // Function drawMinute()
+
+  /**
+   * 绘制秒针
+   *
+   * @param   canvas      画布
+   * @param   second      秒
+   */
+  [drawSecond](canvas, second) {
+    canvas.save()
+    canvas.lineCap = 'round'
+    canvas.lineWidth = Math.max(this.radius / 100.0, 3.0)
+    canvas.strokeStyle = this.secondColor
+    canvas.rotate(second * 6 * Math.PI / 180)
+    canvas.drawLine(0, this.radius / 5.0, 0, -this.radius + this.radius / 10.0)
+    // canvas.drawCircle(0, 0, this.radius / 30.0)
+    // canvas.drawCircle(0, -this.radius + this.radius / 5.0, this.radius / 60.0)
+    // canvas.strokeStyle = Color.YELLOW
+    // canvas.drawCircle(0, 0, this.radius / 100.0)
+    canvas.restore()
+  } // Function drawSecond()
+} // Class SGraphyClockItem

+ 59 - 0
src/assets/graphy/SGraphy/items/SGraphyImageItem.js

@@ -0,0 +1,59 @@
+/**
+ * 图片
+ * 
+ * 
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect'
+export default class SGraphyImageItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param width  图片宽度
+     * @param height  图片高度
+     * @param url    图片url
+     * @param id     point的Id
+     * @param X      图片向x轴的偏移量
+     * @param Y      图片向y轴的偏移量
+     * @param downUrl 图片按下时的url
+     * @param parent  指向父元素
+     */
+    constructor(width, height, url, id, X, Y, downUrl, parent = null) {
+            super(parent)
+            this.width = width
+            this.height = height
+            this.url = url
+            this.id = id
+            this.X = X
+            this.Y = Y
+            this.downUrl = downUrl
+            this.imgFalg = false
+            this.img = new Image()
+            this.img.src = this.url
+            this.img.style.width = this.width
+            this.img.style.height = this.height
+            this.canMove = true
+            this.type = 1
+        } //constructor
+
+    /**
+     * Item对象边界区域
+     *
+     * @return  SRect
+     */
+    boundingRect() {
+            return new SRect(0, 0, this.width * 100, this.height * 100)
+        } // Function boundingRect()
+
+    /**
+     * 绘制图片
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        // canvas.moveTo(this.X, this.Y)
+        // canvas.drawImage(this.img, 0, 0, this.width, this.height, this.X, this.Y, this.width * 100, this.height * 100)
+        canvas.drawImage(this.img, 0, 0, this.width, this.height, 0, 0, this.width * 100, this.height * 100)
+    }
+}

+ 66 - 0
src/assets/graphy/SGraphy/items/SGraphyLineItem.js

@@ -0,0 +1,66 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+export default class SGraphyLineItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param startX  线的起始x坐标
+     * @param startY  线的起始y坐标
+     * @param endX    线的终止x坐标
+     * @param endY    线的终止y坐标
+     * @param width   线的宽度
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     */
+    constructor(startX, startY, endX, endY, color, width, isVirtual, parent = null) {
+        super(parent)
+        this.startX = startX
+        this.startY = startY
+        this.endX = endX
+        this.endY = endY
+        this.color = color
+        this.width = width
+        this.isVirtual = isVirtual
+        this.minX = Math.min(this.startX, this.endX)
+        this.minY = Math.min(this.startY, this.endY)
+        this.maxX = Math.max(this.startX, this.endX)
+        this.maxY = Math.max(this.startY, this.endY)
+        this.name = null
+        this.type = 2
+        this.lineWidth = null
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        canvas.lineWidth = this.lineWidth || this.width || 240
+        canvas.strokeStyle = this.color || '#000'
+        if (this.isVirtual) {
+            canvas.setLineDash([100, 80])
+            canvas.strokeStyle = 'green'
+        }
+        canvas.beginPath();
+        canvas.moveTo(this.startX, this.startY)
+        canvas.lineTo(this.endX, this.endY)
+        canvas.stroke()
+    }
+}

+ 76 - 0
src/assets/graphy/SGraphy/items/SGraphyPillarItems.js

@@ -0,0 +1,76 @@
+/**
+ * 不规则多边形,元空间
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+function getItem(arr, name) {
+    if (arr && arr.length) {
+        return arr.map(item => {
+            return item[name]
+        })
+    } else {
+        return [0]
+    }
+}
+
+
+export default class SGraphyPolygonItem extends SGraphyItem {
+    /**
+     * 
+     * 构造函数
+     * 
+     * @param jsonArr  空间线条数组
+     * @param lineWidth    空间线条的宽度
+     * @param color    空间线条的颜色
+     * @param fillColor  空间的填充颜色
+     * 
+     */
+    constructor(jsonArr, lineWidth, color, fillColor, parent = null) {
+            super(parent)
+            this.jsonArr = jsonArr
+            this.lineWidth = lineWidth
+            this.color = color
+            this.fillColor = fillColor
+            let xArr = getItem(this.jsonArr, 'X')
+            let yArr = getItem(this.jsonArr, 'Y')
+            this.minX = Math.min.apply(null, xArr) || 0
+            this.minY = Math.min.apply(null, yArr) || 0
+            this.width = Math.max.apply(null, xArr) - this.minX || 0
+            this.height = Math.max.apply(null, yArr) - this.minY || 0
+            this.type = 5
+        } //constructor
+
+    /**
+     * Item的边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.width, this.height)
+    }
+
+    /**
+     * 绘制不规则多边形
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        if (this.jsonArr && this.jsonArr.length) {
+            canvas.beginPath();
+            canvas.lineWidth = 240
+            canvas.lineCap = 'butt';
+            canvas.strokeStyle = this.color || '#000'
+            canvas.fillStyle = this.fillColor || '#fff'
+            canvas.moveTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            for (let i = 1; i < this.jsonArr.length; i++) {
+                canvas.lineTo(this.jsonArr[i].X, this.jsonArr[i].Y)
+            }
+            canvas.lineTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            canvas.closePath()
+            canvas.fill()
+            canvas.stroke()
+        }
+    }
+}

+ 257 - 0
src/assets/graphy/SGraphy/items/SGraphyPolygonItem.js

@@ -0,0 +1,257 @@
+/**
+ * 不规则多边形,元空间
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+function getItem(arr, name) {
+    if (arr && arr.length) {
+        return arr.map(item => {
+            return item[name]
+        })
+    } else {
+        return [0]
+    }
+}
+
+function changeArr(arr) {
+    if (arr && arr.length) {
+        return arr.map(item => {
+            return [
+                item.X, item.Y
+            ]
+        })
+    } else {
+        return [0]
+    }
+}
+/**
+ * 求不规则多边形重点
+ * @param {points} 数组,多个点位坐标
+ *  
+ * @return {x, y} 重点点位坐标
+ */
+// function getCenterOfGravityPoint(points) {
+//     let area = 0,
+//         gx = 0,
+//         gy = 0,
+//         i = 1,
+//         px1, px2, py1, py2, temp, length = points.length;
+//     for (i; i <= length; i++) {
+//         px1 = points[(i % length)].X;
+//         px2 = points[(i - 1)].X;
+//         py1 = points[(i % length)].Y;
+//         py2 = points[(i - 1)].Y;
+//         temp = (px1 * py2 - py1 * px2) / 2
+//         area += temp;
+//         gx += temp * (px1 + px2) / 3
+//         gy += temp * (py1 + py2) / 3
+//     }
+
+//     gx = gx / area
+//     gy = gy / area
+
+//     return {
+//         x: gx,
+//         y: gy
+//     }
+// }
+
+
+export default class SGraphyPolygonItem extends SGraphyItem {
+    /**
+     * 
+     * 构造函数
+     * 
+     * @param jsonArr  空间线条数组
+     * @param lineWidth    空间线条的宽度
+     * @param color    空间线条的颜色
+     * @param fillColor  空间的填充颜色
+     * 
+     */
+    constructor(jsonArr, lineWidth, color, fillColor, id, centerOfGravityPoint, name, paths, faceColor, businessColor, isBusiness, parent = null) {
+            super(parent)
+            this.jsonArr = jsonArr
+            this.lineWidth = lineWidth
+            this.color = color
+            this.id = id
+            this.name = name //实际渲染名字
+            this.fillColor = fillColor
+            let xArr = getItem(this.jsonArr, 'X')
+            let yArr = getItem(this.jsonArr, 'Y')
+            this.minX = Math.min.apply(null, xArr) || 0
+            this.maxX = Math.max.apply(null, xArr) || 0
+            this.maxY = Math.max.apply(null, yArr) || 0
+            this.minY = Math.min.apply(null, yArr) || 0
+            this.width = this.maxX - this.minX || 0
+            this.height = this.maxY - this.minY || 0
+            this.type = 3
+            this.businessName = null
+            this.faceColor = faceColor || '#cacaca' //颜色
+            this.businessId = null //业务空间id
+            this.isBusiness = isBusiness || 1 //状态
+            this.businessColor = businessColor || 'rgba(68,161,140,.4)' //业务空间颜色
+            this.businessFaceColor = "#333"
+            this.containsArr = changeArr(this.jsonArr)
+            this.paths = null
+            if (paths && paths.length > 1) {
+                this.paths = paths.map(item => {
+                    if (item && item.length) {
+                        return changeArr(item)
+                    } else {
+                        return undefined
+                    }
+                }).filter(d => d)
+            }
+            this.centerOfGravityPoint = centerOfGravityPoint
+        } //constructor
+
+    /**
+     * Item的边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.width, this.height)
+    }
+
+
+    /**
+     * 判断item是否包含点x,y
+     *
+     * @param   x       横坐标(当前item)
+     * @param   y       纵坐标(当前item)
+     *
+     * @return  boolean
+     */
+    contains(x, y) {
+        let falg = false,
+            isFullIn = false //是否在镂空图形内
+        if (this.paths instanceof Array) {
+            for (let i = 1; i < this.paths.length; i++) {
+                if (this.isIn(x, y, this.paths[i])) {
+                    //位置信息在镂空图形内
+                    isFullIn = true
+                    break
+                }
+            }
+            // //如果鼠标在大图形内切在镂空图形中返回false
+            if (this.isIn(x, y, this.containsArr) && isFullIn) {
+                falg = false
+            } else if (this.isIn(x, y, this.containsArr) && !isFullIn) {
+                falg = true
+            } else {
+                falg = this.isIn(x, y, this.containsArr)
+            }
+        } else {
+            falg = this.isIn(x, y, this.containsArr)
+        }
+        return falg
+    }
+
+
+    isIn(x, y, json) {
+        let nCross = 0,
+            point = typeof(x) == 'object' ? [x.x, x.y] : [x, y],
+            APoints = json,
+            length = APoints.length,
+            p1, p2, i, xinters;
+        p1 = APoints[0];
+        for (i = 1; i <= length; i++) {
+            p2 = APoints[i % length];
+            if (
+                point[0] > Math.min(p1[0], p2[0]) &&
+                point[0] <= Math.max(p1[0], p2[0])
+            ) {
+                if (point[1] <= Math.max(p1[1], p2[1])) {
+                    if (p1[0] != p2[0]) {
+                        //计算位置信息
+                        xinters = (point[0] - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1];
+                        if (p1[1] == p2[1] || point[1] <= xinters) {
+                            nCross++
+                        }
+                    }
+                }
+            }
+            p1 = p2;
+        }
+        if (nCross % 2 == 0) {
+            return false
+        } else {
+            return true
+        }
+
+    }
+
+    /**
+     * 绘制不规则多边形
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        if (this.jsonArr && this.jsonArr.length) {
+            canvas.beginPath();
+            canvas.lineWidth = 220
+            canvas.lineCap = 'butt';
+            if (this.isBusiness == 1) {
+                canvas.strokeStyle = this.color || '#000'
+                canvas.fillStyle = this.fillColor
+            } else if (this.isBusiness == 2) {
+                //已有id 的业务空间
+                canvas.strokeStyle = this.color || '#000'
+                canvas.fillStyle = this.businessColor || '#fff'
+            } else if (this.isBusiness == 3) {
+                //被选择的元空间
+                canvas.strokeStyle = this.color || '#000'
+                canvas.lineWidth = 800
+                canvas.fillStyle = '#1abc9c'
+            } else if (this.isBusiness == 4) {
+                canvas.strokeStyle = 'rgba(251,226,1,.8)' || '#000'
+                canvas.fillStyle = '#fff' || '#fff'
+            } else if (this.isBusiness == 5) {
+                canvas.fillStyle = 'rgba(11,12,12,.2)' || '#fff'
+            } else if (this.isBusiness == 6) {
+                canvas.fillStyle = '#1abc9c'
+                canvas.lineWidth = 800
+                canvas.strokeStyle = 'rgba(68,161,140,.4)' || '#fff'
+            } else if (this.isBusiness == 7) {
+                canvas.strokeStyle = this.color || '#000'
+                canvas.fillStyle = this.businessColor || '#fff'
+            }
+            canvas.moveTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            for (let i = 1; i < this.jsonArr.length; i++) {
+                canvas.lineTo(this.jsonArr[i].X, this.jsonArr[i].Y)
+            }
+            canvas.lineTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            canvas.closePath()
+            canvas.fill()
+            canvas.stroke()
+            if (!!this.name) {
+                canvas.font = "normal small-caps bold 500px Arial";
+                if (this.isBusiness == 1) {
+                    canvas.fillStyle = this.faceColor
+                } else if (this.isBusiness == 2) {
+                    canvas.fillStyle = this.businessFaceColor;
+                } else if (this.isBusiness == 3) {
+                    //业务空间异常状态
+                    canvas.fillStyle = '#fff'
+                } else if (this.isBusiness == 4) {
+                    canvas.fillStyle = '#cacaca'
+                } else if (this.isBusiness == 6) {
+                    canvas.fillStyle = '#fff'
+                } else if (this.isBusiness == 7) {
+                    canvas.fillStyle = 'red'
+                }
+                if (!!this.businessName || !!this.businessId) {
+                    name = '👇   ' + this.businessName
+                } else {
+                    name = '⬇️   ' + this.name
+                }
+                canvas.fillText(name, this.centerOfGravityPoint.x, this.centerOfGravityPoint.y);
+                // canvas.fillText(this.name, (this.maxX - this.minX) / 2 + this.minX, (this.maxY - this.minY) / 2 + this.minY);
+            }
+            // canvas.fillText(this.name, this.jsonArr[0].X, this.jsonArr[0].Y);
+        }
+    }
+}

+ 100 - 0
src/assets/graphy/SGraphy/items/SGraphyRectItem.js

@@ -0,0 +1,100 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+export default class SGraphyRectItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param X  矩形的开始点X
+     * @param Y  矩形的开始点Y
+     * @param width   矩形的宽度
+     * @param height  矩形的高度
+     * 
+     * @param isFill  矩形的是否填充
+     * @param fillColor  矩形的填充色彩
+     * @param text  矩形的文字
+     * @param textSize  矩形的文字大小
+     * @param color  矩形的颜色
+     * @param Tip   提示
+     */
+    constructor(X, Y, width, height, isFill, fillColor, text, textSize, color, Tip, parent = null) {
+        super(parent)
+        this.X = X
+        this.Y = Y
+        this.width = width
+        this.height = height
+        this.isFill = isFill
+        this.fillColor = fillColor
+        this.color = color
+        this.textSize = textSize || 6
+        this.type = 10
+        this.hoverColor = null
+        this.text = text.split(",")
+        this.fontStart = this.X
+        this.Tip = Tip
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.X, this.Y, this.width, this.height)
+    }
+
+    /**
+     * 判断item是否包含点x,y
+     *
+     * @param   x       横坐标(当前item)
+     * @param   y       纵坐标(当前item)
+     *
+     * @return  boolean
+     */
+    contains(x) {
+        if (this.X + this.width > x.x && x.x > this.X && this.Y + this.height > x.y && x.y > this.Y) {
+            return true
+        } else {
+            return false
+        }
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        canvas.beginPath();
+        canvas.lineWidth = "1";
+        if (this.isFill) {
+            if (!!this.hoverColor) {
+                canvas.fillStyle = this.hoverColor
+            } else {
+                canvas.fillStyle = this.fillColor
+            }
+            canvas.fillRect(this.X, this.Y, this.width, this.height);
+        } else {
+            canvas.rect(this.X, this.Y, this.width, this.height);
+        }
+        canvas.stroke();
+        if (!!this.text && this.text.length <= 1) {
+            canvas.font = this.textSize + "px 宋体";
+            canvas.fillStyle = this.color
+            canvas.fillText(this.text[0], this.fontStart, this.Y + this.height / 2);
+        } else if (!!this.text && this.text.length > 1) {
+            canvas.font = this.textSize + "px 宋体";
+            canvas.fillStyle = this.color
+            for (let i = 0; i < this.text.length; i++) {
+                canvas.fillText(this.text[i], this.fontStart, this.Y + this.height / 2 - this.textSize * i);
+            }
+        } else {
+            return
+        }
+    }
+}

+ 51 - 0
src/assets/graphy/SGraphy/items/SGraphyTextItems.js

@@ -0,0 +1,51 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+export default class SGraphyTextItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param X  文字的开始点X
+     * @param Y  文字的开始点Y
+     * @param width   文字的宽度
+     * 
+     * @param color  文字的颜色
+     * @param text   文字的文字
+     */
+    constructor(X, Y, width, color, text, falg, font, parent = null) {
+        super(parent)
+        this.X = X
+        this.Y = Y
+        this.lineWidth = width
+        this.color = color
+        this.font = font ? font : "6px 宋体"
+        this.text = falg ? text + '→' : text
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.X, this.Y, 0, 0)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        if (!!this.text) {
+            canvas.font = this.font;
+            canvas.fillStyle = this.color
+            canvas.fillText(this.text, this.X, this.Y);
+        }
+    }
+}

+ 66 - 0
src/assets/graphy/SGraphy/items/SGraphyVirtualItem.js

@@ -0,0 +1,66 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect';
+
+export default class SGraphyLineItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param startX  线的起始x坐标
+     * @param startY  线的起始y坐标
+     * @param endX    线的终止x坐标
+     * @param endY    线的终止y坐标
+     * @param width   线的宽度
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     * 
+     * @param canMove  移动
+     */
+    constructor(startX, startY, endX, endY, color, width, isVirtual, canMove, parent = null) {
+        super(parent)
+        this.startX = startX
+        this.startY = startY
+        this.endX = endX
+        this.endY = endY
+        this.color = color
+        this.width = width
+        this.isVirtual = isVirtual
+        this.minX = Math.min(this.startX, this.endX)
+        this.minY = Math.min(this.startY, this.endY)
+        this.maxX = Math.max(this.startX, this.endX)
+        this.maxY = Math.max(this.startY, this.endY)
+        this.type = 4
+            // this.canMove = true
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, (this.maxX - this.minX), (this.maxY - this.minY))
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        if (this.isVirtual) {
+            canvas.setLineDash([240, 240])
+        }
+        canvas.lineWidth = 240
+        canvas.strokeStyle = this.color || '#000'
+        canvas.beginPath();
+        canvas.moveTo(this.startX, this.startY)
+        canvas.lineTo(this.endX, this.endY)
+        canvas.stroke()
+    }
+}

+ 40 - 0
src/assets/graphy/SGraphy/types/SPoint.js

@@ -0,0 +1,40 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * 坐标点
+ *
+ * @author  Andy
+ */
+export default class SPoint {
+  /**
+   * 构造函数
+   *
+   * @param   x     X坐标
+   * @param   y     Y坐标
+   */
+  constructor(x, y) {
+    this.x = x
+    this.y = y
+  } // constructor
+} // Class SPoint

+ 169 - 0
src/assets/graphy/SGraphy/types/SRect.js

@@ -0,0 +1,169 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * 矩形
+ *
+ * @author  Andy
+ */
+export default class SRect {
+  /**
+   * 构造函数
+   *
+   * @param   x       X坐标
+   * @param   y       Y坐标
+   * @param   width   宽度
+   * @param   height  高度
+   */
+  constructor(x = 0, y = 0, width = 0, height = 0) {
+    this._x = x
+    this._y = y
+    this._width = Math.max(width, 0)
+    this._height = Math.max(height, 0)
+  } // constructor
+
+  /**
+   * left属性
+   */
+  get left() {
+    return this._x
+  }
+  set left(value) {
+    this._x = value
+  }
+
+  /**
+   * right属性
+   */
+  get right() {
+    return this._x + this._width
+  }
+  set right(value) {
+    this._width = Math.max(value - this._x, 0)
+  }
+
+  /**
+   * top属性
+   */
+  get top() {
+    return this._y
+  }
+  set top(value) {
+    this._y = value
+  }
+
+  /**
+   * top属性
+   */
+  get bottom() {
+    return this._y + this._height
+  }
+  set bottom(value) {
+    this._height = Math.max(value - this._y, 0)
+  }
+
+  /**
+   * x属性
+   *
+   * @return {*}
+   */
+  get x() {
+    return this._x
+  }
+  set x(value) {
+    this._x = value
+  }
+
+  /**
+   * x属性
+   *
+   * @return {*}
+   */
+  get y() {
+    return this._y
+  }
+  set y(value) {
+    this._y = value
+  }
+
+  /**
+   * width 属性
+   *
+   * @return {number}
+   */
+  get width() {
+    return this._width
+  }
+  set width(value) {
+    this._width = Math.max(value, 0)
+  }
+
+  /**
+   * height 属性
+   * @return {number}
+   */
+  get height() {
+    return this._height
+  }
+  set height(value) {
+    this._height = Math.max(value, 0)
+  }
+
+  /**
+   * 判断矩形空间是否包含点x,y
+   *
+   * @param   x       横坐标(当前item)
+   * @param   y       纵坐标(当前item)
+   * @return  boolean
+   */
+  contains(x, y) {
+    return (x >= this.x && x <= this.right) && (y >= this.top && y <= this.bottom)
+  } // Function contains()
+
+  /**
+   * 调整Rect位置
+   *
+   * @param x
+   * @param y
+   * @return  SRect
+   */
+  adjusted(x, y = null) {
+    if (typeof (x) === 'object') {     // 如果传入的是SPoint对象
+      return new SRect(this.x + x.x, this.y + x.y, this.width, this.height)
+    }
+
+    return new SRect(this.x + x, this.y + y, this.width, this.height)
+  } // Function adjusted()
+
+  /**
+   * 合并rect
+   *
+   * @param   rect
+   */
+  union(rect) {
+    this.left = Math.min(this.left, rect.left)
+    this.top = Math.min(this.top, rect.top)
+    this.right = Math.max(this.right, rect.right)
+    this.bottom = Math.max(this.bottom, rect.bottom)
+  } // Function union()
+} // Class SRect

+ 13 - 0
src/assets/graphy/index.js

@@ -0,0 +1,13 @@
+import SGraphyView from './SGraphy/SGraphyView'
+import SGraphyScene from './SGraphy/SGraphyScene'
+import SGraphyClockItem from './SGraphy/items/SGraphyClockItem'
+import SGraphyRectItem from './SGraphy/items/SGraphyRectItem'
+import SGraphyLineItem from './SGraphy/items/SGraphyLineItem'
+import SGraphyPolygonItem from './SGraphy/items/SGraphyPolygonItem'
+import SGraphyCircleItem from './SGraphy/items/SGraphyCircleItem'
+import SGraphyImageItem from './SGraphy/items/SGraphyImageItem'
+import SGraphyPillarItems from './SGraphy/items/SGraphyPillarItems'
+import SGraphyTextItems from './SGraphy/items/SGraphyTextItems'
+import SGraphyVirtualItem from './SGraphy/items/SGraphyVirtualItem'
+
+export { SGraphyView, SGraphyScene, SGraphyClockItem, SGraphyRectItem, SGraphyLineItem, SGraphyPolygonItem, SGraphyCircleItem, SGraphyImageItem, SGraphyPillarItems, SGraphyTextItems, SGraphyVirtualItem }

Fichier diff supprimé car celui-ci est trop grand
+ 1378 - 0
src/assets/js/chosen.jquery.min.js


+ 327 - 0
src/assets/js/handsontable-chosen-editor.js

@@ -0,0 +1,327 @@
+/// chosen plugin
+import Handsontable from "handsontable-pro"
+import 'handsontable-pro/dist/handsontable.full.css'
+import zhCN from 'handsontable-pro/languages/zh-CN';
+(function(Handsontable) {
+    "use strict";
+
+    var ChosenEditor = Handsontable.editors.TextEditor.prototype.extend();
+
+    ChosenEditor.prototype.prepare = function(row, col, prop, td, originalValue, cellProperties) {
+
+        Handsontable.editors.TextEditor.prototype.prepare.apply(this, arguments);
+
+        this.options = {};
+
+        if (this.cellProperties.chosenOptions) {
+            this.options = $.extend(this.options, cellProperties.chosenOptions);
+        }
+
+        cellProperties.chosenOptions = $.extend({}, cellProperties.chosenOptions);
+    };
+
+    ChosenEditor.prototype.createElements = function() {
+        this.$body = $(document.body);
+
+        this.TEXTAREA = document.createElement('select');
+        //this.TEXTAREA.setAttribute('type', 'text');
+        this.$textarea = $(this.TEXTAREA);
+
+        Handsontable.dom.addClass(this.TEXTAREA, 'handsontableInput');
+
+        this.textareaStyle = this.TEXTAREA.style;
+        this.textareaStyle.width = 0;
+        this.textareaStyle.height = 0;
+
+        this.TEXTAREA_PARENT = document.createElement('DIV');
+        Handsontable.dom.addClass(this.TEXTAREA_PARENT, 'handsontableInputHolder');
+
+        this.textareaParentStyle = this.TEXTAREA_PARENT.style;
+        this.textareaParentStyle.top = 0;
+        this.textareaParentStyle.left = 0;
+        this.textareaParentStyle.display = 'none';
+        this.textareaParentStyle.width = "200px";
+
+        this.TEXTAREA_PARENT.appendChild(this.TEXTAREA);
+
+        this.instance.rootElement.appendChild(this.TEXTAREA_PARENT);
+
+        var that = this;
+        this.instance._registerTimeout(setTimeout(function() {
+            that.refreshDimensions();
+        }, 0));
+    };
+
+    var onChosenChanged = function() {
+        var options = this.cellProperties.chosenOptions;
+
+        if (!options.multiple) {
+            this.close();
+            this.finishEditing();
+        }
+    };
+    var onChosenClosed = function() {
+        var options = this.cellProperties.chosenOptions;
+
+        if (!options.multiple) {
+            this.close();
+            this.finishEditing();
+        } else {}
+    };
+    var onBeforeKeyDown = function(event) {
+        var instance = this;
+        var that = instance.getActiveEditor();
+
+        var keyCodes = Handsontable.helper.KEY_CODES;
+        var ctrlDown = (event.ctrlKey || event.metaKey) && !event.altKey; //catch CTRL but not right ALT (which in some systems triggers ALT+CTRL)
+
+        //Process only events that have been fired in the editor
+        if (event.target.tagName !== "INPUT") {
+            return;
+        }
+        if (event.keyCode === 17 || event.keyCode === 224 || event.keyCode === 91 || event.keyCode === 93) {
+            //when CTRL or its equivalent is pressed and cell is edited, don't prepare selectable text in textarea
+            event.stopImmediatePropagation();
+            return;
+        }
+
+        var target = event.target;
+
+        switch (event.keyCode) {
+            case keyCodes.ARROW_RIGHT:
+                if (Handsontable.dom.getCaretPosition(target) !== target.value.length) {
+                    event.stopImmediatePropagation();
+                } else {
+                    that.$textarea.trigger("chosen:close");
+                }
+                break;
+
+            case keyCodes.ARROW_LEFT:
+                if (Handsontable.dom.getCaretPosition(target) !== 0) {
+                    event.stopImmediatePropagation();
+                } else {
+                    that.$textarea.trigger("chosen:close");
+                }
+                break;
+
+            case keyCodes.ENTER:
+                if (that.cellProperties.chosenOptions.multiple) {
+                    event.stopImmediatePropagation();
+                    event.preventDefault();
+                    event.stopPropagation();
+                }
+
+                break;
+
+            case keyCodes.A:
+            case keyCodes.X:
+            case keyCodes.C:
+            case keyCodes.V:
+                if (ctrlDown) {
+                    event.stopImmediatePropagation(); //CTRL+A, CTRL+C, CTRL+V, CTRL+X should only work locally when cell is edited (not in table context)
+                }
+                break;
+
+            case keyCodes.BACKSPACE:
+                var txt = $(that.TEXTAREA_PARENT).find("input").val();
+                $(that.TEXTAREA_PARENT).find("input").val(txt.substr(0, txt.length - 1)).trigger("keyup.chosen");
+
+                event.stopImmediatePropagation();
+                break;
+            case keyCodes.DELETE:
+            case keyCodes.HOME:
+            case keyCodes.END:
+                event.stopImmediatePropagation(); //backspace, delete, home, end should only work locally when cell is edited (not in table context)
+                break;
+        }
+
+    };
+
+    ChosenEditor.prototype.open = function(keyboardEvent) {
+        this.refreshDimensions();
+        this.textareaParentStyle.display = 'block';
+        this.instance.addHook('beforeKeyDown', onBeforeKeyDown);
+
+        this.$textarea.css({
+            height: $(this.TD).height() + 4,
+            'min-width': $(this.TD).outerWidth() - 4
+        });
+
+        //display the list
+        this.$textarea.hide();
+
+        //make sure that list positions matches cell position
+        //this.$textarea.offset($(this.TD).offset());
+
+        var options = $.extend({}, this.options, {
+            width: "100%",
+            search_contains: true
+        });
+
+        if (options.multiple) {
+            this.$textarea.attr("multiple", true);
+        } else {
+            this.$textarea.attr("multiple", false);
+        }
+
+        this.$textarea.empty();
+        this.$textarea.append("<option value=''></option>");
+        var el = null;
+        var originalValue = (this.originalValue + "").split(",");
+        if (options.data && options.data.length) {
+            for (var i = 0; i < options.data.length; i++) {
+                // el = $("<option />");
+                // el.attr("value", options.data[i].Code);
+                // el.html(options.data[i].Name);
+
+                // if (originalValue.indexOf(options.data[i].Code + "") > -1) {
+                //     el.attr("selected", true);
+                // }
+
+                // this.$textarea.append(el);
+                if (options.data[i].content && options.data[i].content.length) {
+                    for (var k = 0; k < options.data[i].content.length; k++) {
+                        if (options.data[i].content[k] && options.data[i].content[k].length) {
+                            for (var j = 0; j < options.data[i].content[k].length; j++) {
+                                el = $("<option />");
+                                el.attr("value", options.data[i].content[k].content[j].Code);
+                                el.html(options.data[i].content[k].content[j].Name);
+
+                                if (originalValue.indexOf(options.data[i].content[k].content[j].Code + "") > -1) {
+                                    el.attr("selected", true);
+                                }
+                                this.$textarea.append(el);
+                            }
+                        } else {
+                            el = $("<option />");
+                            el.attr("value", options.data[i].content[k].Code);
+                            el.html(options.data[i].content[k].Name);
+
+                            if (originalValue.indexOf(options.data[i].content[k].Code + "") > -1) {
+                                el.attr("selected", true);
+                            }
+                            this.$textarea.append(el);
+                        }
+                    }
+                } else {
+                    el = $("<option />");
+                    el.attr("value", options.data[i].Code);
+                    el.html(options.data[i].Name);
+
+                    if (originalValue.indexOf(options.data[i].Code + "") > -1) {
+                        el.attr("selected", true);
+                    }
+                    this.$textarea.append(el);
+                }
+            }
+        }
+
+        if ($(this.TEXTAREA_PARENT).find(".chosen-container").length) {
+            this.$textarea.chosen("destroy");
+        }
+
+        this.$textarea.chosen(options);
+
+        var self = this;
+        setTimeout(function() {
+
+            self.$textarea.on('change', onChosenChanged.bind(self));
+            self.$textarea.on('chosen:hiding_dropdown', onChosenClosed.bind(self));
+
+            self.$textarea.trigger("chosen:open");
+
+            $(self.TEXTAREA_PARENT).find("input").on("keydown", function(e) {
+                if (e.keyCode === Handsontable.helper.KEY_CODES.ENTER /*|| e.keyCode === Handsontable.helper.KEY_CODES.BACKSPACE*/ ) {
+                    if ($(this).val()) {
+                        e.preventDefault();
+                        e.stopPropagation();
+                    } else {
+                        e.preventDefault();
+                        e.stopPropagation();
+
+                        self.close();
+                        self.finishEditing();
+                    }
+
+                }
+
+                if (e.keyCode === Handsontable.helper.KEY_CODES.BACKSPACE) {
+                    var txt = $(self.TEXTAREA_PARENT).find("input").val();
+
+                    $(self.TEXTAREA_PARENT).find("input").val(txt.substr(0, txt.length - 1)).trigger("keyup.chosen");
+
+                    e.preventDefault();
+                    e.stopPropagation();
+                }
+
+                if (e.keyCode === Handsontable.helper.KEY_CODES.ARROW_DOWN || e.keyCode === Handsontable.helper.KEY_CODES.ARROW_UP) {
+                    e.preventDefault();
+                    e.stopPropagation();
+                }
+
+            });
+
+            setTimeout(function() {
+                self.$textarea.trigger("chosen:activate").focus();
+
+                if (keyboardEvent && keyboardEvent.keyCode && keyboardEvent.keyCode != 113) {
+                    var key = keyboardEvent.keyCode;
+                    var keyText = (String.fromCharCode((96 <= key && key <= 105) ? key - 48 : key)).toLowerCase();
+
+                    $(self.TEXTAREA_PARENT).find("input").val(keyText).trigger("keyup.chosen");
+                    self.$textarea.trigger("chosen:activate");
+                }
+            }, 1);
+        }, 1);
+
+    };
+
+    ChosenEditor.prototype.init = function() {
+        Handsontable.editors.TextEditor.prototype.init.apply(this, arguments);
+    };
+
+    ChosenEditor.prototype.close = function() {
+        this.instance.listen();
+        this.instance.removeHook('beforeKeyDown', onBeforeKeyDown);
+        this.$textarea.off();
+        this.$textarea.hide();
+        Handsontable.editors.TextEditor.prototype.close.apply(this, arguments);
+    };
+
+    ChosenEditor.prototype.getValue = function() {
+        if (!this.$textarea.val()) {
+            return "";
+        }
+        if (typeof this.$textarea.val() === "object") {
+            return this.$textarea.val().join(",");
+        }
+        return this.$textarea.val();
+    };
+
+
+    ChosenEditor.prototype.focus = function() {
+        this.instance.listen();
+
+        // DO NOT CALL THE BASE TEXTEDITOR FOCUS METHOD HERE, IT CAN MAKE THIS EDITOR BEHAVE POORLY AND HAS NO PURPOSE WITHIN THE CONTEXT OF THIS EDITOR
+        //Handsontable.editors.TextEditor.prototype.focus.apply(this, arguments);
+    };
+
+    ChosenEditor.prototype.beginEditing = function(initialValue) {
+        var onBeginEditing = this.instance.getSettings().onBeginEditing;
+        if (onBeginEditing && onBeginEditing() === false) {
+            return;
+        }
+
+        Handsontable.editors.TextEditor.prototype.beginEditing.apply(this, arguments);
+
+    };
+
+    ChosenEditor.prototype.finishEditing = function(isCancelled, ctrlDown) {
+        this.instance.listen();
+        return Handsontable.editors.TextEditor.prototype.finishEditing.apply(this, arguments);
+    };
+
+    Handsontable.editors.ChosenEditor = ChosenEditor;
+    Handsontable.editors.registerEditor('chosen', ChosenEditor);
+
+})(Handsontable);

+ 70 - 0
src/assets/js/items/SGraphyCircleItem.js

@@ -0,0 +1,70 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+export default class SGraphyCircleItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param X  圆中心点X
+     * @param Y  圆中心点Y
+     * @param Radius   圆的半径
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     */
+    constructor(X, Y, color, Radius, isSolid, name, parent = null) {
+        super(parent)
+        this.X = X
+        this.Y = Y
+        this.color = color
+        this.Radius = Radius
+        this.isSolid = isSolid
+        this.minX = this.X - this.Radius
+        this.minY = this.Y - this.Radius
+        this.maxX = this.X + this.Radius
+        this.maxY = this.Y + this.Radius
+        this.sAngle = null || 0
+        this.eAngle = null || 2 * Math.PI
+        this.name = name
+        this.type = 6
+        this.lineWidth = null
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        canvas.lineWidth = this.lineWidth || 240
+        canvas.strokeStyle = this.color || '#000'
+        canvas.fillStyle = this.color || '#000'
+        canvas.beginPath();
+        canvas.arc(this.X, this.Y, this.Radius, this.sAngle, this.eAngle);
+        if (!!this.isSolid) {
+            canvas.fillStyle = this.color; //填充颜色,默认是黑色
+            canvas.fill(); //画实心圆
+        }
+        canvas.stroke()
+        if (!!this.name) {
+            canvas.font = "oblique small-caps bold " + this.lineWidth * 10 + "px Arial";
+            // canvas.font = "oblique small-caps bold " + 10 + "px Arial";
+            canvas.fillStyle = 'green'
+            canvas.fillText(this.name, this.X, this.Y);
+        }
+    }
+}

+ 59 - 0
src/assets/js/items/SGraphyImageItem.js

@@ -0,0 +1,59 @@
+/**
+ * 图片
+ * 
+ * 
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect'
+export default class SGraphyImageItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param width  图片宽度
+     * @param height  图片高度
+     * @param url    图片url
+     * @param id     point的Id
+     * @param X      图片向x轴的偏移量
+     * @param Y      图片向y轴的偏移量
+     * @param downUrl 图片按下时的url
+     * @param parent  指向父元素
+     */
+    constructor(width, height, url, id, X, Y, downUrl, parent = null) {
+            super(parent)
+            this.width = width
+            this.height = height
+            this.url = url
+            this.id = id
+            this.X = X
+            this.Y = Y
+            this.downUrl = downUrl
+            this.imgFalg = false
+            this.img = new Image()
+            this.img.src = this.url
+            this.img.style.width = this.width
+            this.img.style.height = this.height
+            this.canMove = true
+            this.type = 1
+        } //constructor
+
+    /**
+     * Item对象边界区域
+     *
+     * @return  SRect
+     */
+    boundingRect() {
+            return new SRect(0, 0, this.width * 100, this.height * 100)
+        } // Function boundingRect()
+
+    /**
+     * 绘制图片
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        // canvas.moveTo(this.X, this.Y)
+        // canvas.drawImage(this.img, 0, 0, this.width, this.height, this.X, this.Y, this.width * 100, this.height * 100)
+        canvas.drawImage(this.img, 0, 0, this.width, this.height, 0, 0, this.width * 100, this.height * 100)
+    }
+}

+ 66 - 0
src/assets/js/items/SGraphyLineItem.js

@@ -0,0 +1,66 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+export default class SGraphyLineItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param startX  线的起始x坐标
+     * @param startY  线的起始y坐标
+     * @param endX    线的终止x坐标
+     * @param endY    线的终止y坐标
+     * @param width   线的宽度
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     */
+    constructor(startX, startY, endX, endY, color, width, isVirtual, parent = null) {
+        super(parent)
+        this.startX = startX
+        this.startY = startY
+        this.endX = endX
+        this.endY = endY
+        this.color = color
+        this.width = width
+        this.isVirtual = isVirtual
+        this.minX = Math.min(this.startX, this.endX)
+        this.minY = Math.min(this.startY, this.endY)
+        this.maxX = Math.max(this.startX, this.endX)
+        this.maxY = Math.max(this.startY, this.endY)
+        this.name = null
+        this.type = 2
+        this.lineWidth = null
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.maxX - this.minX, this.maxY - this.minY)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        canvas.lineWidth = this.lineWidth || this.width || 240
+        canvas.strokeStyle = this.color || '#000'
+        if (this.isVirtual) {
+            canvas.setLineDash([100, 80])
+            canvas.strokeStyle = 'green'
+        }
+        canvas.beginPath();
+        canvas.moveTo(this.startX, this.startY)
+        canvas.lineTo(this.endX, this.endY)
+        canvas.stroke()
+    }
+}

+ 76 - 0
src/assets/js/items/SGraphyPillarItems.js

@@ -0,0 +1,76 @@
+/**
+ * 不规则多边形,元空间
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+function getItem(arr, name) {
+    if (arr && arr.length) {
+        return arr.map(item => {
+            return item[name]
+        })
+    } else {
+        return [0]
+    }
+}
+
+
+export default class SGraphyPolygonItem extends SGraphyItem {
+    /**
+     * 
+     * 构造函数
+     * 
+     * @param jsonArr  空间线条数组
+     * @param lineWidth    空间线条的宽度
+     * @param color    空间线条的颜色
+     * @param fillColor  空间的填充颜色
+     * 
+     */
+    constructor(jsonArr, lineWidth, color, fillColor, parent = null) {
+            super(parent)
+            this.jsonArr = jsonArr
+            this.lineWidth = lineWidth
+            this.color = color
+            this.fillColor = fillColor
+            let xArr = getItem(this.jsonArr, 'X')
+            let yArr = getItem(this.jsonArr, 'Y')
+            this.minX = Math.min.apply(null, xArr) || 0
+            this.minY = Math.min.apply(null, yArr) || 0
+            this.width = Math.max.apply(null, xArr) - this.minX || 0
+            this.height = Math.max.apply(null, yArr) - this.minY || 0
+            this.type = 5
+        } //constructor
+
+    /**
+     * Item的边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.width, this.height)
+    }
+
+    /**
+     * 绘制不规则多边形
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        if (this.jsonArr && this.jsonArr.length) {
+            canvas.beginPath();
+            canvas.lineWidth = 240
+            canvas.lineCap = 'butt';
+            canvas.strokeStyle = this.color || '#000'
+            canvas.fillStyle = this.fillColor || '#fff'
+            canvas.moveTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            for (let i = 1; i < this.jsonArr.length; i++) {
+                canvas.lineTo(this.jsonArr[i].X, this.jsonArr[i].Y)
+            }
+            canvas.lineTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            canvas.closePath()
+            canvas.fill()
+            canvas.stroke()
+        }
+    }
+}

+ 257 - 0
src/assets/js/items/SGraphyPolygonItem.js

@@ -0,0 +1,257 @@
+/**
+ * 不规则多边形,元空间
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+function getItem(arr, name) {
+    if (arr && arr.length) {
+        return arr.map(item => {
+            return item[name]
+        })
+    } else {
+        return [0]
+    }
+}
+
+function changeArr(arr) {
+    if (arr && arr.length) {
+        return arr.map(item => {
+            return [
+                item.X, item.Y
+            ]
+        })
+    } else {
+        return [0]
+    }
+}
+/**
+ * 求不规则多边形重点
+ * @param {points} 数组,多个点位坐标
+ *  
+ * @return {x, y} 重点点位坐标
+ */
+// function getCenterOfGravityPoint(points) {
+//     let area = 0,
+//         gx = 0,
+//         gy = 0,
+//         i = 1,
+//         px1, px2, py1, py2, temp, length = points.length;
+//     for (i; i <= length; i++) {
+//         px1 = points[(i % length)].X;
+//         px2 = points[(i - 1)].X;
+//         py1 = points[(i % length)].Y;
+//         py2 = points[(i - 1)].Y;
+//         temp = (px1 * py2 - py1 * px2) / 2
+//         area += temp;
+//         gx += temp * (px1 + px2) / 3
+//         gy += temp * (py1 + py2) / 3
+//     }
+
+//     gx = gx / area
+//     gy = gy / area
+
+//     return {
+//         x: gx,
+//         y: gy
+//     }
+// }
+
+
+export default class SGraphyPolygonItem extends SGraphyItem {
+    /**
+     * 
+     * 构造函数
+     * 
+     * @param jsonArr  空间线条数组
+     * @param lineWidth    空间线条的宽度
+     * @param color    空间线条的颜色
+     * @param fillColor  空间的填充颜色
+     * 
+     */
+    constructor(jsonArr, lineWidth, color, fillColor, id, centerOfGravityPoint, name, paths, faceColor, businessColor, isBusiness, parent = null) {
+            super(parent)
+            this.jsonArr = jsonArr
+            this.lineWidth = lineWidth
+            this.color = color
+            this.id = id
+            this.name = name //实际渲染名字
+            this.fillColor = fillColor
+            let xArr = getItem(this.jsonArr, 'X')
+            let yArr = getItem(this.jsonArr, 'Y')
+            this.minX = Math.min.apply(null, xArr) || 0
+            this.maxX = Math.max.apply(null, xArr) || 0
+            this.maxY = Math.max.apply(null, yArr) || 0
+            this.minY = Math.min.apply(null, yArr) || 0
+            this.width = this.maxX - this.minX || 0
+            this.height = this.maxY - this.minY || 0
+            this.type = 3
+            this.businessName = null
+            this.faceColor = faceColor || '#cacaca' //颜色
+            this.businessId = null //业务空间id
+            this.isBusiness = isBusiness || 1 //状态
+            this.businessColor = businessColor || 'rgba(68,161,140,.4)' //业务空间颜色
+            this.businessFaceColor = "#333"
+            this.containsArr = changeArr(this.jsonArr)
+            this.paths = null
+            if (paths && paths.length > 1) {
+                this.paths = paths.map(item => {
+                    if (item && item.length) {
+                        return changeArr(item)
+                    } else {
+                        return undefined
+                    }
+                }).filter(d => d)
+            }
+            this.centerOfGravityPoint = centerOfGravityPoint
+        } //constructor
+
+    /**
+     * Item的边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, this.width, this.height)
+    }
+
+
+    /**
+     * 判断item是否包含点x,y
+     *
+     * @param   x       横坐标(当前item)
+     * @param   y       纵坐标(当前item)
+     *
+     * @return  boolean
+     */
+    contains(x, y) {
+        let falg = false,
+            isFullIn = false //是否在镂空图形内
+        if (this.paths instanceof Array) {
+            for (let i = 1; i < this.paths.length; i++) {
+                if (this.isIn(x, y, this.paths[i])) {
+                    //位置信息在镂空图形内
+                    isFullIn = true
+                    break
+                }
+            }
+            // //如果鼠标在大图形内切在镂空图形中返回false
+            if (this.isIn(x, y, this.containsArr) && isFullIn) {
+                falg = false
+            } else if (this.isIn(x, y, this.containsArr) && !isFullIn) {
+                falg = true
+            } else {
+                falg = this.isIn(x, y, this.containsArr)
+            }
+        } else {
+            falg = this.isIn(x, y, this.containsArr)
+        }
+        return falg
+    }
+
+
+    isIn(x, y, json) {
+        let nCross = 0,
+            point = typeof(x) == 'object' ? [x.x, x.y] : [x, y],
+            APoints = json,
+            length = APoints.length,
+            p1, p2, i, xinters;
+        p1 = APoints[0];
+        for (i = 1; i <= length; i++) {
+            p2 = APoints[i % length];
+            if (
+                point[0] > Math.min(p1[0], p2[0]) &&
+                point[0] <= Math.max(p1[0], p2[0])
+            ) {
+                if (point[1] <= Math.max(p1[1], p2[1])) {
+                    if (p1[0] != p2[0]) {
+                        //计算位置信息
+                        xinters = (point[0] - p1[0]) * (p2[1] - p1[1]) / (p2[0] - p1[0]) + p1[1];
+                        if (p1[1] == p2[1] || point[1] <= xinters) {
+                            nCross++
+                        }
+                    }
+                }
+            }
+            p1 = p2;
+        }
+        if (nCross % 2 == 0) {
+            return false
+        } else {
+            return true
+        }
+
+    }
+
+    /**
+     * 绘制不规则多边形
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        if (this.jsonArr && this.jsonArr.length) {
+            canvas.beginPath();
+            canvas.lineWidth = 220
+            canvas.lineCap = 'butt';
+            if (this.isBusiness == 1) {
+                canvas.strokeStyle = this.color || '#000'
+                canvas.fillStyle = this.fillColor
+            } else if (this.isBusiness == 2) {
+                //已有id 的业务空间
+                canvas.strokeStyle = this.color || '#000'
+                canvas.fillStyle = this.businessColor || '#fff'
+            } else if (this.isBusiness == 3) {
+                //被选择的元空间
+                canvas.strokeStyle = this.color || '#000'
+                canvas.lineWidth = 800
+                canvas.fillStyle = '#1abc9c'
+            } else if (this.isBusiness == 4) {
+                canvas.strokeStyle = 'rgba(251,226,1,.8)' || '#000'
+                canvas.fillStyle = '#fff' || '#fff'
+            } else if (this.isBusiness == 5) {
+                canvas.fillStyle = 'rgba(11,12,12,.2)' || '#fff'
+            } else if (this.isBusiness == 6) {
+                canvas.fillStyle = '#1abc9c'
+                canvas.lineWidth = 800
+                canvas.strokeStyle = 'rgba(68,161,140,.4)' || '#fff'
+            } else if (this.isBusiness == 7) {
+                canvas.strokeStyle = this.color || '#000'
+                canvas.fillStyle = this.businessColor || '#fff'
+            }
+            canvas.moveTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            for (let i = 1; i < this.jsonArr.length; i++) {
+                canvas.lineTo(this.jsonArr[i].X, this.jsonArr[i].Y)
+            }
+            canvas.lineTo(this.jsonArr[0].X, this.jsonArr[0].Y)
+            canvas.closePath()
+            canvas.fill()
+            canvas.stroke()
+            if (!!this.name) {
+                canvas.font = "normal small-caps bold 500px Arial";
+                if (this.isBusiness == 1) {
+                    canvas.fillStyle = this.faceColor
+                } else if (this.isBusiness == 2) {
+                    canvas.fillStyle = this.businessFaceColor;
+                } else if (this.isBusiness == 3) {
+                    //业务空间异常状态
+                    canvas.fillStyle = '#fff'
+                } else if (this.isBusiness == 4) {
+                    canvas.fillStyle = '#cacaca'
+                } else if (this.isBusiness == 6) {
+                    canvas.fillStyle = '#fff'
+                } else if (this.isBusiness == 7) {
+                    canvas.fillStyle = 'red'
+                }
+                if (!!this.businessName || !!this.businessId) {
+                    name = '👇   ' + this.businessName
+                } else {
+                    name = '⬇️   ' + this.name
+                }
+                canvas.fillText(name, this.centerOfGravityPoint.x, this.centerOfGravityPoint.y);
+                // canvas.fillText(this.name, (this.maxX - this.minX) / 2 + this.minX, (this.maxY - this.minY) / 2 + this.minY);
+            }
+            // canvas.fillText(this.name, this.jsonArr[0].X, this.jsonArr[0].Y);
+        }
+    }
+}

+ 100 - 0
src/assets/js/items/SGraphyRectItem.js

@@ -0,0 +1,100 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+export default class SGraphyRectItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param X  矩形的开始点X
+     * @param Y  矩形的开始点Y
+     * @param width   矩形的宽度
+     * @param height  矩形的高度
+     * 
+     * @param isFill  矩形的是否填充
+     * @param fillColor  矩形的填充色彩
+     * @param text  矩形的文字
+     * @param textSize  矩形的文字大小
+     * @param color  矩形的颜色
+     * @param Tip   提示
+     */
+    constructor(X, Y, width, height, isFill, fillColor, text, textSize, color, Tip, parent = null) {
+        super(parent)
+        this.X = X
+        this.Y = Y
+        this.width = width
+        this.height = height
+        this.isFill = isFill
+        this.fillColor = fillColor
+        this.color = color
+        this.textSize = textSize || 6
+        this.type = 10
+        this.hoverColor = null
+        this.text = text.split(",")
+        this.fontStart = this.X
+        this.Tip = Tip
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.X, this.Y, this.width, this.height)
+    }
+
+    /**
+     * 判断item是否包含点x,y
+     *
+     * @param   x       横坐标(当前item)
+     * @param   y       纵坐标(当前item)
+     *
+     * @return  boolean
+     */
+    contains(x) {
+        if (this.X + this.width > x.x && x.x > this.X && this.Y + this.height > x.y && x.y > this.Y) {
+            return true
+        } else {
+            return false
+        }
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        canvas.beginPath();
+        canvas.lineWidth = "1";
+        if (this.isFill) {
+            if (!!this.hoverColor) {
+                canvas.fillStyle = this.hoverColor
+            } else {
+                canvas.fillStyle = this.fillColor
+            }
+            canvas.fillRect(this.X, this.Y, this.width, this.height);
+        } else {
+            canvas.rect(this.X, this.Y, this.width, this.height);
+        }
+        canvas.stroke();
+        if (!!this.text && this.text.length <= 1) {
+            canvas.font = this.textSize + "px 宋体";
+            canvas.fillStyle = this.color
+            canvas.fillText(this.text[0], this.fontStart, this.Y + this.height / 2);
+        } else if (!!this.text && this.text.length > 1) {
+            canvas.font = this.textSize + "px 宋体";
+            canvas.fillStyle = this.color
+            for (let i = 0; i < this.text.length; i++) {
+                canvas.fillText(this.text[i], this.fontStart, this.Y + this.height / 2 - this.textSize * i);
+            }
+        } else {
+            return
+        }
+    }
+}

+ 51 - 0
src/assets/js/items/SGraphyTextItems.js

@@ -0,0 +1,51 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+export default class SGraphyTextItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param X  文字的开始点X
+     * @param Y  文字的开始点Y
+     * @param width   文字的宽度
+     * 
+     * @param color  文字的颜色
+     * @param text   文字的文字
+     */
+    constructor(X, Y, width, color, text, falg, font, parent = null) {
+        super(parent)
+        this.X = X
+        this.Y = Y
+        this.lineWidth = width
+        this.color = color
+        this.font = font ? font : "6px 宋体"
+        this.text = falg ? text + '→' : text
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.X, this.Y, 0, 0)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        if (!!this.text) {
+            canvas.font = this.font;
+            canvas.fillStyle = this.color
+            canvas.fillText(this.text, this.X, this.Y);
+        }
+    }
+}

+ 66 - 0
src/assets/js/items/SGraphyVirtualItem.js

@@ -0,0 +1,66 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../../node-templete/SGraphy/SGraphyItem'
+import SRect from '../../node-templete/SGraphy/types/SRect';
+
+export default class SGraphyLineItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param startX  线的起始x坐标
+     * @param startY  线的起始y坐标
+     * @param endX    线的终止x坐标
+     * @param endY    线的终止y坐标
+     * @param width   线的宽度
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     * 
+     * @param canMove  移动
+     */
+    constructor(startX, startY, endX, endY, color, width, isVirtual, canMove, parent = null) {
+        super(parent)
+        this.startX = startX
+        this.startY = startY
+        this.endX = endX
+        this.endY = endY
+        this.color = color
+        this.width = width
+        this.isVirtual = isVirtual
+        this.minX = Math.min(this.startX, this.endX)
+        this.minY = Math.min(this.startY, this.endY)
+        this.maxX = Math.max(this.startX, this.endX)
+        this.maxY = Math.max(this.startY, this.endY)
+        this.type = 4
+            // this.canMove = true
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(this.minX, this.minY, (this.maxX - this.minX), (this.maxY - this.minY))
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        if (this.isVirtual) {
+            canvas.setLineDash([240, 240])
+        }
+        canvas.lineWidth = 240
+        canvas.strokeStyle = this.color || '#000'
+        canvas.beginPath();
+        canvas.moveTo(this.startX, this.startY)
+        canvas.lineTo(this.endX, this.endY)
+        canvas.stroke()
+    }
+}

Fichier diff supprimé car celui-ci est trop grand
+ 2934 - 0
src/assets/js/jsmind.js


BIN
src/assets/logo.png


+ 44 - 0
src/assets/node-templete/SGraphy/SCanvas.js

@@ -0,0 +1,44 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * Canvas功能扩展
+ *
+ * @author  Andy
+ */
+Object.assign(CanvasRenderingContext2D.prototype, {
+  /**
+   * 绘制线段
+   *
+   * @param   x0    起点x坐标
+   * @param   y0    起点y坐标
+   * @param   x1    终点x坐标
+   * @param   y1    终点y坐标
+   */
+  drawLine (x0, y0, x1, y1) {
+    this.beginPath()
+    this.moveTo(x0, y0)
+    this.lineTo(x1, y1)
+    this.stroke()
+  } // Function drawLine()
+}) // Class CanvasRenderingContext2D

+ 30 - 0
src/assets/node-templete/SGraphy/SGraphyEnums.js

@@ -0,0 +1,30 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+const SGraphyItemFlag = {
+  ItemIsMovable: 1,
+  ItemIsSelectable: 2
+}
+
+if (Object.freeze)
+  Object.freeze(SGraphyItemFlag)

+ 396 - 0
src/assets/node-templete/SGraphy/SGraphyItem.js

@@ -0,0 +1,396 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+import SPoint from './types/SPoint'
+import SRect from './types/SRect'
+import SMouseEvent from './SMouseEvent'
+
+const sortItemZOrder = Symbol('sortItemZOrder')
+const toChildMouseEvent = Symbol('toChildMouseEvent')
+const grabItem = Symbol('grabItem')
+const releaseItem = Symbol('releaseItem')
+
+/**
+ * SGraphyItem
+ *
+ * @author  Andy
+ */
+export default class SGraphyItem {
+    /**
+     * 构造函数
+     */
+    constructor(parent) {
+            this.name = 'item'
+            this._scene = null
+            this._parent = parent
+            this.children = []
+
+            /** Z轴顺序 */
+            this.zOrder = 0
+                /** item位置 */
+            this._pos = new SPoint(0, 0)
+                /** 缩放比例 */
+            this._scale = new SPoint(1, 1)
+                /** 是否可见 */
+            this._isVisible = true
+
+            /** 鼠标按下时位置 */
+            this._mouseDownPos = new SPoint(4, 21)
+            this._isMove = false
+            this.canMove = false
+        } // Function constructor()
+
+    // ===================================================================================================================
+    // 属性
+    /** parent属性存值函数 */
+    get parent() {
+        return this._parent
+    }
+    set parent(value) {
+            if (this._parent === value) {
+                return
+            }
+            if (this._parent != null) { // 如果原parent不为空
+                // 将节点从原parent节点中摘除
+                let i = this._parent.children.indexOf(this)
+                this._parent.children.splice(i, 1)
+            }
+            this._parent = value
+
+            if (this._parent != null) { // 如果新parent不为空
+                // 将节点加入到新parent节点中
+                this._parent.children.push(this)
+                this._parent.children.sort(this[sortItemZOrder])
+            }
+            this._parent = value
+        } // Function set Parent()
+
+    /** scene属性 */
+    get scene() {
+        if (this._parent != null) {
+            return this._parent.scene
+        } else {
+            return this._scene
+        }
+    }
+
+    /** pos属性 */
+    get pos() {
+        return this._pos
+    }
+    set pos(value) {
+        this._pos = value
+    }
+
+    /** scale属性 */
+    get scale() {
+        return this._scale
+    }
+    set scale(value) {
+        this._scale = value
+    }
+
+    /** visible属性 */
+    get visible() {
+        return this._isVisible
+    }
+    set visible(value) {
+        this._isVisible = value
+    }
+
+    // ===================================================================================================================
+    // 函数
+    /**
+     * Item对象边界区域
+     *
+     * @return  SRect
+     */
+    boundingRect() {
+            return new SRect(0, 0, 10, 10)
+        } // Function boundingRect()
+
+    /**
+     * Item绘制操作
+     *
+     * @param   canvas        画布对象
+     * @param   rect          绘制区域
+     */
+    onDraw(canvas, rect) {
+            for (let item of this.children) {
+                // 保存画布状态
+                canvas.save()
+                    // item位移到指定位置绘制
+                canvas.translate(item.pos.x, item.pos.y)
+                    // 设置绘制区域
+                    // canvas.clip(item.boundingRect())
+                    // 绘制item
+                item.onDraw(canvas, rect)
+                    // 恢复画布状态
+                canvas.restore()
+            }
+        } // Function onDraw()
+
+    /**
+     * 更新Item
+     */
+    update() {
+            // TODO: PLX
+            // scene?.update()
+        } // Function update()
+
+    /**
+     * 移动item到指定位置
+     *
+     * @param   x           新位置的x坐标
+     * @param   y           新位置的y坐标
+     */
+    moveTo(x, y) {
+            this.pos = new SPoint(x, y)
+        } // moveTo()
+
+    /**
+     * 判断item是否包含点x,y
+     *
+     * @param   x       横坐标(当前item)
+     * @param   y       纵坐标(当前item)
+     *
+     * @return  boolean
+     */
+    contains(x, y) {
+            return this.boundingRect().contains(x - this.pos.x, y - this.pos.y)
+        } // Function contains()
+
+    /**
+     * 获得item的路径节点列表。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
+     *
+     * @return  *[]
+     */
+    itemPath() {
+            if (this.parent != null) {
+                let list = this.parent.itemPath()
+                list.push(this)
+                return list
+            }
+
+            return [this]
+        } // Function itemPath()
+
+    /**
+     * 将场景中的xy坐标转换成item坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
+     *
+     * @param   x       场景中的横坐标
+     * @param   y       场景中的纵坐标
+     *
+     * @return  在item中的坐标
+     */
+    mapFromScene(x, y) {
+            let list = this.itemPath()
+            let x0 = x
+            let y0 = y
+            for (let item of list) {
+                x0 = (x0 - item.pos.x) / item.scale.x
+                y0 = (y0 - item.pos.y) / item.scale.y
+            }
+
+            return new SPoint(x0, y0)
+        } // Function mapFromScene()
+
+    /**
+     * 将item中的xy坐标转换成场景坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
+     *
+     * @param   x       item中的横坐标
+     * @param   y       item中的纵坐标
+     *
+     * @return  在场景中的坐标
+     */
+    mapToScene(x, y) {
+            if (this.parent == null) {
+                return new SPoint(x, y)
+            }
+
+            return this.parent.mapToScene(x * this.scale.x + this.pos.x, y * this.scale.y + this.pos.y)
+        } // Function mapToScene()
+
+    // ===================================================================================================================
+    // 事件
+    /**
+     * 鼠标单击事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onClick(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onClick(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            return false
+        } // Function onClick()
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onDbClick(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onDbClick(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            return false
+        } // Function onClick()
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(e) {
+            // console.log(e)
+
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onMouseDown(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            if (this.canMove) {
+                this._mouseDownPos = new SPoint(e.x, e.y)
+                this._isMove = true
+                this[grabItem](this)
+                    // console.log(this.scene.grabItem)
+                return true
+            }
+            return false
+        } // Function onMouseDown()
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseMove(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onMouseMove(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            if (e.buttons & SMouseEvent.LEFT_BUTTON && this.canMove && this._isMove) {
+                this.moveTo(this.pos.x + e.x - this._mouseDownPos.x, this.pos.y + e.y - this._mouseDownPos.y)
+            }
+
+            return false
+        } // Function onMouseMove()
+
+    /**
+     * 释放鼠标事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseUp(e) {
+            for (let item of this.children) {
+                if (!item.visible) { // 如果项目不可见
+                    continue
+                }
+                let ce = this[toChildMouseEvent](item, e)
+                if (item.contains(e.x, e.y) && item.onMouseUp(ce)) { // 如果点在子项目上且子项目处理了事件
+                    return true
+                }
+            }
+
+            this._isMove = false
+            this[releaseItem]()
+            return false
+        } // Function onMouseUp()
+
+    // ===================================================================================================================
+    // 私有方法
+    /**
+     * 按ZOrder排序
+     *
+     * @param   a   比较元素1
+     * @param   b   比较元素2
+     * @return {number}
+     */
+    [sortItemZOrder](a, b) {
+        return a.zOrder - b.zOrder
+    } // Function sortItemZOrder()
+
+    /**
+     * 鼠标事件转子对象鼠标事件
+     *
+     * @param   child   子对象e
+     * @param   e       事件参数
+     * @return  {}
+     */
+    [toChildMouseEvent](child, e) {
+        let ce = {...e }
+        ce.x = (e.x - child.pos.x) / child.scale.x
+        ce.y = (e.y - child.pos.y) / child.scale.y
+        return ce
+    } // Function toChildMouseEvent()
+
+    /**
+     * 锁定item
+     *
+     * @param   item    被锁定的item
+     */
+    [grabItem](item) {
+        if (this.scene != null) {
+            this.scene.grabItem = item
+        }
+    } // Function grabItem
+
+    /**
+     * 释放被锁定的item
+     */
+    [releaseItem]() {
+        if (this.scene != null) {
+            this.scene.grabItem = null
+        }
+    } // Function grabItem
+} // Class SGraphyItem

+ 201 - 0
src/assets/node-templete/SGraphy/SGraphyScene.js

@@ -0,0 +1,201 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+import SPoint from './types/SPoint'
+import SRect from './types/SRect'
+import SGraphyItem from './SGraphyItem'
+
+const toGrabItemMotionEvent = Symbol('toGrabItemMotionEvent')
+
+/**
+ * SGraphy图形引擎场景类
+ *
+ * @author  Andy
+ */
+export default class SGraphyScene {
+    /**
+     * 构造函数
+     */
+    constructor() {
+            this.view = null
+            console.log(this, 'this')
+            this.root = new SGraphyItem(null)
+            this.root._scene = this
+            this.pos = new SPoint(0, 0)
+            this.scale = new SPoint(1, 1)
+            this.grabItem = null
+        } // Function constructor()
+
+    /**
+     * 添加item对象到场景。
+     *
+     * @param   item        添加的对象
+     */
+    addItem(item) {
+            item.parent = this.root
+        } // Function addItem()
+
+    /**
+     * 从场景中移除Item。
+     *
+     * @param   item        被移除的对象
+     */
+    removeItem(item) {
+            item.parent = null
+        } // Function removeItem()
+
+    /**
+     * 绘制场景
+     *
+     * @param   canvas      画布
+     * @param   rect        更新绘制区域
+     */
+    drawScene(canvas, rect) {
+            this.root.onDraw(canvas, rect)
+        } // Function drawScene()
+
+    /**
+     * 绘制背景
+     *
+     * @param   canvas      画布
+     * @param   rect        更新绘制区域
+     */
+    drawBackground(canvas, rect) {
+            // DO NOTHING
+        } // Function drawBackground()
+
+    /**
+     * 绘制前景
+     *
+     * @param   canvas      画布
+     * @param   rect        更新绘制区域
+     */
+    drawForeground(canvas, rect) {
+            // DO NOTHING
+        } // Function drawForeground()
+
+    /**
+     * 返回场景的item边界。即所有item边界的并集。
+     *
+     * @return  SRect
+     */
+    worldRect() {
+            let rect = null
+
+            for (let item of this.root.children) { // 依次取item列中的所有item。将所有item的边界做并焦处理。
+                if (rect == null) {
+                    rect = item.boundingRect().adjusted(item.pos)
+                } else {
+                    rect.union(item.boundingRect().adjusted(item.pos))
+                }
+            }
+
+            return rect
+        } // Function worldsRect()
+
+    /**
+     * 更新
+     */
+    update() {} // Function update()
+        // ===================================================================================================================
+        // 事件
+        /**
+         * 鼠标单击事件
+         *
+         * @param   e   保存事件参数
+         * @return  boolean
+         */
+    onClick(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onClick(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onClick(e)
+        } // onClick
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onDbClick(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onDbClick(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onDbClick(e)
+        } // onClick
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseDown(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onMouseDown(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onMouseDown(e)
+        } // onMouseDown
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseMove(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onMouseMove(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onMouseMove(e)
+        } // onMouseMove
+
+    /**
+     * 释放鼠标事件
+     *
+     * @param   e   保存事件参数
+     * @return  boolean
+     */
+    onMouseUp(e) {
+            if (this.grabItem != null) {
+                return this.grabItem.onMouseUp(this[toGrabItemMotionEvent](this.grabItem, e))
+            }
+            return this.root.onMouseUp(e)
+        } // onMouseUp
+
+    /**
+     * 转换场景事件坐标到指定Item坐标事件
+     *
+     * @param   item        指定的item对象
+     * @param   e           场景事件
+     * @return  {}
+     */
+    [toGrabItemMotionEvent](item, e) {
+        let se = {...e }
+        let p = item.mapFromScene(e.x, e.y)
+        se.x = p.x
+        se.y = p.y
+        return se
+    } // Function toGrabItemMotionEvent()
+} // Class SGraphyScene

+ 280 - 0
src/assets/node-templete/SGraphy/SGraphyView.js

@@ -0,0 +1,280 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+import './SCanvas'
+import SPoint from './types/SPoint'
+import SMouseEvent from './SMouseEvent'
+
+const bindEvent = Symbol('bindEvent')
+const toSceneMotionEvent = Symbol('toSceneMotionEvent')
+    /**
+     * Graphy图形引擎视图类
+     *
+     * @author  Andy
+     */
+export default class SGraphyView {
+    /**
+     * 构造函数
+     *
+     * @param   id    Canvas对象ID
+     */
+    constructor(id, scene) {
+            this.canvasView = document.getElementById(id)
+            this.canvas = this.canvasView.getContext('2d')
+            this[bindEvent](this.canvasView)
+            this.scene = scene
+            this.pos = new SPoint(10, 0)
+            this.scale = 1
+            this.minScale = 0.004
+            this.maxScale = 0.5
+            this._midKeyX = 0
+            this._midKeyY = 0
+            this.wheelZoom = 1.05
+
+            window.requestAnimationFrame(this.onDraw.bind(this))
+        } // Function constructor()
+
+    /**
+     * 绑定canvas事件
+     *
+     * @param   canvas      canvas对象
+     * @private
+     */
+    [bindEvent](canvasView) {
+        canvasView.onclick = this.onClick.bind(this)
+        canvasView.ondblclick = this.onDbClick.bind(this)
+        canvasView.onmousedown = this.onMouseDown.bind(this)
+        canvasView.onmousemove = this.onMouseMove.bind(this)
+        canvasView.onmouseup = this.onMouseUp.bind(this)
+        canvasView.onmousewheel = this.onMouseWheel.bind(this)
+        canvasView.onresize = this.onResize.bind(this)
+    } // Function [bindEvent]()
+
+    /**
+     * 获取canvas的宽度  
+     */
+
+    get width() {
+        return this.canvasView.width
+    }
+
+    get height() {
+        return this.canvasView.height
+    }
+
+    /**
+     * 将场景中的xy坐标转换成视图坐标。
+     *
+     * @param   x       场景中的横坐标
+     * @param   y       场景中的纵坐标
+     *
+     * @return  SPoint
+     */
+    mapFromScene(x, y = null) {
+            if (typeof(x) === 'object') { // 如果传入的是SPoint对象
+                return new SPoint(x.x * this.scale + this.pos.x, x.y * this.scale + this.pos.y)
+            }
+
+            return new SPoint(x * this.scale + this.pos.x, y * this.scale + this.pos.y)
+        } // Function mapFromScene()
+
+    /**
+     * 将item中的xy坐标转换成场景坐标。
+     *
+     * @param   x       item中的横坐标
+     * @param   y       item中的纵坐标
+     * @return  SPoint
+     */
+    mapToScene(x, y = null) {
+            if (typeof(x) === 'object') { // 如果传入的是SPoint对象
+                return new SPoint((x.x - this.pos.x) / this.scale, (x.y - this.pos.y) / this.scale)
+            }
+
+            return new SPoint((x - this.pos.x) / this.scale, (y - this.pos.y) / this.scale)
+        } // Function mapToScene()
+
+    /**
+     * 缩放视图时计算视图的位置与缩放比例
+     *
+     * @param   zoom        缩放比例
+     * @param   x0          缩放计算的中心点X坐标
+     * @param   y0          缩放计算的中心点Y坐标
+     */
+    scaleByPoint(zoom, x0, y0) {
+            let z = zoom
+                /**
+                 * 缩放比例在最小比例和最大比例范围内
+                 */
+            if (this.scale * zoom >= this.maxScale) { // 大于最大缩放比例
+                z = this.maxScale / this.scale
+                this.scale = this.maxScale
+            } else if (this.scale * zoom <= this.minScale) { // 小于最小绽放比例
+                z = this.minScale / this.scale
+                this.scale = this.minScale
+            } else {
+                this.scale *= zoom
+            }
+
+            this.pos.x = x0 - (x0 - this.pos.x) * z
+            this.pos.y = y0 - (y0 - this.pos.y) * z
+
+            // EventBus.getDefault().post(SGraphyViewZoomEvent(this, scale))
+            // EventBus.getDefault().post(SGraphyViewMoveEvent(this, pos.x, pos.y))
+            // return
+        } // Function scaleByPoint()
+        // ===================================================================================================================
+        // 事件
+    onDraw() {
+            this.canvas.save()
+            this.canvas.clearRect(0, 0, this.canvasView.width, this.canvasView.height)
+            this.canvas.restore()
+
+            if (this.scene != null) {
+                // 绘制背景
+                this.canvas.save()
+                this.scene.drawBackground(this.canvas)
+                this.canvas.restore()
+
+                // 绘制场景
+                this.canvas.save()
+                this.canvas.translate(this.pos.x, this.pos.y)
+                this.canvas.scale(this.scale, this.scale)
+                this.scene.drawScene(this.canvas)
+                this.canvas.restore()
+
+                // 绘制前景
+                this.canvas.save()
+                this.scene.drawForeground(this.canvas)
+                this.canvas.restore()
+            }
+
+            window.requestAnimationFrame(this.onDraw.bind(this))
+        } // Function onDraw()
+
+    /**
+     * 鼠标单击事件
+     *
+     * @param   e   保存事件参数
+     */
+    onClick(e) {
+            if (this.scene != null) {
+                let se = this[toSceneMotionEvent](e)
+                this.scene.onClick(se)
+            }
+        } // Function onClick()
+
+    /**
+     * 鼠标双击事件
+     *
+     * @param   e   保存事件参数
+     */
+    onDbClick(e) {
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onDbClick(ce)
+            }
+        } // Function onDbClick()
+
+    /**
+     * 鼠标按下事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseDown(e) {
+            let se = new SMouseEvent(e)
+            if (se.buttons & SMouseEvent.MIDDLE_BUTTON) { // 如果按下中键
+                this._midKeyX = e.x
+                this._midKeyY = e.y
+            }
+
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onMouseDown(ce)
+            }
+        } // Function onMouseDown()
+
+    /**
+     * 鼠标移动事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseMove(e) {
+            let se = new SMouseEvent(e)
+            if (se.buttons & SMouseEvent.MIDDLE_BUTTON) { // 如果按下中键,则移动视图
+                this.pos.x += e.x - this._midKeyX
+                this.pos.y += e.y - this._midKeyY
+                this._midKeyX = e.x
+                this._midKeyY = e.y
+                return
+            }
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onMouseMove(ce)
+            }
+        } // Function onMouseMove()
+
+    /**
+     * 释放鼠标事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseUp(e) {
+            if (this.scene != null) {
+                let ce = this[toSceneMotionEvent](e)
+                this.scene.onMouseUp(ce)
+            }
+        } // Function onMouseUp()
+
+    /**
+     * 鼠标滚轮事件
+     *
+     * @param   e   保存事件参数
+     */
+    onMouseWheel(e) {
+            let se = new SMouseEvent(e)
+            if (e.wheelDelta < 0) {
+                this.scaleByPoint(1 / this.wheelZoom, se.x, se.y)
+            } else {
+                this.scaleByPoint(this.wheelZoom, se.x, se.y)
+            }
+        } // Function onMouseWheel()
+
+    /**
+     * View大小发生变化事件
+     *
+     * @param   e   保存事件参数
+     */
+    onResize(e) {} // Function onResize()
+
+    /**
+     * MotionEvent转场景对象MotionEvent
+     *
+     * @param   e       MotionEvent
+     * @return  子对象MotionEvent
+     */
+    [toSceneMotionEvent](e) {
+        let se = new SMouseEvent(e)
+        se.x = (se.x - this.pos.x) / this.scale
+        se.y = (se.y - this.pos.y) / this.scale
+        return se
+    } // Function toSceneMotionEvent()
+} // Class SGraphyView

+ 53 - 0
src/assets/node-templete/SGraphy/SMouseEvent.js

@@ -0,0 +1,53 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * 鼠标事件
+ *
+ * @author  Andy
+ */
+export default class SMouseEvent {
+  /**
+   * 构造函数
+   *
+   * @param   e       系统鼠标事件
+   */
+  constructor(e) {
+    let bbox          = e.srcElement.getBoundingClientRect()
+    this.type         = e.type
+    this.x            = e.clientX - bbox.left
+    this.y            = e.clientY - bbox.top
+    this.screenX      = e.screenX
+    this.screenY      = e.screenY
+    this.clientX      = e.clientX
+    this.clientY      = e.clientY
+    this.altKey       = e.altKey
+    this.ctrlKey      = e.ctrlKey
+    this.buttons      = e.buttons
+    this.wheelDelta   = e.wheelDelta
+  } // Function constructor()
+} // Class MouseEvent
+
+SMouseEvent.LEFT_BUTTON   = 0x01
+SMouseEvent.RIGHT_BUTTON  = 0x02
+SMouseEvent.MIDDLE_BUTTON = 0x04

+ 201 - 0
src/assets/node-templete/SGraphy/items/SGraphyClockItem.js

@@ -0,0 +1,201 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect'
+
+/** 定义符号,用于定义私有成员变晴儿 */
+const drawScale     = Symbol('drawScale')
+const drawScaleText = Symbol('drawScaleText')
+const drawHour      = Symbol('drawHour')
+const drawMinute    = Symbol('drawMinute')
+const drawSecond    = Symbol('drawSecond')
+
+/**
+ * SGraphy引擎时钟Item
+ *
+ * @author  Andy
+ */
+export default class SGraphyClockItem extends SGraphyItem {
+  /**
+   * 构造函数
+   *
+   * @param   parent    指向父对象
+   */
+  constructor(width, height, parent = null) {
+    super(parent)
+    this.name = 'ClockItem'
+    this.width = width
+    this.height = height
+    /** 是否显示刻度 */
+    this.isShowScale = true
+    /** 刻度颜色 */
+    this.scaleColor = '#000'
+    /** 刻度文本颜色 */
+    this.textColor = '#000'
+    /** 时针颜色 */
+    this.hourColor = '#000'
+    /** 分针颜色 */
+    this.minuteColor = '#000'
+    /** 秒针颜色 */
+    this.secondColor = '#F00'
+    /** 是否显示钞针 */
+    this.isShowSecond = true
+    /** 是否平滑移动秒针 */
+    this.isSmooth = true
+    /** 边缘宽度 */
+    this.padding = 100.0
+  } // Function constructor()
+
+  /**
+   * Item对象边界区域
+   *
+   * @return  SRect
+   */
+  boundingRect() {
+    return new SRect(0, 0, this.width / 2, this.height / 2)
+  } // Function boundingRect()
+
+  /**
+   * 时钟半径,只读属性
+   *
+   * @return  number
+   */
+  get radius() {
+    return Math.min(this.width, this.height) / 2.0
+  } // getter radius()
+
+  /**
+   * 绘制时钟
+   *
+   * @param   canvas      画布
+   * @param   rect        绘制区域
+   */
+  onDraw(canvas, rect) {
+    canvas.translate(this.width / 2, this.height / 2)
+    canvas.arc(0, 0, this.radius, 0, 2 * Math.PI)
+    let t = new Date()
+
+    this[drawScale](canvas)
+    this[drawHour](canvas, t.getHours(), t.getMinutes(), t.getSeconds())
+    this[drawMinute](canvas, t.getMinutes(), t.getSeconds())
+    this[drawSecond](canvas, t.getSeconds() + t.getMilliseconds() / 1000.0)
+  } // Function onDraw()
+
+  /**
+   * 绘制刻度
+   *
+   * @param   canvas      画布
+   */
+  [drawScale](canvas) {
+    let scaleLength = Math.max(this.radius / 10.0, 2.0)
+    let scaleLength1 = scaleLength * 1.2
+    let strokeWidth = Math.max(this.radius / 100.0, 2.0)
+    let strokeWidth1 = strokeWidth * 2.0
+
+    canvas.save()
+    canvas.strokeStyle = this.scaleColor
+
+    for (let i = 1; i <= 12; i++) {          // 12小时刻度
+      canvas.lineWidth = strokeWidth1
+      canvas.drawLine(0, -this.radius, 0, -this.radius + scaleLength1)
+
+      if (this.radius >= 40) {              // 如果半度大于40显示分钟刻度
+        canvas.rotate(6 * Math.PI / 180)
+        for (let j = 1; j <= 4; j++) {       // 分钟刻度
+          canvas.lineWidth = strokeWidth
+          canvas.drawLine(0, -this.radius, 0, -this.radius + scaleLength)
+          canvas.rotate(6 * Math.PI / 180)
+        }
+      } else {
+        canvas.rotate(30 * Math.PI / 180)
+      }
+    }
+
+    canvas.restore()
+  } // Function drawScale()
+
+  /**
+   * 绘制刻度数字
+   *
+   * @param   canvas      画布
+   */
+  [drawScaleText](canvas) {
+
+  } // Function drawScaleText()
+
+  /**
+   * 绘制时针
+   *
+   * @param   canvas      画布
+   * @param   hour        时
+   * @param   minute      分
+   * @param   second      秒
+   */
+  [drawHour](canvas, hour, minute, second) {
+    canvas.save()
+    canvas.lineCap = 'round'
+    canvas.lineWidth = Math.max(this.radius / 30.0, 4.0)
+    canvas.strokeStyle = this.hourColor
+    canvas.rotate((hour * 30.0 + minute * 30.0 / 60 + second * 30.0 / 60 / 60) * Math.PI / 180)
+    canvas.drawLine(0, this.radius / 10.0, 0, -this.radius / 2.0)
+    canvas.restore()
+  } // Function drawHour()
+
+  /**
+   * 绘制分针
+   *
+   * @param   canvas      画布
+   * @param   minute      分
+   * @param   second      秒
+   */
+  [drawMinute](canvas, minute, second) {
+    canvas.save()
+    canvas.lineCap = 'round'
+    canvas.lineWidth = Math.max(this.radius / 40.0, 4.0)
+    canvas.strokeStyle = this.minuteColor
+    canvas.rotate((minute * 6 + second * 6 / 60.0) * Math.PI / 180.0)
+    canvas.drawLine(0, this.radius / 10.0, 0, -this.radius * 2.0 / 3.0)
+    canvas.restore()
+  } // Function drawMinute()
+
+  /**
+   * 绘制秒针
+   *
+   * @param   canvas      画布
+   * @param   second      秒
+   */
+  [drawSecond](canvas, second) {
+    canvas.save()
+    canvas.lineCap = 'round'
+    canvas.lineWidth = Math.max(this.radius / 100.0, 3.0)
+    canvas.strokeStyle = this.secondColor
+    canvas.rotate(second * 6 * Math.PI / 180)
+    canvas.drawLine(0, this.radius / 5.0, 0, -this.radius + this.radius / 10.0)
+    // canvas.drawCircle(0, 0, this.radius / 30.0)
+    // canvas.drawCircle(0, -this.radius + this.radius / 5.0, this.radius / 60.0)
+    // canvas.strokeStyle = Color.YELLOW
+    // canvas.drawCircle(0, 0, this.radius / 100.0)
+    canvas.restore()
+  } // Function drawSecond()
+} // Class SGraphyClockItem

+ 58 - 0
src/assets/node-templete/SGraphy/items/SGraphyLineItem.js

@@ -0,0 +1,58 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from './../types/SRect';
+
+export default class SGraphyLineItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param startX  线的起始x坐标
+     * @param startY  线的起始y坐标
+     * @param endX    线的终止x坐标
+     * @param endY    线的终止y坐标
+     * @param width   线的宽度
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     */
+    constructor(startX, startY, endX, endY, color, width, isVirtual, parent = null) {
+        super(parent)
+        this.startX = startX
+        this.startY = startY
+        this.endX = endX
+        this.endY = endY
+        this.color = color
+        this.width = width
+        this.isVirtual = isVirtual
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(0, 0, this.width, this.height)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        if (this.isVirtual) {
+            canvas.setLineDash([15, 5])
+        }
+        canvas.lineWidth = this.width || 1
+        canvas.strokeStyle = this.color || '#000'
+        canvas.beginPath();
+        canvas.moveTo(this.startX, this.startY)
+        canvas.lineTo(this.endX, this.endY)
+        canvas.stroke()
+    }
+}

+ 57 - 0
src/assets/node-templete/SGraphy/items/SGraphyPolygonItem.js

@@ -0,0 +1,57 @@
+/**
+ * 不规则多边形,元空间
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from './../types/SRect';
+
+export default class SGraphyPolygonItem extends SGraphyItem {
+    /**
+     * 
+     * 构造函数
+     * 
+     * @param jsonArr  空间线条数组
+     * @param lineWidth    空间线条的宽度
+     * @param color    空间线条的颜色
+     * @param fillColor  空间的填充颜色
+     * 
+     */
+    constructor(jsonArr, lineWidth, color, fillColor, parent = null) {
+            super(parent)
+            this.jsonArr = jsonArr
+            this.lineWidth = lineWidth
+            this.color = color
+            this.fillColor = fillColor
+        } //constructor
+
+    /**
+     * Item的边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(0, 0, this.width, this.height)
+    }
+
+    /**
+     * 绘制不规则多边形
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+    onDraw(canvas, rect) {
+        if (this.jsonArr && this.jsonArr.length) {
+            canvas.beginPath();
+            canvas.lineWidth = this.lineWidth || 1
+            canvas.strokeStyle = this.color || '#000'
+            canvas.fillStyle = this.fillColor || '#fff'
+            canvas.moveTo(this.jsonArr[0].X / 120 * -1, this.jsonArr[0].Y / 120)
+            for (let i = 1; i < this.jsonArr.length - 1; i++) {
+                canvas.lineTo(this.jsonArr[i].X / 120 * -1, this.jsonArr[i].Y / 120)
+            }
+            canvas.lineTo(this.jsonArr[0].X / 120 * -1, this.jsonArr[0].Y / 120)
+            canvas.closePath()
+            canvas.fill()
+            canvas.stroke()
+        }
+    }
+}

+ 64 - 0
src/assets/node-templete/SGraphy/items/SGraphyRectItem.js

@@ -0,0 +1,64 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+import SGraphyItem from '../SGraphyItem'
+import SRect from '../types/SRect'
+
+/**
+ * SGraphy引擎时钟Item
+ *
+ * @author  Andy
+ */
+export default class SGraphyRectItem extends SGraphyItem {
+  /**
+   * 构造函数
+   */
+  constructor(width, height, parent) {
+    super(parent)
+    this.name = 'RectItem'
+    this.width = width
+    this.height = height
+    this.style = '#f00'
+    this.canMove = true
+  } // Function constructor()
+
+  /**
+   * Item对象边界区域
+   *
+   * @return  SRect
+   */
+  boundingRect() {
+    return new SRect(0, 0, this.width, this.height)
+  } // Function boundingRect()
+
+  /**
+   * 绘制时钟
+   *
+   * @param   canvas      画布
+   * @param   rect        绘制区域
+   */
+  onDraw(canvas, rect) {
+    canvas.fillStyle = this.style
+    canvas.fillRect(0, 0, this.width, this.height)
+  } // Function onDraw()
+} // Class SGraphyRectItem

+ 61 - 0
src/assets/node-templete/SGraphy/items/SGraphyVirtualItem.js

@@ -0,0 +1,61 @@
+/**
+ * 线条
+ */
+import SGraphyItem from '../SGraphyItem'
+import SRect from './../types/SRect';
+
+export default class SGraphyLineItem extends SGraphyItem {
+    /**
+     * 构造函数
+     * 
+     * @param startX  线的起始x坐标
+     * @param startY  线的起始y坐标
+     * @param endX    线的终止x坐标
+     * @param endY    线的终止y坐标
+     * @param width   线的宽度
+     * 
+     * @param color  线的颜色
+     * @param isVirtual    是否为虚线
+     * 
+     * @param canMove  移动
+     */
+    constructor(startX, startY, endX, endY, color, width, isVirtual, canMove, parent = null) {
+        super(parent)
+        this.startX = startX
+        this.startY = startY
+        this.endX = endX
+        this.endY = endY
+        this.color = color
+        this.width = width
+        this.isVirtual = isVirtual
+        this.canMove = canMove
+    }
+
+    /**
+     * Item对象边界区域
+     * 
+     * @return SRect
+     */
+    boundingRect() {
+        return new SRect(0, 0, this.width, this.height)
+    }
+
+    /**
+     * 绘制线条
+     * 
+     * @param canvas 画布
+     * @param rect   绘制区域
+     */
+
+    onDraw(canvas, rect) {
+        if (this.isVirtual) {
+            canvas.setLineDash([15, 5])
+        }
+        canvas.lineWidth = this.width || 1
+        canvas.strokeStyle = this.color || '#000'
+        canvas.beginPath();
+        canvas.moveTo(this.startX, this.startY)
+        canvas.lineTo(this.endX, this.endY)
+        canvas.stroke()
+    }
+}

+ 40 - 0
src/assets/node-templete/SGraphy/types/SPoint.js

@@ -0,0 +1,40 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * 坐标点
+ *
+ * @author  Andy
+ */
+export default class SPoint {
+  /**
+   * 构造函数
+   *
+   * @param   x     X坐标
+   * @param   y     Y坐标
+   */
+  constructor(x, y) {
+    this.x = x
+    this.y = y
+  } // constructor
+} // Class SPoint

+ 169 - 0
src/assets/node-templete/SGraphy/types/SRect.js

@@ -0,0 +1,169 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2018.  斯伯坦机器人世界
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+/**
+ * 矩形
+ *
+ * @author  Andy
+ */
+export default class SRect {
+  /**
+   * 构造函数
+   *
+   * @param   x       X坐标
+   * @param   y       Y坐标
+   * @param   width   宽度
+   * @param   height  高度
+   */
+  constructor(x = 0, y = 0, width = 0, height = 0) {
+    this._x = x
+    this._y = y
+    this._width = Math.max(width, 0)
+    this._height = Math.max(height, 0)
+  } // constructor
+
+  /**
+   * left属性
+   */
+  get left() {
+    return this._x
+  }
+  set left(value) {
+    this._x = value
+  }
+
+  /**
+   * right属性
+   */
+  get right() {
+    return this._x + this._width
+  }
+  set right(value) {
+    this._width = Math.max(value - this._x, 0)
+  }
+
+  /**
+   * top属性
+   */
+  get top() {
+    return this._y
+  }
+  set top(value) {
+    this._y = value
+  }
+
+  /**
+   * top属性
+   */
+  get bottom() {
+    return this._y + this._height
+  }
+  set bottom(value) {
+    this._height = Math.max(value - this._y, 0)
+  }
+
+  /**
+   * x属性
+   *
+   * @return {*}
+   */
+  get x() {
+    return this._x
+  }
+  set x(value) {
+    this._x = value
+  }
+
+  /**
+   * x属性
+   *
+   * @return {*}
+   */
+  get y() {
+    return this._y
+  }
+  set y(value) {
+    this._y = value
+  }
+
+  /**
+   * width 属性
+   *
+   * @return {number}
+   */
+  get width() {
+    return this._width
+  }
+  set width(value) {
+    this._width = Math.max(value, 0)
+  }
+
+  /**
+   * height 属性
+   * @return {number}
+   */
+  get height() {
+    return this._height
+  }
+  set height(value) {
+    this._height = Math.max(value, 0)
+  }
+
+  /**
+   * 判断矩形空间是否包含点x,y
+   *
+   * @param   x       横坐标(当前item)
+   * @param   y       纵坐标(当前item)
+   * @return  boolean
+   */
+  contains(x, y) {
+    return (x >= this.x && x <= this.right) && (y >= this.top && y <= this.bottom)
+  } // Function contains()
+
+  /**
+   * 调整Rect位置
+   *
+   * @param x
+   * @param y
+   * @return  SRect
+   */
+  adjusted(x, y = null) {
+    if (typeof (x) === 'object') {     // 如果传入的是SPoint对象
+      return new SRect(this.x + x.x, this.y + x.y, this.width, this.height)
+    }
+
+    return new SRect(this.x + x, this.y + y, this.width, this.height)
+  } // Function adjusted()
+
+  /**
+   * 合并rect
+   *
+   * @param   rect
+   */
+  union(rect) {
+    this.left = Math.min(this.left, rect.left)
+    this.top = Math.min(this.top, rect.top)
+    this.right = Math.max(this.right, rect.right)
+    this.bottom = Math.max(this.bottom, rect.bottom)
+  } // Function union()
+} // Class SRect

+ 9 - 0
src/assets/node-templete/index.js

@@ -0,0 +1,9 @@
+import SGraphyView from './SGraphy/SGraphyView'
+import SGraphyScene from './SGraphy/SGraphyScene'
+import SGraphyClockItem from './SGraphy/items/SGraphyClockItem'
+import SGraphyRectItem from './SGraphy/items/SGraphyRectItem'
+// import SGraphyImageItem from './SGraphy/items/SGraphyImageItem'
+import SGraphyLineItem from './SGraphy/items/SGraphyLineItem'
+import SGraphyPolygonItem from './SGraphy/items/SGraphyPolygonItem'
+
+export { SGraphyView, SGraphyScene, SGraphyClockItem, SGraphyRectItem, SGraphyLineItem, SGraphyPolygonItem }

+ 37 - 0
src/assets/pako/pako.js

@@ -0,0 +1,37 @@
+import pako from 'pako'
+
+let funcTo = atob
+console.log(funcTo)
+
+const pakoObj = {
+    unzip: function unzip(b64Data) {
+        // console.log(b64Data)
+        var strData = funcTo(b64Data);
+
+        // Convert binary string to character-number array
+        var charData = strData.split('').map(function(x) { return x.charCodeAt(0); });
+        // console.log("charData", charData)
+
+        // Turn number array into byte-array
+        var binData = new Uint8Array(charData);
+
+
+        // // unzip
+        var data = pako.inflate(binData, { to: 'string' });
+        // console.log(data)
+        // strData = new Uint16Array(data).reduce(function(data, byte) {
+        //     return data + String.fromCharCode(byte);
+        // })
+        // console.log(strData)
+        return eval("(" + data + ")");
+    },
+    // 压缩
+    zip: function(str) {
+        //escape(str)  --->压缩前编码,防止中午乱码
+        var binaryString = pako.gzip(escape(str), { to: 'string' });
+        console.log(binaryString)
+        return binaryString;
+    }
+}
+
+export default pakoObj

+ 120 - 0
src/assets/scss/point/point_config/common.scss

@@ -0,0 +1,120 @@
+$borderWidth: 1px;
+$borderColor: #ccc;
+$hoverColor: #409EFF;
+$w: 800px;
+$radiusWidth: 4px;
+$size3: 23px;
+.saga-size3 {
+    font-size: $size3;
+}
+
+.select-bg {
+    background-color: $hoverColor;
+}
+
+.saga-border {
+    border: $borderWidth solid $borderColor;
+    box-sizing: border-box;
+    border-radius: $radiusWidth;
+}
+
+.el-dialog__body {
+    padding: 0;
+}
+
+body .htFiltersConditionsMenu:not(.htGhostTable) {
+    z-index: 4070;
+}
+
+.border-hover {}
+
+.border-hover:hover {
+    border-color: $hoverColor;
+}
+
+.saga-dashed-border {
+    border: $borderWidth dashed $borderColor;
+    border-radius: $radiusWidth;
+}
+
+.flex {
+    display: flex;
+    justify-content: space-around;
+}
+
+.saga-fill {
+    width: 100%;
+    height: 100%;
+}
+
+.margin5 {
+    margin: 18px;
+}
+
+.dcenter {
+    margin-left: auto;
+    margin-right: auto;
+}
+
+.h1 {
+    font-size: 24px;
+    color: #8E9CC1;
+}
+
+.h3 {
+    font-size: 14px;
+    color: #333;
+}
+
+.h5 {
+    font-size: 12px;
+    color: #999;
+}
+
+.center {
+    text-align: center;
+}
+
+.padding10 {
+    padding: 10px;
+}
+
+.bg-F5 {
+    background: #F5FCFF;
+}
+
+.block {
+    display: block;
+}
+
+.w {
+    width: $w;
+}
+
+.h100 {
+    height: 100%;
+}
+
+.p-over2 {
+    text-overflow: -o-ellipsis-lastline;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2; //这儿的数字代表的就是你所需要实现效果的第N行
+    -webkit-box-orient: vertical;
+    overflow-wrap: break-word;
+}
+
+.pointer {
+    cursor: pointer;
+}
+
+@media screen and (max-width: 1200px) {
+    .w {
+        width: 560px;
+    }
+    html {
+        overflow-x: auto;
+        overflow-y: auto;
+    }
+}

+ 473 - 0
src/assets/scss/point/point_config/reset.scss

@@ -0,0 +1,473 @@
+$WinWitdh:1200px;
+$BaseC:#555;
+$LinkC:#06f;
+$HoverC:#f60;
+$FontSize:13px;
+$fontFamily: "Spurce Han Sans,Wah Kang font,Microsoft YaHei";
+$allColor: #333; //正文#333
+$color1: #666; //辅助文字#666
+$color2: #999; //#999
+$color3: #ccc; //失效文字#ccc
+$color4: #00A0E8; //链接文字
+$color5: #8E9CC1; //表头文字加粗
+$colorList: ( 1:$color1, 2:$color2, 3:$color3, 4:$color4, 5:$color5);
+$theme1: #5BA7FF;
+$theme2: #FFF;
+$theme3: #8E9CC1;
+$themeList: (1:$theme1, 2:$theme2, 3:$theme3);
+$auxiliary1: #548FF0; //辅助色彩
+$auxiliary2: #FF4121;
+$auxiliary3: #7ED321;
+$auxiliary4: #F5A623;
+$auxiliaryList: ( 1:$auxiliary1, 2:$auxiliary2, 3:$auxiliary3, 4:$auxiliary4);
+$borderColor1: #C2CEDB; //线条色彩
+$borderColor2: #DDD;
+$borderColorList: (1:$borderColor1, 2:$borderColor2);
+$bg1: #FFF;
+$bg2: #F2FBFF;
+$bg3: #EEFAFF;
+$bg4: #FFF5E4;
+$bg5: #E5F6D3;
+$bgList: ( 1:$bg1, 2:$bg2, 3:$bg3, 4:$bg4, 5:$bg5);
+$allSize: 14px; //正常文字大小
+$size1: 12px; //辅助文字大小
+// mixins
+@mixin dz($time:0.25s) {
+    -webkit-transition: all $time ease-in-out;
+    -moz-transition: all $time ease-in-out;
+    -o-transition: all $time ease-in-out;
+    transition: all $time ease-in-out;
+}
+
+// .el-dialog__headerbtn {
+//     top: -24px;
+//     right: -24px;
+//     width: 24px;
+//     height: 24px;
+//     border-radius: 50%;
+//     color: #8E9CC1;
+//     background-color: #fff;
+// }
+.el-dialog__header {
+    border-bottom: 1px solid #C2CEDB;
+    box-sizing: border-box;
+    .el-dialog__title {
+        height: 20px;
+        line-height: 20px;
+        font-size: 14px;
+    }
+}
+
+// .el-button--mini,
+// .el-button--mini.is-round {
+//     padding: 5px 20px;
+// }
+// .el-button {
+//     box-sizing: border-box;
+//     border-color: $auxiliary1;
+// }
+// .el-button span {
+//     color: $color4;
+// }
+// .el-button:focus,
+// .el-button:hover {
+//     background-color: $bg1;
+//     border-color: $auxiliary1;
+// }
+// .el-button span {
+//     display: inline-block;
+//     line-height: 20px;
+//     height: 20px;
+// }
+// * {
+//     font-family: $fontFamily;
+//     font-size: $allSize;
+//     color: $allColor;
+// }
+// .color {
+//     color: $allColor;
+// }
+// .el-input__inner:hover{
+//     border-color: 
+// }
+//文字色
+@each $i,
+$color in $colorList {
+    .color#{$i} {
+        color: $color;
+    }
+}
+
+//主题色
+@each $i,
+$theme in $themeList {
+    .theme#{$i} {
+        background-color: $theme;
+    }
+}
+
+//辅助背景色
+@each $i,
+$auxiliary in $auxiliaryList {
+    .auxiliary#{$i} {
+        background-color: $auxiliary;
+    }
+}
+
+//辅助色
+@each $i,
+$auxiliary in $auxiliaryList {
+    .auxiliary-color#{$i} {
+        color: $auxiliary;
+    }
+}
+
+//线条色
+@each $i,
+$borderColor in $borderColorList {
+    .border-color#{$i} {
+        border-color: $borderColor;
+    }
+}
+
+//背景色
+@each $i,
+$bg in $bgList {
+    .bg#{$i} {
+        background-color: $bg;
+    }
+}
+
+@for $i from 1 to 51 {
+    .border-radius#{$i} {
+        border-radius: #{$i}px;
+    }
+}
+
+@for $i from 1 to 11 {
+    .border#{$i} {
+        border-width: #{$i}px;
+        border-style: solid;
+    }
+}
+
+// .color2 {
+//     color: $color2;
+// }
+// .color3 {
+//     color: $color3;
+// }
+// .color4 {
+//     color: $color4;
+// }
+// .color5 {
+//     color: $color5
+// }
+// @charset "UTF-8";
+body,
+ul,
+ol,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+pre,
+form,
+fieldset,
+legend,
+input,
+button,
+textarea,
+p,
+blockquote,
+table,
+th,
+td,
+menu {
+    margin: 0;
+    padding: 0
+}
+
+html,
+body,
+#app {
+    height: 100%;
+    width: 100%;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+    table-layout: fixed
+}
+
+ul,
+ol,
+menu {
+    list-style: none
+}
+
+fieldset,
+img {
+    border: none
+}
+
+img,
+object,
+select,
+input,
+textarea,
+button {
+    vertical-align: middle
+}
+
+input,
+textarea,
+select,
+address,
+caption,
+cite,
+code,
+dfn,
+em,
+i,
+b,
+strong,
+small,
+th,
+var,
+abbr {
+    font-size: 100%;
+    font-style: normal
+}
+
+caption,
+th {
+    text-align: left;
+}
+
+article,
+aside,
+footer,
+header,
+hgroup,
+nav,
+section,
+figure,
+figcaption {
+    display: block;
+}
+
+code,
+kbd,
+pre,
+samp,
+tt {
+    font-family: Consolas, "Courier New", Courier, monospace;
+}
+
+address,
+cite,
+dfn,
+em,
+var,
+i {
+    font-style: normal;
+}
+
+blockquote,
+q {
+    quotes: none;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+    content: "";
+    content: none;
+}
+
+a {
+    color: $LinkC;
+    text-decoration: none;
+    cursor: pointer;
+    &:link,
+    &:visited,
+    &:active {
+        color: $LinkC;
+    }
+    &:hover,
+    &:focus {
+        color: $HoverC;
+        text-decoration: underline;
+        outline: none;
+    }
+    &,
+    span,
+    i,
+    em,
+    u,
+    strong,
+    b,
+    p,
+    img,
+    ul,
+    li,
+    div,
+    dd,
+    dt,
+    dl,
+    ol,
+    table,
+    th,
+    td,
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6,
+    input,
+    textarea,
+    button,
+    small,
+    select {
+        cursor: pointer;
+        @include dz;
+    }
+}
+
+abbr[title],
+acronym[title] {
+    border-bottom: 1px dotted;
+    cursor: help;
+}
+
+// base by fungleo
+html {
+    min-width: $WinWitdh;
+    // overflow-y: scroll;
+}
+
+body {
+    font-size: $FontSize;
+    color: $BaseC;
+    line-height: 2;
+}
+
+body,
+button,
+input,
+select,
+textarea {
+    font-family: tahoma, Helvetica, Arial, "\5FAE\8F6F\96C5\9ED1";
+    *font-family: "\5FAE\8F6F\96C5\9ED1";
+}
+
+h1 {
+    font-size: $FontSize+10;
+}
+
+h2 {
+    font-size: $FontSize+8;
+}
+
+h3 {
+    font-size: $FontSize+6;
+}
+
+h4 {
+    font-size: $FontSize+4;
+}
+
+h5 {
+    font-size: $FontSize+2;
+}
+
+h6 {
+    font-size: $FontSize;
+}
+
+hr {
+    border: none;
+    height: 1px;
+    background: lighten($BaseC, 50%);
+}
+
+.fl {
+    float: left;
+}
+
+.fr {
+    float: right;
+}
+
+.tl {
+    text-align: left;
+}
+
+.tr {
+    text-align: right;
+}
+
+.tc {
+    text-align: center;
+}
+
+.cf:before,
+.cf:after,
+.web:before,
+.web:after,
+.web_:before,
+.web_:after {
+    content: "";
+    display: table;
+}
+
+.cf:after,
+.web:after,
+.web_:after {
+    clear: both;
+}
+
+.cf {
+    zoom: 1;
+}
+
+.web {
+    width: $WinWitdh;
+    margin-left: auto;
+    margin-right: auto;
+    zoom: 1;
+}
+
+.web_ {
+    min-width: $WinWitdh;
+    width: 100%;
+    zoom: 1;
+}
+
+.block {
+    display: block;
+}
+
+.none {
+    display: none
+}
+
+.clear {
+    clear: both;
+}
+
+.fl {
+    float: left;
+}
+
+.disIne {
+    display: inline-block;
+}

+ 473 - 0
src/assets/scss/reset.scss

@@ -0,0 +1,473 @@
+$WinWitdh:1200px;
+$BaseC:#555;
+$LinkC:#06f;
+$HoverC:#f60;
+$FontSize:13px;
+$fontFamily: "Spurce Han Sans,Wah Kang font,Microsoft YaHei";
+$allColor: #333; //正文#333
+$color1: #666; //辅助文字#666
+$color2: #999; //#999
+$color3: #ccc; //失效文字#ccc
+$color4: #00A0E8; //链接文字
+$color5: #8E9CC1; //表头文字加粗
+$colorList: ( 1:$color1, 2:$color2, 3:$color3, 4:$color4, 5:$color5);
+$theme1: #5BA7FF;
+$theme2: #FFF;
+$theme3: #8E9CC1;
+$themeList: (1:$theme1, 2:$theme2, 3:$theme3);
+$auxiliary1: #548FF0; //辅助色彩
+$auxiliary2: #FF4121;
+$auxiliary3: #7ED321;
+$auxiliary4: #F5A623;
+$auxiliaryList: ( 1:$auxiliary1, 2:$auxiliary2, 3:$auxiliary3, 4:$auxiliary4);
+$borderColor1: #C2CEDB; //线条色彩
+$borderColor2: #DDD;
+$borderColorList: (1:$borderColor1, 2:$borderColor2);
+$bg1: #FFF;
+$bg2: #F2FBFF;
+$bg3: #EEFAFF;
+$bg4: #FFF5E4;
+$bg5: #E5F6D3;
+$bgList: ( 1:$bg1, 2:$bg2, 3:$bg3, 4:$bg4, 5:$bg5);
+$allSize: 14px; //正常文字大小
+$size1: 12px; //辅助文字大小
+// mixins
+@mixin dz($time:0.25s) {
+    -webkit-transition: all $time ease-in-out;
+    -moz-transition: all $time ease-in-out;
+    -o-transition: all $time ease-in-out;
+    transition: all $time ease-in-out;
+}
+
+// .el-dialog__headerbtn {
+//     top: -24px;
+//     right: -24px;
+//     width: 24px;
+//     height: 24px;
+//     border-radius: 50%;
+//     color: #8E9CC1;
+//     background-color: #fff;
+// }
+.el-dialog__header {
+    border-bottom: 1px solid #C2CEDB;
+    box-sizing: border-box;
+    .el-dialog__title {
+        height: 20px;
+        line-height: 20px;
+        font-size: 14px;
+    }
+}
+
+// .el-button--mini,
+// .el-button--mini.is-round {
+//     padding: 5px 20px;
+// }
+// .el-button {
+//     box-sizing: border-box;
+//     border-color: $auxiliary1;
+// }
+// .el-button span {
+//     color: $color4;
+// }
+// .el-button:focus,
+// .el-button:hover {
+//     background-color: $bg1;
+//     border-color: $auxiliary1;
+// }
+// .el-button span {
+//     display: inline-block;
+//     line-height: 20px;
+//     height: 20px;
+// }
+// * {
+//     font-family: $fontFamily;
+//     font-size: $allSize;
+//     color: $allColor;
+// }
+// .color {
+//     color: $allColor;
+// }
+// .el-input__inner:hover{
+//     border-color: 
+// }
+//文字色
+@each $i,
+$color in $colorList {
+    .color#{$i} {
+        color: $color;
+    }
+}
+
+//主题色
+@each $i,
+$theme in $themeList {
+    .theme#{$i} {
+        background-color: $theme;
+    }
+}
+
+//辅助背景色
+@each $i,
+$auxiliary in $auxiliaryList {
+    .auxiliary#{$i} {
+        background-color: $auxiliary;
+    }
+}
+
+//辅助色
+@each $i,
+$auxiliary in $auxiliaryList {
+    .auxiliary-color#{$i} {
+        color: $auxiliary;
+    }
+}
+
+//线条色
+@each $i,
+$borderColor in $borderColorList {
+    .border-color#{$i} {
+        border-color: $borderColor;
+    }
+}
+
+//背景色
+@each $i,
+$bg in $bgList {
+    .bg#{$i} {
+        background-color: $bg;
+    }
+}
+
+@for $i from 1 to 51 {
+    .border-radius#{$i} {
+        border-radius: #{$i}px;
+    }
+}
+
+@for $i from 1 to 11 {
+    .border#{$i} {
+        border-width: #{$i}px;
+        border-style: solid;
+    }
+}
+
+// .color2 {
+//     color: $color2;
+// }
+// .color3 {
+//     color: $color3;
+// }
+// .color4 {
+//     color: $color4;
+// }
+// .color5 {
+//     color: $color5
+// }
+// @charset "UTF-8";
+body,
+ul,
+ol,
+dl,
+dd,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+pre,
+form,
+fieldset,
+legend,
+input,
+button,
+textarea,
+p,
+blockquote,
+table,
+th,
+td,
+menu {
+    margin: 0;
+    padding: 0
+}
+
+html,
+body,
+#app {
+    height: 100%;
+    width: 100%;
+}
+
+table {
+    border-collapse: collapse;
+    border-spacing: 0;
+    table-layout: fixed
+}
+
+ul,
+ol,
+menu {
+    list-style: none
+}
+
+fieldset,
+img {
+    border: none
+}
+
+img,
+object,
+select,
+input,
+textarea,
+button {
+    vertical-align: middle
+}
+
+input,
+textarea,
+select,
+address,
+caption,
+cite,
+code,
+dfn,
+em,
+i,
+b,
+strong,
+small,
+th,
+var,
+abbr {
+    font-size: 100%;
+    font-style: normal
+}
+
+caption,
+th {
+    text-align: left;
+}
+
+article,
+aside,
+footer,
+header,
+hgroup,
+nav,
+section,
+figure,
+figcaption {
+    display: block;
+}
+
+code,
+kbd,
+pre,
+samp,
+tt {
+    font-family: Consolas, "Courier New", Courier, monospace;
+}
+
+address,
+cite,
+dfn,
+em,
+var,
+i {
+    font-style: normal;
+}
+
+blockquote,
+q {
+    quotes: none;
+}
+
+blockquote:before,
+blockquote:after,
+q:before,
+q:after {
+    content: "";
+    content: none;
+}
+
+a {
+    color: $LinkC;
+    text-decoration: none;
+    cursor: pointer;
+    &:link,
+    &:visited,
+    &:active {
+        color: $LinkC;
+    }
+    &:hover,
+    &:focus {
+        color: $HoverC;
+        text-decoration: underline;
+        outline: none;
+    }
+    &,
+    span,
+    i,
+    em,
+    u,
+    strong,
+    b,
+    p,
+    img,
+    ul,
+    li,
+    div,
+    dd,
+    dt,
+    dl,
+    ol,
+    table,
+    th,
+    td,
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6,
+    input,
+    textarea,
+    button,
+    small,
+    select {
+        cursor: pointer;
+        @include dz;
+    }
+}
+
+abbr[title],
+acronym[title] {
+    border-bottom: 1px dotted;
+    cursor: help;
+}
+
+// base by fungleo
+html {
+    min-width: $WinWitdh;
+    // overflow-y: scroll;
+}
+
+body {
+    font-size: $FontSize;
+    color: $BaseC;
+    line-height: 2;
+}
+
+body,
+button,
+input,
+select,
+textarea {
+    font-family: tahoma, Helvetica, Arial, "\5FAE\8F6F\96C5\9ED1";
+    *font-family: "\5FAE\8F6F\96C5\9ED1";
+}
+
+h1 {
+    font-size: $FontSize+10;
+}
+
+h2 {
+    font-size: $FontSize+8;
+}
+
+h3 {
+    font-size: $FontSize+6;
+}
+
+h4 {
+    font-size: $FontSize+4;
+}
+
+h5 {
+    font-size: $FontSize+2;
+}
+
+h6 {
+    font-size: $FontSize;
+}
+
+hr {
+    border: none;
+    height: 1px;
+    background: lighten($BaseC, 50%);
+}
+
+.fl {
+    float: left;
+}
+
+.fr {
+    float: right;
+}
+
+.tl {
+    text-align: left;
+}
+
+.tr {
+    text-align: right;
+}
+
+.tc {
+    text-align: center;
+}
+
+.cf:before,
+.cf:after,
+.web:before,
+.web:after,
+.web_:before,
+.web_:after {
+    content: "";
+    display: table;
+}
+
+.cf:after,
+.web:after,
+.web_:after {
+    clear: both;
+}
+
+.cf {
+    zoom: 1;
+}
+
+.web {
+    width: $WinWitdh;
+    margin-left: auto;
+    margin-right: auto;
+    zoom: 1;
+}
+
+.web_ {
+    min-width: $WinWitdh;
+    width: 100%;
+    zoom: 1;
+}
+
+.block {
+    display: block;
+}
+
+.none {
+    display: none
+}
+
+.clear {
+    clear: both;
+}
+
+.fl {
+    float: left;
+}
+
+.disIne {
+    display: inline-block;
+}

+ 117 - 0
src/components/alarm/common/AlarmClass.vue

@@ -0,0 +1,117 @@
+<template>
+    <div>
+        <el-select :style='"width:+"+width'  v-model='selectValue' :clearable='!isAll' :placeholder='placeholder' @change="selectHandler">
+            <el-option v-for='item in selectArr' :key='item.value' :label='item.category' :value='item.category'></el-option>
+        </el-select>
+        <el-popover v-if="isAddBtn" title='添加新分类' v-model='visible' placement='top-start' width='400' trigger='click'>
+            <div>
+                <el-input placeholder='手动添加一个新分类' v-model='inputValue' clearable style='width:300px'></el-input>
+                <el-button type='primary' size='mini' @click='addNew'>确定</el-button>
+            </div>
+            <el-button type='text' slot='reference'>添加新分类</el-button>
+        </el-popover>
+        
+    </div>
+</template>
+<script>
+export default {
+    name: 'alarm-class',
+    data() {
+        return {
+            selectValue: '', //下拉绑定value
+            selectArr:[], //下拉数据的数组
+            inputValue: '', //输入框中的value
+            visible: false,
+        }
+    },
+    props: {
+        //绑定的下拉value值
+        value: {
+            default: ''
+        },
+        //下拉数据Arr
+        options: {
+            type: Array,
+            default: function() {
+                return [
+                    {
+                        category: '测试',
+                        id: 'a'
+                    }
+                ]
+            }
+        },
+        //是否显示添加按钮
+        isAddBtn: {
+            type: Boolean,
+            dafault: true
+        },
+        //宽度
+        width: {
+            type: String,
+            dafault:'200px'
+        },
+        //占位符
+        placeholder: {
+            type: String,
+            default: '请选择报警分类'
+        },
+        //是否全部
+        isAll: {
+            type: Boolean,
+            default: false
+        }
+    },
+    methods: {
+        selectHandler() {
+            if(this.selectValue == '报警分类:全部') {
+                this.$emit('change', null)
+            } else {
+                this.$emit('change', this.selectValue)
+            }            
+        },
+        addNew() {
+            if(this.inputValue == '') {
+                this.$message({
+                    message: '请填写分类信息',
+                    type: 'warning'
+                });
+                return false
+            }
+            if(this.inputValue.length > 5 ) {
+                this.$message({
+                    message: '长度不大于5位',
+                    type: 'warning'
+                });
+                return false
+            }
+            this.$emit('add',this.inputValue)
+        },
+        addAll() {
+            return {category: '报警分类:全部', id: ''}
+        }
+    },
+    watch: {
+        value: {
+            immediate: true,
+            handler(val) {
+                this.selectValue = val
+            }
+        },
+        options: {
+            handler() {
+                let arr = []
+                if(this.isAll){
+                    arr = [this.addAll(),...this.options]
+                } else {
+                    arr = this.options
+            
+                }   
+                this.selectArr = arr;             
+            }
+        }
+    }
+}
+</script>
+<style>
+</style>

+ 237 - 0
src/components/alarm/common/AlarmItem.vue

@@ -0,0 +1,237 @@
+/** 
+*props: objTypeArr:[](对象类数组)
+**/
+<template>
+<span>
+    <span class="show-box">
+        <el-button style="width: 250px;" @click="btnClick">{{btnInfo}}</el-button>
+        <i class="el-icon-circle-close-outline clear-icon" @click="clear"></i> 
+    </span>    
+<el-dialog
+title="选择报警条目"
+:visible.sync="dialogVisible"
+width="40%"
+center
+append-to-body
+>
+    <div class="alarm-item-box">
+        <div class="radio-box">
+            <el-radio-group v-model="radioValue" @change="radioChange">
+                <el-radio-button label="fixed">固定条目</el-radio-button>
+                <el-radio-button label="customed">自定义条目</el-radio-button>                
+            </el-radio-group>
+        </div>
+        <div class="table-box">
+            <el-table
+            ref="multipleTable"
+            :loading="loading"
+            :data="tableData"
+            tooltip-effect="dark"
+            style="width: 100%"
+            border
+            @selection-change="handleSelectionChange">                
+                <el-table-column
+                header-align="center"
+                prop="code"
+                label="报警条目编码">                
+                </el-table-column>
+                <el-table-column
+                header-align="center"
+                prop="name"
+                label="报警条目名称">
+                </el-table-column>
+                <el-table-column
+                type="selection"
+                width="55">
+                </el-table-column>
+            </el-table>
+        </div>
+        <div class="btn-box">
+            <el-button type="primary" @click="save">保存</el-button>
+            <el-button class="cancel-btn" @click="cancel">取消</el-button>
+        </div>
+    </div>      
+</el-dialog> 
+</span>
+</template>
+
+<script>
+import fixedApi from "@/api/alarm/alarm"
+import fixedUtils from './utils'
+import { mapGetters } from 'vuex'
+export default {
+    name: 'alarm-item',
+    data() {
+        return {
+            btnInfo: "报警条目",
+            loading: false,
+            dialogVisible: false,
+            radioValue: 'fixed', //默认固定
+            tableData:[], 
+            multipleSelection:[],
+            alarmItemArr:[]
+        }
+    },
+    props: {
+        objTypeArr: {
+            type: Array,
+            default: []
+        }
+    },
+    computed: {
+        ...mapGetters("alarm",["spaceCodeObj"])
+    },
+    methods: {
+        btnClick() {
+            this.dialogVisible = true;
+            this.btnInfo = "报警条目";
+            this.getAlarmItem()
+        },
+         // radio改变事件
+        radioChange(val) {
+            this.multipleSelection = [];
+            this.alarmItemArr = [];
+            this.getAlarmItem();
+        },
+        // 多选事件
+        handleSelectionChange(val,index) {
+            this.multipleSelection = val;
+        },
+        // 获取报警条目数据
+        getAlarmItem() {
+            this.tableData = []
+            let category = null;
+            let objType = null;
+            if(this.objTypeArr.length) {
+                if(this.objTypeArr[0]) { //第一项全部
+                    objType = this.objTypeArr[0]
+                    if(this.objTypeArr[0] == "building" || this.objTypeArr[0] == "floor") {
+                        category = null;
+                    }else if(this.objTypeArr[0] == "space") {
+                        if(this.objTypeArr.length == 1) {
+                            category = null;
+                        } else {
+                            // category = this.objTypeArr[this.objTypeArr.length - 1];
+                            let p = fixedUtils.spaceSonCode(this.objTypeArr[this.objTypeArr.length - 1], this.spaceCodeObj);
+                            category = p;
+                        }
+                    } else {
+                        category = [this.objTypeArr[this.objTypeArr.length - 1]];
+                    }
+                }
+            }
+            let params = {
+                criteria: {
+                    type: this.radioValue,   //报警条目类别,fixed 固定,customed 自定义     **必须
+                    projectId: this.radioValue == "fixed" ? null : this.projectId,  //项目id,自定义类别时不为空
+                    objType: objType,   //对象类型,building,floor,space,system,facility,为null或没有此字段时不过滤此项
+                    category: category ? {"$in":category} : null, 
+                }              
+            };  
+            fixedApi.getListData(params).then(res => {
+                if(res.result == "success"){
+                    if(res.content && res.content.length) {
+                        this.tableData = res.content;    
+                        // 判断是否已经选中
+                        this.$nextTick(() => {
+                            if(this.alarmItemArr) {
+                                let indexArr = [];
+                                this.alarmItemArr.length ? this.alarmItemArr.forEach((item, idx) =>{
+                                    this.tableData.forEach((ele, index) =>{
+                                        if(ele.code === item.code){
+                                            indexArr.push(index);
+                                        }    
+                                    })
+                                }) : null;
+                                indexArr.length ? indexArr.forEach((item) => {
+                                    this.$refs.multipleTable.toggleRowSelection(this.tableData[item], true);
+                                }) : null
+                            } else {
+                                this.$refs.multipleTable.clearSelection();
+                            }
+                        })
+                    } else {
+                        this.tableData =[];
+                    }                   
+                }
+            });
+        },
+        save() {
+            this.alarmItemArr = this.multipleSelection;           
+            let codeArr = [];
+			let str = "";
+			this.multipleSelection.forEach((item, index, arr) => {
+				codeArr.push(item.code);
+				if(index == arr.length-1) {
+					str += item.name;
+				} else {
+					str += item.name + "、";
+				}
+				
+			})
+			if(str.length > 22) {				
+				 let strTem= str.substring(0, 13);
+				 if(strTem.charAt(strTem.length-1) == "、") {
+					 strTem = strTem.substr(0, strTem.length-1)
+				 }
+				this.btnInfo = strTem.charAt(strTem.length-1) == "、" ? strTem : (strTem + "...");
+			} else {
+				this.btnInfo = str
+            };
+            if( !this.multipleSelection.length ) {
+                this.btnInfo = "报警条目";
+            }
+            this.$emit("change", this.radioValue, codeArr);
+            this.dialogVisible = false;
+        },
+        cancel() {
+            this.dialogVisible = false;
+        },
+        clear() {
+            this.multipleSelection = [];
+            this.alarmItemArr = [];
+            this.btnInfo = "报警条目";
+            this.$emit("change", this.radioValue, []);
+        }
+
+    },
+    created() {
+        if(!this.spaceCodeObj) {
+            this.$store.commit("alarm/ADD_INDEX")
+        }
+    },
+    watch:{
+        objTypeArr: {
+            handler() {
+                this.clear();
+            }
+        }
+    }
+}
+</script>
+
+<style lang="less" scoped>
+.show-box {
+    font-size: 20px;
+    .clear-icon{
+        cursor: pointer;
+    }
+}
+.alarm-item-box{    
+    .radio-box{
+        text-align: center;
+    }
+    .table-box{
+        height: 500px;
+        overflow-y: auto;
+        margin-top: 10px;
+    }
+    .btn-box{
+        margin-top:10px;
+        text-align: center;
+        .cancel-btn{
+            margin-left: 50px;
+        }
+    }
+}
+</style>

+ 121 - 0
src/components/alarm/common/CheckBox.vue

@@ -0,0 +1,121 @@
+/**
+@author:fugy--update
+@date:2018.12.12
+@info:多选组件封装
+@prop:sendMessage, sendEmail, sendEmail
+@return: {sendMessage: 0|1, sendEmail: 0|1, sendEmail: 0|1}
+**/
+<template>
+    <div class='box'>
+        <p v-if='isAll'>
+            <el-checkbox v-model='checkAll' @change='handleCheckAllChange'>全选</el-checkbox>
+        </p>
+        <p class='check-item'>
+            <el-checkbox v-model='message' @change='messageHandle'>短信</el-checkbox>
+            <el-checkbox v-model='email' @change='emailHandle'>邮件</el-checkbox>
+            <el-checkbox v-model='wechat' @change='wechatHandle'>微信</el-checkbox>
+        </p>
+    </div>
+</template>
+<script>
+export default {
+    name: 'multiple-checkbox',
+    data() {
+        return {
+            checkAll: false,
+            message: false,
+            email: false,
+            wechat: false,
+            flag: false, //防止回传
+        }
+    },
+    props: {
+        sendMessage: {
+            require: true
+        },
+        sendEmail: {
+            require: true
+        },
+        sendWechat: {
+            require: true
+        },
+        define: {
+            default: null
+        },
+        isAll: {
+            type: Boolean,
+            default: true
+        }
+    },
+    methods: {
+        //全部
+        handleCheckAllChange(val) {
+            if (val) {
+                this.message = true
+                this.email = true
+                this.wechat = true
+            } else {
+                this.message = false
+                this.email = false
+                this.wechat = false
+            }
+            this.emit()
+        },
+        messageHandle() {
+            this.handleIsAll()
+        },
+        emailHandle() {
+            this.handleIsAll()
+        },
+        wechatHandle() {
+            this.handleIsAll()
+        },
+        //是否全部
+        handleIsAll() {
+            if (this.message && this.email && this.wechat) {
+                this.checkAll = true
+            } else {
+                this.checkAll = false
+            }
+            this.emit()
+        },
+        //回传
+        emit() {
+            let obj = {
+                sendMessage: this.message ? 1 : 0,
+                sendEmail: this.email ? 1 : 0,
+                sendWechat: this.wechat ? 1 : 0
+            }
+            if(this.flag) {
+                this.$emit('change', obj, this.define)
+            }
+            
+        },
+        DataHandle() {
+            this.message = this.sendMessage ? true : false;
+            this.email = this.sendEmail ? true : false;
+            this.wechat = this.sendWechat ? true: false;
+            this.handleIsAll();
+            this.flag = true
+        }
+    },
+    watch: {
+        sendMessage: {
+            immediate: true,
+            handler(val) {
+                this.DataHandle()
+            }
+            
+        }
+    }
+}
+</script>
+<style lang="less" scoped>
+.box {
+    display: flex;
+    .check-item {
+        margin-left: 20px;
+    }
+}
+</style>
+

+ 153 - 0
src/components/alarm/common/ObjLocation.vue

@@ -0,0 +1,153 @@
+/**
+@author:guoguo
+@date:2018.11.30
+@info: 对象位置
+**/
+<template>
+<el-cascader
+    style="width: 249px;"
+    placeholder="对象位置"
+    :options="options"
+    :value="value"
+    @active-item-change="handleItemChange"
+    @change="change"
+    :props="configObj"
+  ></el-cascader>
+</template>
+<script>
+export default {
+    name: "obj-location",
+    data() {
+        return {
+            options: [{
+                value: 'zhinan',
+                label: '指南',
+                children:[]
+            }],
+            configObj: {
+                label: "name",
+                value: "id",
+            },
+            value: []
+        }
+    },
+    props:{
+        projectId: {
+            type: String,
+            required: true
+        }
+    },
+    methods: {
+        //id=[建筑id|楼层Id]
+        getList(type, id, BdId) {
+            let params = {
+                criteria:{
+                    id: id || undefined,
+                    type: [type]
+                }
+            }
+            this.postJson(`/server/dataplatform/object/subquery?projectId=${this.projectId}`, params, resp => {
+                if (resp.Result === 'success'  && resp.Count) {
+                    if(type == "Bd") {
+                        this.handleBd(resp.Content);
+                    } else if ( type == "Fl") {
+                        this.handleFl(resp.Content, id)
+                    } else {
+                        this.handleSp(resp.Content, BdId, id)
+                    }                   
+                } else { //如果是室外
+                    if(type == "Sp") {
+                        let arr = [this.allItem()];
+                        this.options.forEach(item => {
+                            item.children?item.children.forEach(ele => {
+                                if(ele.id == id) {
+                                    ele.children = arr;
+                                }
+                            }):""
+                        })
+                    }
+
+                }
+            })            
+        },
+        handleBd(content) {
+            let arr = [];
+            content.forEach(item=> {
+                let obj = {
+                    id: item.id,
+                    name: item.infos.BuildLocalName,
+                    children: []
+                }
+                arr.push(obj);
+            })
+            this.options = arr;
+            this.options.unshift(this.allBdItem())
+        },
+        handleFl(content, id) {
+            let arr = [];
+            content.forEach(item=> {
+                let obj = {
+                    id: item.id,
+                    name: item.infos.FloorLocalName,
+                    children:[]
+                }
+                arr.push(obj);
+            })
+            this.options.forEach(item => {
+                if(item.id == id) {
+                    item.children = arr
+                }
+            })
+        },
+        handleSp(content, BdId, flId) {
+            let arr = []
+            content.forEach(item => {
+                let obj = {
+                    id: item.id,
+                    name: item.infos.RoomLocalName,
+                }
+                arr.push(obj);
+            });
+            arr.unshift(this.allItem())
+             this.options.forEach(item => {
+                if(item.id == BdId) {
+                    item.children.forEach(ele => {
+                        if(ele.id == flId) {
+                            ele.children = arr;
+                        }
+                    })
+                }
+            })
+        },
+        allBdItem() {
+            return {id: "", name: "对象位置:全部"}
+        },
+        allItem() {
+            return {id: "" , name: "全部"}
+        },
+        handleItemChange(val) {
+            if(val.length == 1 && val[0]) {
+                this.getList( "Fl", val[0]);
+            } else if(val.length == 2){
+                this.getList( "Sp", val[1], val[0]);
+            }
+        },
+        change(val) {
+            let id = val[val.length - 1];
+            if(val.length > 1 && !val[val.length-1]){
+                id = val[val.length-2];
+            }
+            this.$emit("change", id)
+        }
+    },
+    watch: {
+        projectId: {
+            immediate: true,
+            handler(val){
+                this.getList("Bd")
+            }
+        }
+    }
+
+}
+</script>

+ 313 - 0
src/components/alarm/common/ObjType.vue

@@ -0,0 +1,313 @@
+/**
+@author:guoguo
+@date:2018.11.30
+@info: 对象类型
+**/
+<template>
+    <el-form class='form' :model='ruleForm' ref='ruleForm'>
+        <el-form-item
+            class='select-left'
+            prop='selectValue'
+            :rules='hasVerify ? [{ required: true, message: "请选择对象类", trigger: "change" }] : null'
+        >
+            <!-- 下拉框 -->
+            <el-select
+                style='width:150px;'
+                v-model='ruleForm.selectValue'
+                placeholder='对象类'
+                @change='selectChange'
+                clearable
+                :disabled="disabled"
+            >
+                <el-option v-for='item in selectOpt' :key='item.value' :label='item.label' :value='item.value'></el-option>
+            </el-select>
+        </el-form-item>
+        <el-form-item
+            prop='spaceValue'
+            v-show='ruleForm.selectValue == "space"'
+        >
+            <!-- 空间级联 -->
+            <el-cascader
+                v-show="!isShowSpace"
+                clearable
+                ref='space'
+                change-on-select
+                :options='spaceOptions'
+                v-model='ruleForm.spaceValue'
+                :props='spaceProps'
+                @change='spaceChange'
+                placeholder="请选择空间类"
+                :disabled="disabled"
+                :style='"width:" + width'
+            ></el-cascader>
+        </el-form-item>
+        <!-- 系统 -->
+        <el-form-item
+            prop='sysValue'
+            v-show='ruleForm.selectValue == "system"'
+            :rules=' hasVerify ? [{ required: true, message: "请选择系统类",  trigger: ["blur","change" ] }] : null'
+        >
+            <el-cascader ref='sys' :options='sysOptions' v-model='ruleForm.sysValue' @change='sysChange' placeholder="请选择系统类" :disabled="disabled" clearable :style='"width:" + width'></el-cascader>
+        </el-form-item>
+        <!-- 设备 -->
+        <el-form-item
+            prop='devValue'
+            v-show='ruleForm.selectValue == "facility"'
+            :rules=' hasVerify ? [{ required: true, message: "请选择设备类",  trigger: ["blur","change" ] }] : null'
+        >
+            <el-cascader ref='dev' :options='devOptions' v-model='ruleForm.devValue' @change='devChange' placeholder="请选择设备类" :disabled="disabled" clearable :style='"width:" + width'></el-cascader>
+        </el-form-item>
+        <!-- <el-button @click='btn'></el-button> -->
+    </el-form>
+</template>
+<script>
+import fixApi from '@/api/alarm/alarm'
+import utils from './utils'
+import { mapGetters } from 'vuex'
+export default {
+    name: 'obj-type',
+    data() {
+        return {
+            isShowSpace: false,
+            ruleForm: {
+                selectValue: '',
+                spaceValue: [],
+                sysValue: [],
+                devValue: []
+            },
+            /****************左侧下拉***************** */
+            selectOpt: [
+                {
+                    value: 'building',
+                    label: '建筑'
+                },
+                {
+                    value: 'floor',
+                    label: '楼层'
+                },
+                {
+                    value: 'space',
+                    label: '空间'
+                },
+                {
+                    value: 'system',
+                    label: '系统类'
+                },
+                {
+                    value: 'facility',
+                    label: '设备类'
+                }
+            ],
+            selectValue: '',
+            /*****************右侧级联******************* */
+            // 空间
+            spaceOptions: [],
+            spaceValue: [],
+            spaceProps: {
+                value: 'code',
+                label: 'name',
+                children: 'content'
+            },
+            // 系统
+            sysOptions: [],
+            sysValue: [],
+            // 设备
+            devOptions: [],
+            devValue: [],
+            //Dict
+            spaceDict: {},
+            spaceNameDict: {}
+        }
+    },
+    props: {
+        objType: {},
+        hasVerify: {
+            default: false
+        },
+        disabled: {
+            default: false
+        },
+        width: {
+            default: "200px",
+            type: String
+        }
+    },
+    computed: {
+        ...mapGetters('alarm', ['index'])
+    },
+    methods: {
+        // 下拉框
+        selectChange(val) {
+            this.isShowSpace = false
+            this.ruleForm.spaceValue = []
+            this.ruleForm.sysValue = []
+            this.ruleForm.devValue = []
+            if (val == 'building' || val == 'floor') {
+            } else if (val == 'space') {
+                this.$refs.space.handleClick()
+            } else if (val == 'system') {
+                this.$refs.sys.handleClick()
+            } else if (val == 'facility') {
+                this.$refs.dev.handleClick()
+            } else {
+            }
+            if (val) {
+                this.$emit('change', [val])
+            } else {
+                this.$emit('change', [])
+            }
+        },
+        spaceChange(val) {
+            let arr = [this.ruleForm.selectValue, ...val]
+            this.$emit('change', arr)
+        },
+        sysChange(val) {
+            let arr = [this.ruleForm.selectValue, ...val]
+            this.$emit('change', arr)
+        },
+        devChange(val) {
+            let arr = [this.ruleForm.selectValue, ...val]
+            this.$emit('change', arr)
+        },
+        change(val) {},
+        async getSpace() {
+            // 获取空间的级联
+            let spaceRes = await fixApi.getObjTypeSpace()
+            this.spaceOptions = spaceRes
+            this.spaceDictHandle(spaceRes)
+            // 获取name的dict
+            this.spaceNameDictHandle(spaceRes)
+            // 获取code的子集
+            this.spaceCodeDictHandle(spaceRes)
+        },
+        spaceNameDictHandle(spaceRes) {
+            if (spaceRes.length) {
+                let obj = {}
+                spaceRes.forEach(item => {
+                    obj[item.code] = item.name
+                    if (item.content) {
+                        item.content.forEach(item1 => {
+                            obj[item1.code] = item1.name
+                            if (item1.content) {
+                                item1.content.forEach(item2 => {
+                                    obj[item2.code] = item2.name
+                                })
+                            }
+                        })
+                    }
+                })
+                this.$store.commit('alarm/SPACE_NAME_OBJ', obj)
+            }
+        },
+        spaceCodeDictHandle(spaceRes) {
+            let res = utils.getSpaceSonCode(spaceRes)
+            this.$store.commit('alarm/SPACE_CODE_OBJ', res)
+        },
+        spaceDictHandle(spaceRes) {
+            // 做dict   spaceDict
+            let obj = {}
+            if (spaceRes.length) {
+                // let cur = spaceRes
+                spaceRes.forEach(item => {
+                    obj[item.code] = [item.code]
+                    if (item.content) {
+                        item.content.forEach(item1 => {
+                            obj[item1.code] = [item.code, item1.code]
+                            if (item1.content) {
+                                item1.content.forEach(item2 => {
+                                    obj[item2.code] = [item.code, item1.code, item2.code]
+                                })
+                            }
+                        })
+                    }
+                })
+            }
+            this.spaceDict = obj
+        },
+        async getSysDev() {
+            // 获取系统和设备的级联
+            let sysDevRes = await fixApi.getObjectType()
+            this.sysOptions = sysDevRes.sysArr
+            this.devOptions = sysDevRes.devArr
+        },
+        async init() {
+            this.getSpace()
+            this.getSysDev()
+        },
+        // 校验,return 【true or false】
+        verify() {
+            let flag = false
+            this.$refs['ruleForm'].validate(valid => {})
+            if (this.ruleForm.selectValue) {
+                let selectValue = this.ruleForm.selectValue
+                if (selectValue == 'building' || selectValue == 'floor') {
+                    flag = true
+                } else if (selectValue == 'space') {
+                    flag = true
+                } else if (selectValue == 'system') {
+                    if (this.ruleForm.sysValue.length) {
+                        flag = true
+                    } else {
+                        flag = false
+                    }
+                } else if (selectValue == 'facility') {
+                    if (this.ruleForm.devValue.length) {
+                        flag = true
+                    } else {
+                        flag = false
+                    }
+                }
+            } else {
+                flag = false
+            }
+            return flag
+        },
+        btn() {
+            this.verify()
+        },
+        // 处理回填值
+        async handleObjType() {
+            await this.getSpace()
+            if (this.objType.length) {
+                this.ruleForm.selectValue = this.objType[0]
+                let arr = JSON.parse(JSON.stringify(this.objType))
+                if (this.objType[0] == 'space') {
+                    if(this.objType.length == 1) {
+                        this.isShowSpace = true
+                    }else {
+                        this.isShowSpace = false
+                        this.ruleForm.spaceValue = this.spaceDict[arr[1]]
+                    }                    
+                } else if (this.objType[0] == 'system') {
+                    this.ruleForm.sysValue = arr.splice(1)
+                } else if (this.objType[0] == 'facility') {
+                    this.ruleForm.devValue = arr.splice(1)
+                }
+            }
+        }
+    },
+    created() {
+        this.init()
+    },
+    watch: {
+        objType: {
+            handler() {
+                this.handleObjType()
+            }
+        },
+        index : {
+            handler() {
+                this.init()
+            }
+        }
+    }
+}
+</script>
+<style scoped lang="less">
+.form {
+    display: flex;
+    .select-left {
+        margin-right: 5px;
+    }
+}
+</style>

+ 45 - 0
src/components/alarm/common/utils.js

@@ -0,0 +1,45 @@
+var spaceObj = {}
+var spaceSonCode = []
+const api = {    
+    // 1、获取指定空间code下的所有子code,返回{code: code-obj}
+    getSpaceSonCode(arr) {
+        if(arr.length) {
+            spaceObj = {};
+            api.getSpaceData(arr);
+        }
+        return spaceObj
+    },
+    // 获取space的obj数据
+    getSpaceData(arr) {
+        if(arr.length) {
+            arr.forEach((item) => {
+                spaceObj[item.code] = item;
+                if (item.content) {
+                    api.getSpaceData(item.content);
+                }
+            })
+        }
+    },
+    // 2、空间对象类根据指定的code值返回该值下所有的子集code
+    spaceSonCode(code, data) {
+        let obj = data[code];
+        spaceSonCode = []
+        spaceSonCode.push(obj.code)
+        if (obj.content) {
+            api.getSonAllCode(obj)
+        }
+        return spaceSonCode
+    },
+    // 获取子集的所有code值
+    getSonAllCode(obj) {
+        if (obj.content) {
+            obj.content.forEach(item => {
+                api.getSonAllCode(item)
+            })
+        } else {
+            spaceSonCode.push(obj.code);
+        }
+    }
+
+};
+export default api;

+ 627 - 0
src/components/business_space/business/handsontable.vue

@@ -0,0 +1,627 @@
+<template>
+    <div id="handsontable" v-loading="isLoading">
+        <div style="overflow:hidden;margin-bottom:10px;margin-right:20px;height:50px;">
+            <el-button @click="undo" size="small" style="float:right;margin-top:5px;margin-right:10px;" icon="iconfont icon-undo" circle></el-button>
+            <el-button @click="reset" size="small" style="float:right;margin-top:5px;margin-right:10px;" icon="iconfont icon-shuaxin" circle></el-button>
+            <el-button @click="addSp" size="small" style="float:right;margin-top:5px;margin-right:10px;" icon="iconfont icon-tianjia" circle></el-button>
+        </div>
+        <div v-show="main &&main.length" :id="id"></div>
+        <div class="no-data" v-show="!main || !main.length">暂无数据</div>
+        <!-- <add-business :buildMess="buildMess" :dialog="dialog"></add-business> -->
+        <my-pagination v-show="main &&main.length" :page="page" @change="pageChange"></my-pagination>
+    </div>
+</template>
+<script>
+     // 引用handsontable插件
+     // import "@/assets/js/chosen.jquery.min";
+     import tools from "@/utils/scan/tools";
+     // import "@/assets/js/handsontable-chosen-editor";
+     import myPagination from "@/components/common/myPagination";
+     import addBusiness from "@/components/business_space/dialogs/addDialog/businessDialog"
+     import showTools from "@/utils/handsontable/notShow"
+     import Handsontable from "handsontable-pro"
+     import 'handsontable-pro/dist/handsontable.full.css'
+     import zhCN from 'handsontable-pro/languages/zh-CN';
+     console.log(Handsontable,"Handsontable")
+     import {
+         updateBusiness,
+         createBusiness,
+         createBusinessList,
+         delManySpace,
+         isSpaceBuss
+     } from "@/api/scan/request";
+     import {
+         mapGetters,
+         mapActions
+     } from "vuex";
+     export default {
+         props: {
+             id: {
+                 type: String
+             }
+         },
+         components: {
+             myPagination,
+             addBusiness
+         },
+         computed: {
+             ...mapGetters("peojMess", [
+                 "projectId",
+                 "secret",
+                 "userId"
+             ])
+         },
+         data() {
+             return {
+                 dialog: {
+                     addDevice: false
+                 },
+                 headers: null,
+                 main: null,
+                 hot: null,
+                 tableData: [],
+                 isLoading: true,
+                 allData: null,
+                 page: {
+                     size: 50,
+                     sizes: [10, 30, 50, 100, 150, 200],
+                     total: 2,
+                     currentPage: 1
+                 },
+                 param: {
+                     ProjId: this.projectId, //项目id
+                     UserId: this.userId, //用户id
+                     secret: this.secret
+                 },
+                 buildMess: null,
+                 filtersArr: [], //表格数据被过滤后的下标
+                 copyMain: null, //深拷贝数组
+                 activeType: {}
+             };
+         },
+         created() {
+             this.param.ProjId = this.projectId
+             this.param.UserId = this.userId
+             this.param.secret = this.secret
+         },
+         mounted() {},
+         methods: {
+             //获取表头
+             getHeader(headers, activeTabType) {
+                 this.headers = headers.map(item => {
+                     let dataSoucse = item.DataSource
+                     console.log(dataSoucse)
+                     if (!dataSoucse) {
+                         return item
+                     }
+                     dataSoucse.map(keys => {
+                         if (!!keys.Content && keys.Content.length) {
+                             keys.Content.map(child => {
+                                 dataSoucse.push({
+                                     Code: child.Code,
+                                     Name: child.Name,
+                                 })
+                                 if (!!child.Content && child.Content.length) {
+                                     child.Content.map(node => {
+                                         dataSoucse.push({
+                                             Code: node.Code,
+                                             Name: node.Name,
+                                         })
+                                     })
+                                 }
+                             })
+                         }
+                     })
+                     return item
+                 });
+                 console.log(this.headers, "headers")
+                 this.activeType = activeTabType
+             },
+             getData(buildMess) {
+                 if (!!buildMess) {
+                     this.buildMess = buildMess;
+                 }
+                 let param = {
+                     id: this.buildMess.code,
+                     type: ["Sp"],
+                     data: {
+                         customInfo: true,
+                         criteria: {
+                             floor: this.buildMess.code, // 必填, 指定楼层id
+                             business_type: this.activeType.code, // 必填, 楼层下某个类型的业务空间PowerSupplyZone, 默认是空字符串
+                             rel_type: this.activeType.rel_type, // 必填, 指定业务类型的关系类型(参考数据平台),
+                             // 数字型字符串, 例如: 通用是99
+                         }
+                     },
+                     ProjId: this.param.ProjId,
+                     secret: this.param.secret
+                 };
+                 isSpaceBuss(param).then(res => {
+                     if (res.data.Result == "success") {
+                         this.allData = res.data.Content;
+                         this.page.total = res.data.Count;
+                         this.getMain(
+                             this.pagination(this.page.currentPage, this.page.size, this.allData)
+                         );
+                     } else {
+                         this.$message.error("请求错误");
+                     }
+                 });
+             },
+             //获取表格主体内容
+             getMain(main) {
+                 this.main = main;
+                 if (this.main && this.main.length && this.main[0].id) {
+                     this.copyMain = tools.deepCopy(this.main);
+                 }
+                 this.isLoading = true;
+                 if (!!this.hot) {
+                     this.hot.destroy();
+                     this.hot = null;
+                 }
+                 this.$nextTick(() => {
+                     this.initHot();
+                 });
+             },
+             //初始化handsontable组件
+             initHot() {
+                 let id = this.id
+                 console.log(id)
+                 var container = document.getElementById(this.id);
+                 let winHeight = document.documentElement.clientHeight;
+                 this.hot = new Handsontable(container, {
+                     data: this.main,
+                     colHeaders: this.delHeader(this.headers), //表头文案
+                     columns: this.getType(this.headers), //数据显示格式
+                     filters: true,
+                     maxRows: this.main.length,
+                     height: winHeight - 100 - 50 - 80,
+                     columnSorting: true, //添加排序
+                     sortIndicator: true, //添加排序
+                     renderAllRows: true,
+                     autoColumnSize: true,
+                     language: "zh-CN",
+                     manualColumnResize: true,
+                     manualColumnMove: true,
+                     dropdownMenu: [
+                         "filter_by_condition",
+                         "filter_by_value",
+                         "filter_action_bar"
+                     ],
+                     contextMenu: {
+                         items: {
+                             remove_row: {
+                                 name: "删除该业务空间"
+                             }
+                         }
+                     },
+                     // 事件
+                     afterChange: this.tdChange, //修改后
+                     afterFilter: this.trimmedRows, //排序前
+                     beforeRemoveRow: this.romoveFm, //右键删除
+                     afterOnCellMouseDown: this.eventClick //鼠标点击
+                 });
+                 let pro = document.getElementById("hot-display-license-info");
+                 if (!!pro) {
+                     pro.parentNode.removeChild(pro);
+                 }
+                 this.isLoading = false;
+             },
+             //表格中的查看详情
+             eventClick(el, rowArr) {
+                 let filter = this.filtersArr;
+                 //被筛选过后的数组
+                 let trimmedArr = this.trimmedRows();
+                 //是否启用了排序
+                 let isSort = this.hot.getPlugin("columnSorting").isSorted();
+                 // debugger
+                 if (trimmedArr.length && isSort) {
+                     let sortArr = this.myHotArr.getPlugin("columnSorting").rowsMapper
+                         .__arrayMap;
+                     let infos = this.main[trimmedArr[sortArr[rowArr.row]]];
+                     this.getInfors(infos, rowArr);
+                 } else if (isSort) {
+                     //排序后的数组
+                     let sortArr = this.hot.getPlugin("columnSorting").rowsMapper.__arrayMap;
+                     let infos = this.main[sortArr[rowArr.row]];
+                     this.getInfors(infos, rowArr);
+                 } else if (trimmedArr.length) {
+                     let infos = this.main[trimmedArr[rowArr.row]];
+                     this.getInfors(infos, rowArr);
+                 } else {
+                     let infos = this.main[rowArr.row];
+                     this.getInfors(infos, rowArr);
+                 }
+             },
+             getInfors(infos, row) {
+                 if (row.col == 1) {
+                     this.$emit("lookEqu", infos);
+                 } else {
+                     return false;
+                 }
+             },
+             //表格发生更改
+             tdChange(changeData, source) {
+                 let filter = this.filtersArr;
+                 if (changeData) {
+                     let param = this.getParam(changeData, source);
+                     let data = [];
+                     for (let i = 0; i < param.length; i++) {
+                         if (!!param[i].id) {
+                             data.push(param[i]);
+                         } else {
+                             this.createSq(param[i], changeData);
+                         }
+                     }
+                     if (data && data.length) {
+                         this.updateBusiness(data, changeData);
+                     }
+                 }
+             },
+             //右键删除
+             romoveFm(index, amout, physicalRows, source) {
+                 if (physicalRows && physicalRows.length) {
+                     let items = [],
+                         text = null;
+                     physicalRows.map(item => {
+                         text = text ?
+                             this.copyMain[item].infos.RoomName + "、" + text :
+                             this.copyMain[item].infos.RoomName;
+                         items.push(this.copyMain[item].id);
+                     });
+                     this.$confirm("您正在删除" + text + ", 是否继续?", "提示", {
+                             confirmButtonText: "确定",
+                             cancelButtonText: "取消",
+                             type: "warning"
+                         })
+                         .then(() => {
+                             this.delBusiness(items);
+                         })
+                         .catch(() => {
+                             this.hot.loadData(tools.deepCopy(this.copyMain));
+                         });
+                 }
+             },
+             delBusiness(items) {
+                 let param = {
+                     ProjId: this.param.ProjId,
+                     secret: this.param.secret,
+                     data: {
+                         criteria: {
+                             ids: items
+                         }
+                     }
+                 };
+                 delManySpace(param)
+                     .then(res => {
+                         if (res.data.Result == "success") {
+                             this.$message.success("删除成功");
+                             this.getData();
+                         } else {
+                             this.$message.error("删除失败");
+                         }
+                     })
+                     .catch(() => {
+                         this.$message.error("请求发生错误");
+                     });
+             },
+             // 新建
+             createSq(param, change) {
+                 let seedData = {
+                     building_id: this.buildMess.buildCode,
+                     floor_id: this.buildMess.code,
+                     business_type: this.activeType.code == "GeneralZone" ? null : this.activeType.code,
+                     infos: {}
+                 };
+                 for (let i = 0; i < change.length; i++) {
+                     let arr = change[i][1].split(".");
+                     seedData[arr[0]][arr[1]] = [{
+                         value: change[i][3] != "" ? change[i][3] : null
+                     }];
+                 }
+                 this.createSpace(seedData);
+             },
+             // 修改
+             updateBusiness(data, change) {
+                 let param = {
+                     ProjId: this.param.ProjId,
+                     secret: this.param.secret,
+                     data: {
+                         criterias: []
+                     }
+                 };
+                 data.map((item, index) => {
+                     console.log(change[index][1]);
+                     let arr = change[index][1].split(".");
+                     let info = {};
+                     info[arr[1]] = [{
+                         value: change[index][3] != "" ? change[index][3] : null
+                     }];
+                     param.data.criterias.push({
+                         id: item.id,
+                         infos: info
+                     });
+                 });
+                 updateBusiness(param).then(res => {
+                     console.log(res);
+                 });
+             },
+             /**    请求部分 ------------------------------------ */
+             createSpace(data) {
+                 let param = {
+                     ProjId: this.param.ProjId,
+                     secret: this.param.secret,
+                     data: {
+                         criterias: [data]
+                     }
+                 };
+                 createBusinessList(param).then(res => {
+                     if (res.data.Result == "success") {
+                         console.log(this.main);
+                         this.main.map(item => {
+                             if (!item.id) {
+                                 item.id = res.data.id;
+                                 item.infos.RoomID = res.data.id;
+                                 item.infos.RoomName = res.data.RoomName;
+                             }
+                         });
+                         this.hot.loadData(this.main);
+                         this.reset()
+                     } else {
+                         this.$message.error("创建发生错误:" + res.data.ResultMsg);
+                     }
+                 });
+             },
+             /**
+              * 获取被排序后的数组
+              *
+              * @param changeData 发生改变的数据
+              * @param source     数组
+              *
+              * @return array     经过排序后或者经过搜索后的数组
+              */
+             getParam(changeData, source) {
+                 let param = "";
+                 //被筛选过后的数组
+                 let trimmedArr = this.trimmedRows();
+                 //是否启用了排序
+                 let isSort = this.hot.getPlugin("columnSorting").isSorted();
+                 if (trimmedArr.length && isSort) {
+                     //排序后的数组
+                     let sortArr = this.hot.getPlugin("columnSorting").rowsMapper.__arrayMap;
+                     param = changeData.map(item => {
+                         return this.hot.getSourceDataAtRow(trimmedArr[sortArr[item[0]]]);
+                     });
+                 } else if (isSort) {
+                     //排序后的数组
+                     let sortArr = this.hot.getPlugin("columnSorting").rowsMapper.__arrayMap;
+                     param = changeData.map(item => {
+                         return this.hot.getSourceDataAtRow(sortArr[item[0]]);
+                     });
+                 } else if (trimmedArr.length) {
+                     param = changeData.map(item => {
+                         return this.hot.getSourceDataAtRow(trimmedArr[item[0]]);
+                     });
+                 } else {
+                     param = changeData.map(item => {
+                         return this.hot.getSourceDataAtRow(item[0]);
+                     });
+                 }
+                 return param;
+             },
+             //获取被筛选掉的行号
+             trimmedRows() {
+                 // var plugin = hot.getPlugin('trimRows').trimmedRows;//获取被筛选掉的行号
+                 var plugin = this.hot.getPlugin("trimRows").trimmedRows;
+                 let dataLength = this.main.length;
+                 let dataArr = new Array();
+                 for (let i = 0; i < dataLength; i++) {
+                     dataArr.push(i);
+                 }
+                 if (plugin.length <= 0) {
+                     dataArr = undefined;
+                 } else {
+                     dataArr = this.array_diff(dataArr, plugin);
+                 }
+                 this.filtersArr = dataArr;
+                 return dataArr || [];
+                 // var DataArray = new Array();
+                 // for (var i = 0; i < plugin.length; i++) {
+                 //     // 通过行号获取数据
+                 //     DataArray.push(this.hot.getSourceDataAtRow(plugin[i]));
+                 // }
+             },
+             //去除数组中相同的元素
+             array_diff(a, b) {
+                 for (var i = 0; i < b.length; i++) {
+                     for (var j = 0; j < a.length; j++) {
+                         if (a[j] == b[i]) {
+                             a.splice(j, 1);
+                             j = j - 1;
+                         }
+                     }
+                 }
+                 return a;
+             },
+             /**
+              * 表头文案处理函数
+              * @param list header数组数据
+              *
+              * @return 处理好的文案
+              */
+             delHeader(list) {
+                 let arr = tools.copyArr(list)
+                 // 如果不是只读状态,添加四大厂商选择
+                 let readArr = ["A1", "A2", "B1", "C5", "D1", "D2", "X", "L", "L1", "L2"]
+                 if (!this.onlyRead) {
+                     arr = showTools.arrToArr(arr)
+                     readArr.push("Own")
+                 }
+                 let data = showTools.changeHeader(arr, readArr, false, false, true, true, true)
+                 data.unshift("是否关联平面图", "与空间相关联的设备");
+                 console.log("header", data)
+                 return data;
+             },
+             /**
+              * 表头数据处理函数
+              * @param arr header数组数据
+              *
+              * @return 处理好的数据格式
+              */
+             getType(list) {
+                 let arr = tools.copyArr(list)
+                 let data = showTools.showTypes(arr, false, false, true, true, ["F1", "F2"])
+                 data.unshift({
+                     data: "hasSi",
+                     renderer: this.myRenderer,
+                     readOnly: true
+                 }, {
+                     data: "point",
+                     renderer: this.facilityRender,
+                     readOnly: true
+                 });
+                 data = data.filter(item => item);
+                 console.log(data)
+                 return data;
+             },
+             myRenderer(instance, td, row, col, prop, value, cellProperties) {
+                 if (value) {
+                     td.innerHTML = "已关联";
+                 } else {
+                     td.innerHTML = "未关联";
+                 }
+                 return td;
+             },
+             facilityRender(instance, td, row, col, prop, value, cellProperties) {
+                 td.style.color = "#409EFF";
+                 td.style.cursor = "pointer";
+                 td.innerHTML = "点击查看";
+                 return td;
+             },
+             //下拉插件修改
+             customDropdownRenderer(
+                 instance,
+                 td,
+                 row,
+                 col,
+                 prop,
+                 value,
+                 cellProperties
+             ) {
+                 var selectedId;
+                 var optionsList = cellProperties.chosenOptions.data;
+                 if (
+                     typeof optionsList === "undefined" ||
+                     typeof optionsList.length === "undefined" ||
+                     !optionsList.length
+                 ) {
+                     Handsontable.renderers.TextRenderer(
+                         instance,
+                         td,
+                         row,
+                         col,
+                         prop,
+                         value,
+                         cellProperties
+                     );
+                     return td;
+                 }
+                 var values = (value + "").split(",");
+                 value = [];
+                 for (var index = 0; index < optionsList.length; index++) {
+                     if (optionsList[index].content && optionsList[index].content.length) {
+                         for (let i = 0; i < optionsList[index].content.length; i++) {
+                             if (
+                                 optionsList[index].content[i] &&
+                                 optionsList[index].content[i].length
+                             ) {
+                                 for (let j = 0; j < optionsList[index].content[i].length; j++) {
+                                     if (
+                                         values.indexOf(
+                                             optionsList[index].content[i].content[j].code + ""
+                                         ) > -1
+                                     ) {
+                                         selectedId = optionsList[index].content[i].content[j].code;
+                                         value.push(optionsList[index].content[i].content[j].name);
+                                     }
+                                 }
+                             } else {
+                                 if (
+                                     values.indexOf(optionsList[index].content[i].code + "") > -1
+                                 ) {
+                                     selectedId = optionsList[index].content[i].code;
+                                     value.push(optionsList[index].content[i].name);
+                                 }
+                             }
+                         }
+                     } else {
+                         if (values.indexOf(optionsList[index].code + "") > -1) {
+                             selectedId = optionsList[index].code;
+                             value.push(optionsList[index].name);
+                         }
+                     }
+                 }
+                 value = value.join(", ");
+                 Handsontable.renderers.TextRenderer(
+                     instance,
+                     td,
+                     row,
+                     col,
+                     prop,
+                     value,
+                     cellProperties
+                 );
+                 return td;
+             },
+             /**  页面中的按钮事件--------------------------- */
+             addSp() {
+                 //   this.dialog.addDevice = true
+                 //   this.hot.downloadFile()
+                 //   this.hot.getPlugin('exportFile').downloadFile("csv", { columnHeaders: true, exportHiddenRows: true, exportHiddenColumns: true, rowHeaders: true })
+                 if (this.main && this.main.length && !!this.main[0].id) {
+                     this.hot.destroy();
+                     this.hot = null;
+                     this.main.unshift([]);
+                     this.getMain(this.main);
+                 } else {
+                     if (this.main && this.main.length) {
+                         this.$message.error("请添加完成后继续添加");
+                     } else {
+                         this.main.unshift([]);
+                         this.getMain(this.main);
+                     }
+                 }
+             },
+             //刷新
+             reset() {
+                 this.getData();
+             },
+             //撤回操作
+             undo() {
+                 this.hot.undo();
+             },
+             /**  分页事件------------------------ */
+             pageChange() {
+                 this.getMain(
+                     this.pagination(this.page.currentPage, this.page.size, this.allData)
+                 );
+             },
+             pagination(pageNo, pageSize, array) {
+                 let offset = (pageNo - 1) * pageSize;
+                 return offset + pageSize >= array.length ?
+                     array.slice(offset, array.length) :
+                     array.slice(offset, offset + pageSize);
+             }
+         }
+     };
+</script>
+<style lang="less" scoped>
+    #handsontable {
+        .no-data {
+            height: 150px;
+            line-height: 150px;
+            text-align: center;
+            color: gray;
+        }
+    }
+</style>

+ 766 - 0
src/components/business_space/dialogs/addDialog/businessDialog.vue

@@ -0,0 +1,766 @@
+<!--
+    上传文件的弹窗
+-->
+
+<template>
+  <div v-show="dialog.addDevice">
+    <el-dialog
+      :title="title"
+      :center="isCenter"
+      :visible.sync="dialog.addDevice"
+      :width="width"
+      :fullscreen="isFull"
+    >
+      <div id="addDevice">
+        <div v-show="!isCenter">
+          <my-cascader @change="changeCader"></my-cascader>
+        </div>
+        <div v-if="isCenter">
+          <div>
+            <div class="hanson-bar" style="overflow:hidden;">
+              <el-button
+                style="float:right;"
+                size="small"
+                @click="undo"
+                icon="iconfont icon-undo"
+              >撤销</el-button>
+              <el-button
+                style="float:right;"
+                size="small"
+                @click="reset"
+                icon="iconfont icon-shuaxin"
+              >刷新</el-button>
+              <el-button style="float:right;" size="small" @click="addMain">增加</el-button>
+              <el-input
+                v-model="addNum"
+                placeholder="增加个数"
+                style="width:90px;float:right;"
+                size="small"
+              ></el-input>
+            </div>
+            <div v-show="main && main.length" id="myHandson" ref="myHandson"></div>
+          </div>
+        </div>
+      </div>
+      <span slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="addSpace">添加业务空间</el-button>
+      </span>
+    </el-dialog>
+    <qrcode :dialog="myDialog" :addBody="true" ref="qrcode"></qrcode>
+    <firm ref="firm" :dialog="myDialog"></firm>
+    <supply-dialog ref="supply" :dialog="myDialog"></supply-dialog>
+    <supplier-dialog ref="supplier" @changeSupplier="supplierChange" :dialog="myDialog"></supplier-dialog>
+    <guarantee-dialog ref="guarantee" :dialog="myDialog"></guarantee-dialog>
+    <upload-files-dialog
+      ref="upload"
+      @changeFile="fileChange"
+      :keysArr="filesArr"
+      :dialog="myDialog"
+    ></upload-files-dialog>
+    <upload-img-dialog @changeFile="imgChange" :keysArr="imgsArr" :dialog="myDialog"></upload-img-dialog>
+    <maintainer-dialog @changeMaintainer="changeMaintainer" ref="maintainer" :dialog="myDialog"></maintainer-dialog>
+    <insurer-dialog @changeInsurer="changeInsurer" ref="insurer" :dialog="myDialog"></insurer-dialog>
+    <pic-dialog :dialog="myDialog" :keysArr="picsArr" @change="changePics"></pic-dialog>
+  </div>
+</template>
+<script>
+
+import myCascader from "@/components/business_space/lib/cascaders/assets";
+// import hansonTable from "@/components/business_space/dialogHanson/addDevice";
+import { getSpaceHeader } from "@/api/scan/request";
+import tools from "@/utils/tools";
+import qrcode from "@/components/business_space/lib/qrcode";
+import firm from "@/components/business_space/dialogs/list/firm";
+import supplyDialog from "@/components/business_space/dialogs/list/supplyDialog";
+import supplierDialog from "@/components/business_space/dialogs/list/supplierDialog";
+import maintainerDialog from "@/components/business_space/dialogs/list/maintainerDialog";
+import insurerDialog from "@/components/business_space/dialogs/list/insurerDialog";
+import guaranteeDialog from "@/components/business_space/dialogs/list/guaranteeDialog";
+import uploadFilesDialog from "@/components/business_space/dialogs/list/filesDialog";
+import uploadImgDialog from "@/components/business_space/dialogs/list/uploadImgDialog";
+import picDialog from "@/components/business_space/dialogs/list/picDialog"
+
+let table = function (num) {
+  let main = []
+  for (let i = 0; i < num; i++) {
+    main.push({})
+  }
+  return main
+}
+export default {
+  components: {
+    myCascader,
+    // hansonTable,
+    qrcode, //二维码页面
+    firm, //
+    supplyDialog,
+    supplierDialog,
+    guaranteeDialog,
+    uploadFilesDialog,
+    maintainerDialog,
+    insurerDialog,
+    uploadImgDialog,
+    picDialog
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          addDevice: false
+        };
+      }
+    },
+    headerData: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    }
+  },
+  data() {
+    return {
+      width: "30%",
+      isFull: true,//dialog是否为全屏
+      title: "添加业务空间",
+      isCenter: true,
+      deviceType: {},
+      main: table(10),
+      mess: {},
+      headers: null,
+      page: {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 400,
+        currentPage: 1
+      },
+      id: null,
+      myDialog: {
+        qrcode: false, //二维码弹窗
+        firm: false, //厂商弹窗
+        supply: false, //选择供应合同
+        supplier: false, //供应商选择
+        guarantee: false, //选择保单
+        maintainer: false, //选择维修商
+        insurer: false, //选择保险公司
+        uploadFiles: false,//上传文件
+        uploadImgs: false,//上传单个图片
+        pic: false, //多个图片
+      },
+      addNum: 1,//增加个数
+      row: null,//被修改的row
+      filesArr: [],//保存临时的文件key
+      messKey: null,
+      imgsArr: [],//临时保存的图片key数组
+      picsArr: [],//临时设备图片keys数组
+    };
+  },
+  created() {
+
+  },
+  mounted() {
+  },
+  methods: {
+    //增加个数
+    addMain() {
+      for (let i = 0; i < this.addNum; i++) {
+        this.main.push({})
+      }
+      this.hot.loadData(this.main)
+    },
+
+    //添加业务空间确定
+    addSpace() {
+      console.log(this.main)
+      let pushData = []
+      this.main.map(item => {
+        if (!!item.infos) {
+          for (let key in item.infos) {
+            if (!!item.infos[key]) {
+              pushData.push(item)
+            }
+          }
+        }
+      })
+      console.log(pushData)
+    },
+
+    //下一步
+    // step(val) {
+    //   if (!!val) {
+    //     this.isFull = true
+    //     this.isCenter = true
+    //     this.title = "添加资产"
+    //     this.getData()
+    //   } else {
+    //     this.isFull = false
+    //     this.isCenter = false
+    //   }
+    // },
+    //修改设备类型
+    changeCader(val) {
+      this.deviceType = val
+    },
+    //获取header的mess
+    getHeader(mess) {
+      this.mess = mess;
+    },
+
+    //获取主体内容
+    // getMain(floorMess) {
+    //   console.log(floorMess);
+    // },
+
+    //获取表头
+    getData() {
+      this.main = table(10)
+      getSpaceHeader({
+        code: "space",
+        ProjId: this.$route.query.projId
+      }).then(res => {
+        this.headers = res.data.Content;
+        if (!!this.hot) {
+          this.hot.destroy();
+          this.hot = null;
+        }
+        let data = tools.getItem(this.deviceType.code)
+        if (!!data) {
+          this.main = data
+        }
+        this.initHot();
+      });
+    },
+
+    //撤回
+    undo() {
+      this.hot.undo();
+    },
+
+    //刷新
+    reset() {
+      this.getData();
+    },
+
+    //添加设备
+    addDevice() {
+      console.log("addDevice")
+    },
+
+    /**
+     * 表头文案处理函数
+     * @param arr header数组数据
+     *
+     * @return 处理好的文案
+     */
+    delHeader(arr) {
+      let data = arr.map(item => {
+        if (
+          item.InputMode == "A1" ||
+          item.InputMode == "A2" ||
+          item.InputMode == "B1" ||
+          item.InputMode == "C5" ||
+          item.InputMode == "D1" ||
+          item.InputMode == "D2" ||
+          item.InputMode == "X" ||
+          item.InputMode == "L" ||
+          item.InputMode == "L1" ||
+          item.InputMode == "L2" ||
+          item.InputMode == "F1" ||
+          item.InputMode == "F2"
+        ) {
+          if (item.unit == "") {
+            return item.InfoPointName;
+          } else {
+            return item.InfoPointName + "(" + item.unit + ")";
+          }
+        } else {
+          return undefined;
+        }
+      });
+      data = data.filter(item => item);
+      //   data.unshift("同时创建资产", "所属建筑楼层", "所属系统实例");
+      return data;
+    },
+
+    /**
+     * 表头数据处理函数
+     * @param arr header数组数据
+     *
+     * @return 处理好的数据格式
+     */
+    getType(arr) {
+      let data = arr.map(item => {
+        //二维码
+        if (item.infoPointCode == "EquipQRCode") {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+        //厂商选择
+        if (item.infoPointCode == "Brand" || item.infoPointCode == "Specification") {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+        if (item.infoPointCode == "SupplierContractID") {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+
+        if (item.infoPointCode == "InsuranceNum") {
+          //选择保单
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+
+        if (item.infoPointCode == "InsuranceFile" || item.infoPointCode == "Archive") {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+
+        if (
+          item.infoPointCode == "InstallLocation" ||
+          item.infoPointCode == "InstallPic" ||
+          item.infoPointCode == "InstallDrawing" ||
+          item.infoPointCode == "Nameplate" ||
+          item.infoPointCode == "Pic" ||
+          item.infoPointCode == "Drawing"
+        ) {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+
+        if (
+          item.infoPointCode == "Maintainer" ||
+          item.infoPointCode == "Supplier" ||
+          item.infoPointCode == "Insurer" ||
+          item.infoPointCode == "InsurerContactor"
+        ) {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.lookDetails,
+            readOnly: true
+          }
+        }
+        if (item.InputMode == "D1") {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.customDropdownRenderer,
+            editor: "chosen",
+            chosenOptions: {
+              // multiple: true,//多选
+              data: item.DataSource || ""
+            }
+          };
+        } else if (item.InputMode == "A1" || item.InputMode == "A2") {
+          return {
+            data: "infos." + item.infoPointCode,
+            type: "numeric",
+            numericFormat: {
+              pattern: "0,0.00"
+              // culture: 'de-DE' // use this for EUR (German),
+              // more cultures available on http://numbrojs.com/languages.html
+            }
+          };
+        } else if (item.InputMode == "C5") {
+          return {
+            data: "infos." + item.infoPointCode,
+            type: "date",
+            dateFormat: "YYYY-MM-DD",
+            correctFormat: true
+          };
+        } else if (
+          item.InputMode == "B1" ||
+          item.InputMode == "L" ||
+          item.InputMode == "L1" ||
+          item.InputMode == "L2"
+        ) {
+          return {
+            data: "infos." + item.infoPointCode
+          };
+        } else if (
+          item.InputMode == "X" ||
+          item.InputMode == "F2"
+          // item.InputMode == "L1" ||
+          // item.InputMode == "L2"
+        ) {
+          return {
+            data: "infos." + item.infoPointCode,
+            readOnly: true
+          };
+        } else if (item.InputMode == "D2") {
+          return {
+            data: "infos." + item.infoPointCode,
+            renderer: tools.customDropdownRenderer,
+            editor: "chosen",
+            chosenOptions: {
+              multiple: true, //多选
+              data: item.DataSource.Content || ""
+            }
+          };
+        } else {
+          return undefined;
+        }
+
+      });
+      //   data.unshift(
+      //     {
+      //       type: "checkbox",
+      //       checkedTemplate: 1,
+      //       uncheckedTemplate: 0,
+      //       data: "Checked",
+      //       label: {
+      //         position: "after",
+      //       }
+      //     },
+      //     {
+      //       data: "flowBuild",
+      //       renderer: tools.customDropdownRenderer,
+      //       editor: "chosen",
+      //       chosenOptions: {
+      //         // multiple: true,//多选
+      //         // data: item.DataSource.Content || ""
+      //       }
+      //     },
+      //     {
+      //       data: "system",
+      //       renderer: tools.customDropdownRenderer,
+      //       editor: "chosen",
+      //       chosenOptions: {
+      //         // multiple: true,//多选
+      //         // data: item.DataSource.Content || ""
+      //       }
+      //     }
+      //   );
+      data = data.filter(item => item);
+      return data;
+    },
+
+    //初始化插件
+    initHot() {
+      var container = document.getElementById("myHandson");
+      let winHeight = document.documentElement.clientHeight;
+      this.hot = new Handsontable(container, {
+        data: this.main,
+        colHeaders: this.delHeader(this.headers), //表头文案
+        columns: this.getType(this.headers), //数据显示格式
+        filters: true,
+        height: winHeight - 100 - 50 - 60,
+        columnSorting: true, //添加排序
+        sortIndicator: true, //添加排序
+        renderAllRows: true,
+        autoColumnSize: true,
+        language: "zh-CN",
+        manualColumnResize: true,
+        manualColumnMove: true,
+        dropdownMenu: [
+          "filter_by_condition",
+          "filter_by_value",
+          "filter_action_bar"
+        ],
+        contextMenu: {
+          items: {
+            remove_row: {
+              name: "删除该业务空间"
+            }
+          }
+        },
+        filters: true,
+        afterFilter: this.trimmedRows,
+        // 事件
+        afterChange: this.tdChange, //修改后
+        afterFilter: this.trimmedRows, //排序前
+        beforeRemoveRow: this.romoveFm, //右键删除
+        afterOnCellMouseDown: this.eventClick //鼠标点击
+      });
+      let pro = document.getElementById("hot-display-license-info");
+      if (!!pro) {
+        pro.parentNode.removeChild(pro);
+      }
+      this.isLoading = false;
+    },
+
+    tdChange() {
+      tools.setItem(this.deviceType.code, this.main)
+    },
+
+    //表格中的点击
+    eventClick(el, rowArr) {
+      console.log(rowArr)
+      //点击的是表头
+      if (rowArr.row < 0) {
+        return;
+      }
+      let filter = this.filtersArr;
+      //被筛选过后的数组
+      let trimmedArr = this.trimmedRows();
+      //是否启用了排序
+      let isSort = this.hot.getPlugin("columnSorting").isSorted();
+      if (trimmedArr.length && isSort) {
+        let sortArr = this.myHotArr.getPlugin("columnSorting").rowsMapper
+          .__arrayMap;
+        let infos = this.main[trimmedArr[sortArr[rowArr.row]]];
+        this.getInfors(infos, sortArr[rowArr.row]);
+      } else if (isSort) {
+        //排序后的数组
+        let sortArr = this.hot.getPlugin("columnSorting").rowsMapper.__arrayMap;
+        let infos = this.main[sortArr[rowArr.row]];
+        this.getInfors(infos, sortArr[rowArr.row]);
+      } else if (trimmedArr.length) {
+        let infos = this.main[trimmedArr[rowArr.row]];
+        this.getInfors(infos, trimmedArr[rowArr.row]);
+      } else {
+        let infos = this.main[rowArr.row];
+        this.getInfors(infos, rowArr);
+      }
+    },
+
+    getInfors(infos, row) {
+      //其他的开始判断
+      let val = this.hot.colToProp(row.col);
+      if (val == "infos.EquipQRCode") {
+        this.myDialog.qrcode = true;
+        this.$refs.qrcode.getCanvas(1111);
+      }
+
+      if (val == "infos.Brand" || val == "infos.Specification") {
+        this.myDialog.firm = true;
+      }
+
+      if (val == "infos.SupplierContractID") {
+        let falg = null
+        if (!!this.main[row.row].infos) {
+          if (!!this.main[row.row].infos) {
+            falg = this.main[row.row].infos.DPSupplierID
+          }
+        }
+        if (!!falg) {
+          this.id = falg
+          this.myDialog.supply = true;
+        } else {
+          this.$message("请先选择供应商")
+        }
+      }
+
+      if (val == "infos.InsuranceNum") {
+        //选择保单
+        this.myDialog.guarantee = true;
+      }
+
+      if (val == "infos.InsuranceFile" || val == "infos.Archive") {
+        this.filesArr = this.main[row.row].infos ? (this.main[row.row].infos[val.split(".")[1]] ? [this.main[row.row].infos[val.split(".")[1]]] : []) : []
+        this.myDialog.uploadFiles = true
+      }
+
+      if (
+        val == "infos.InstallLocation" ||
+        val == "infos.InstallPic" ||
+        val == "infos.InstallDrawing" ||
+        val == "infos.Nameplate" ||
+        val == "infos.Drawing"
+      ) {
+        if (val == "infos.Nameplate") {
+          this.imgsArr = this.main[row.row].infos ? (this.main[row.row].infos[val.split(".")[1]] ? [this.main[row.row].infos[val.split(".")[1]].key] : []) : []
+        } else {
+          this.imgsArr = this.main[row.row].infos ? (this.main[row.row].infos[val.split(".")[1]] ? [this.main[row.row].infos[val.split(".")[1]]] : []) : []
+        }
+        this.myDialog.uploadImgs = true
+      }
+
+      if (val == "infos.Pic") {
+        // console.log(this.main[row.row].infos[val.split(".")[1]], "this.main[row.row].infos[val.split")
+        this.picsArr = this.main[row.row].infos ? (this.main[row.row].infos[val.split(".")[1]] ? this.main[row.row].infos[val.split(".")[1]] : []) : []
+        console.log(this.picsArr)
+        this.myDialog.pic = true
+      }
+
+      if (
+        val == "infos.Supplier"
+      ) {
+        this.myDialog.supplier = true;
+      }
+
+      if (val == "infos.Maintainer") {
+        this.myDialog.maintainer = true;
+      }
+
+      if (
+        val == "infos.Insurer" ||
+        val == "infos.InsurerContactor") {
+        this.myDialog.insurer = true;
+      }
+      this.row = row.row
+      this.messKey = val
+      console.log(val, row);
+    },
+
+    //获取被筛选掉的行号
+    trimmedRows() {
+      // var plugin = hot.getPlugin('trimRows').trimmedRows;//获取被筛选掉的行号
+      var plugin = this.hot.getPlugin("trimRows").trimmedRows;
+      let dataLength = this.main.length;
+      let dataArr = new Array();
+      for (let i = 0; i < dataLength; i++) {
+        dataArr.push(i);
+      }
+      if (plugin.length <= 0) {
+        dataArr = undefined;
+      } else {
+        dataArr = this.array_diff(dataArr, plugin);
+      }
+      return dataArr || [];
+      // var DataArray = new Array();
+
+      // for (var i = 0; i < plugin.length; i++) {
+      //     // 通过行号获取数据
+      //     DataArray.push(this.hot.getSourceDataAtRow(plugin[i]));
+      // }
+    },
+
+    //判断是否为空行
+    isEmptyRow(instance, row) {
+      var rowData = instance.countRows();
+      for (var i = 0, ilen = rowData.length; i < ilen; i++) {
+        if (rowData[i] !== null) {
+          return false;
+        }
+      }
+      return true;
+    },
+
+    //如果选择供应商之后
+    supplierChange(data) {
+      console.log(data, "供应商")
+      for (let key in data) {
+        this.utilToKey(key, "name", data, "Supplier")
+        this.utilToKey(key, "email", data, "SupplierEmail")
+        this.utilToKey(key, "website", data, "SupplierWeb")
+        this.utilToKey(key, "phone", data, "SupplierPhone")
+        this.utilToKey(key, "fox", data, "SupplierFax")
+        this.utilToKey(key, "man", data, "SupplierContactor")
+        this.utilToKey(key, "venderId", data, "DPSupplierID")
+      }
+      tools.setItem(this.deviceType.code, this.main)
+    },
+
+    //保险商变更
+    changeInsurer(data) {
+      console.log(data, "保险商")
+      for (let key in data) {
+        this.utilToKey(key, "name", data, "Insurer")
+        this.utilToKey(key, "email", data, "InsurerEmail")
+        this.utilToKey(key, "website", data, "InsurerWeb")
+        this.utilToKey(key, "phone", data, "InsurerFax")
+        this.utilToKey(key, "man", data, "InsurerContactor")
+        this.utilToKey(key, "venderId", data, "DPInsurerID")
+      }
+      tools.setItem(this.deviceType.code, this.main)
+    },
+
+    //维修商变更
+    changeMaintainer(data) {
+      for (let key in data) {
+        this.utilToKey(key, "name", data, "Maintainer")
+        this.utilToKey(key, "email", data, "MaintainerEmail")
+        this.utilToKey(key, "website", data, "MaintainerWeb")
+        this.utilToKey(key, "phone", data, "MaintainerPhone")
+        this.utilToKey(key, "fox", data, "MaintainerFax")
+        this.utilToKey(key, "man", data, "MaintainerContactor")
+        this.utilToKey(key, "venderId", data, "DPMaintainerID")
+      }
+      tools.setItem(this.deviceType.code, this.main)
+    },
+
+    utilToKey(key, name, data, messName) {
+      if (key == name) {
+        this.setDataToMain(data[key], messName, this.row)
+      }
+    },
+
+    //上传文件弹窗触发事件
+    fileChange(keys) {
+      this.setDataToMain(keys[0], this.messKey.split(".")[1], this.row)
+      tools.setItem(this.deviceType.code, this.main)
+    },
+
+    //上传图片弹窗触发事件
+    imgChange(keys) {
+      this.setDataToMain(keys[0], this.messKey.split(".")[1], this.row)
+      tools.setItem(this.deviceType.code, this.main)
+    },
+
+    //设备图片弹窗改变事件
+    changePics(keys) {
+      this.setDataToMain(keys, this.messKey.split(".")[1], this.row)
+      tools.setItem(this.deviceType.code, this.main)
+      console.log("assets", keys)
+    },
+
+    //判断是否有值,有值赋值
+    setDataToMain(data, key, row) {
+      if (!!data && data != '--') {
+        if (!!this.main[row].infos) {
+          //铭牌照片特殊处理
+          if (key == 'Nameplate') {
+            this.main[row].infos[key] = {
+              "systemId": "dataPlatform",
+              "name": "铭牌照片",
+              "type": "image",
+              "key": data
+            }
+          } else {
+            this.main[row].infos[key] = data
+          }
+        } else {
+          this.main[row].infos = {}
+          if (key == 'Nameplate') {
+            this.main[row].infos[key] = {
+              "systemId": "dataPlatform",
+              "name": "铭牌照片",
+              "type": "image",
+              "key": data
+            }
+          } else {
+            this.main[row].infos[key] = data
+          }
+        }
+      } else {
+        this.main[row].infos[key] = ''
+      }
+    }
+  },
+  watch: {
+    dialog: {
+      deep: true,
+      handler(newName, oldName) {
+        // this.getData()
+        console.log(this.dialog)
+        if (this.dialog.addDevice) {
+          this.getData()
+        }
+      },
+    }
+  }
+};
+</script>
+<style lang="less" scoped>
+#addDevice {
+  overflow: hidden;
+}
+</style>

+ 344 - 0
src/components/business_space/dialogs/changeRea.vue

@@ -0,0 +1,344 @@
+<!--
+    changeRea 厂商
+-->
+<template>
+  <el-dialog title="关联资产" :visible.sync="dialog.changeRea" width="800px">
+    <div>
+      <div id="changeRea">
+        <div class="title-search" style="margin-bottom: 10px;">
+          <el-input
+            placeholder="输入名称查询"
+            v-model="search"
+            size="small"
+            style="width:300px;"
+            clearable
+          ></el-input>
+          <el-button @click="getData" size="small">查找</el-button>
+        </div>
+        <div>
+          <floor-cascader @change="changeFloor"></floor-cascader>
+        </div>
+        <el-radio-group v-model="radio" style="width:100%;">
+          <el-table :data="tableData" style="width: 100%" height="300px">
+            <el-table-column label="资产名称">
+              <template slot-scope="scope">
+                <el-radio
+                  v-model="radio"
+                  :label="scope.row.id"
+                >{{scope.row.infos.EquipLocalName || scope.row.infos.EquipName}}</el-radio>
+              </template>
+            </el-table-column>
+            <el-table-column label="本地编码">
+              <template
+                slot-scope="scope"
+              >{{scope.row.infos.EquipLocalID || scope.row.infos.EquipID}}</template>
+            </el-table-column>
+            <el-table-column prop="name" label="所在楼层">
+              <template slot-scope="scope">{{getFloor(scope.row.relation)}}</template>
+            </el-table-column>
+            <el-table-column label="设备族">
+              <template slot-scope="scope">{{getFramily(scope.row.family)}}</template>
+            </el-table-column>
+            <el-table-column label="安装位置">
+              <template slot-scope="scope">{{scope.row.infos.InstallLocation || "--"}}</template>
+            </el-table-column>
+          </el-table>
+        </el-radio-group>
+        <my-pagination :page="page" @change="changed" style="margin-top:10px;"></my-pagination>
+        <div class="footer">
+          <el-button
+            type="primary"
+            @click="clickTrue"
+            style="display:block;margin: 10px auto 0;"
+          >确 定</el-button>
+        </div>
+      </div>
+    </div>
+    <param-details :isLoad="isLoad" :data="data"></param-details>
+  </el-dialog>
+</template>
+<script>
+import myPagination from "@/components/ledger/lib/myPagination";
+import paramDetails from "@/components/dialogs/list/paramDetails"
+import { getBillPropList, getEquipmentFamily, getSpaceFloor, getBussines, upDateTableMain } from "@/api/scan/request"
+import floorCascader from "@/components/ledger/lib/floorCascader";
+
+export default {
+  components: {
+    myPagination,
+    paramDetails,
+    floorCascader
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          changeRea: true
+        };
+      }
+    },
+    category: {
+      type: Object,
+      default: function () {
+        return {}
+      }
+    }
+  },
+  data() {
+    return {
+      search: "", //搜索文案
+      radio: "",
+      tableData: [
+      ],
+      data: {},
+      isLoad: {
+        paramShow: false
+      },
+      page: {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      },
+      mess: {},
+      codeListCode: [],
+      floorList: []
+    };
+  },
+  created() {
+    this.getCode()
+  },
+  mounted() { },
+  methods: {
+    getFloor(relation) {
+      let floor = !!relation ? relation.floor || "" : ""
+      if (!!floor) {
+        console.log(floor)
+        let floorName = ""
+        this.floorList.map(item => {
+          if (!!item.floors && item.floors.length) {
+            item.floors.map(child => {
+              if (child.id == floor) {
+                floorName = child.infos.FloorLocalName || child.infos.FloorName
+              }
+            })
+          }
+        })
+        return floorName
+      } else {
+        return "--"
+      }
+    },
+
+    clickTrue() {
+      if (!!this.radio) {
+        let param = {
+          UserId: this.$route.query.userId,
+          ProjId: this.$route.query.projId
+        }
+        let paramList = [{
+          EquipmentId: this.category.id,
+          FmId: this.radio
+        }]
+        console.log(param, paramList)
+        upDateTableMain(param, paramList).then(res => {
+          this.$emit("close")
+          this.dialog.changeRea = false
+        })
+        // upDateTableMain(param, res => {
+        // })
+      } else {
+        this.$message("请先选择资产")
+      }
+    },
+
+    getFramily(code) {
+      console.log(code)
+      let name = ""
+      this.codeListCode.map(item => {
+        if (item.code == code) {
+          name = item.name
+        }
+      })
+      return name || "--"
+    },
+    getCode() {
+      getEquipmentFamily(res => {
+        this.codeListCode = res.Content
+        this.getFloorList()
+      })
+    },
+    getFloorList() {
+      let param = {
+        secret: this.$route.query.secret,
+        ProjId: this.$route.query.projId
+      }
+      getSpaceFloor(param).then(res => {
+        if (res.data.Result == "success") {
+          this.floorList = res.data.Content
+        } else {
+          this.$message.error("请求错误:" + res.data.ResultMsg)
+        }
+      }).catch(_ => {
+        this.$message.error("请求错误")
+      })
+    },
+    changeFloor(val) {
+      this.mess = {
+        buildId: val[0] || "",
+        floorId: val[1] || ""
+      }
+      this.getData()
+      console.log(val)
+    },
+    changed() {
+      this.getData()
+    },
+    //查询技术参数
+    lookParam(data) {
+      this.data = data
+      this.isLoad.paramShow = true
+    },
+    getChange() {
+      if (!!this.radio) {
+        this.$emit("changechangeRea", this.radio)
+        this.dialog.changeRea = false
+      } else {
+        this.$message("请选择型号")
+      }
+    },
+    searchKey() {
+      this.changePage()
+    },
+    changePage() {
+      this.page = {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      }
+      this.getData()
+    },
+    getData() {
+      //   let param = {
+      //     data: {
+      //       "limit": {        // 分页查询, 选填.
+      //         "skip": this.page.size * (this.page.currentPage - 1),  // 跳过多少数据
+      //         "count": this.page.size   // 查询跳过300条数据之后的50条 (默认按照创建时间从大-->小排序)
+      //       },
+      //       filter: {
+      //         EquipLocalName: this.search
+      //       },
+      //       "criteria": {
+      //         "id": "",  // 选填, 默认值是Pj****, 即查询项目下的所有设备
+      //         "family": ["XXX"]         // 必填, 要查询的类型, 只能是设备类型
+      //       }
+      //     },
+      //     secret: this.$route.query.secret,
+      //     ProjId: this.$route.query.projId
+      //   }
+      //   if (this.mess.buildId == "all") {
+      //     param.data.criteria.id = this.mess.ProjId
+      //   } else if (this.mess.buildId == "noKnow") {
+      //     param.data.criteria.id = this.mess.ProjId
+      //     param.data.directOnly = true
+      //   } else if (this.mess.floorId == "noKnow") {
+      //     param.data.criteria.id = this.mess.buildId
+      //     param.data.directOnly = true
+      //   } else if (this.mess.floorId && this.mess.floorId != "all") {
+      //     param.data.criteria.id = this.mess.floorId
+      //   } else if (this.mess.floorId == 'all') {
+      //     param.data.criteria.id = this.mess.buildId
+      //   } else {
+      //     param.data.criteria.id = this.$route.query.projId
+      //   }
+      //   console.log("category", this.category)
+      //   param.data.criteria.family = [this.getType(this.category.category)]
+      //   getBillPropList(param, res => {
+      //     console.log(res)
+      //     this.tableData = res.Content
+      //     this.page.total = res.Count
+      //   })
+      let param = {
+        data: {
+          filter: {
+            EquipLocalName: this.search
+          },
+          directOnly: false,
+          criteria: {
+            id: "",
+            graphType: "Pe2Eq",
+            side: -1,
+            type: ""
+          }
+        },
+        secret: this.$route.query.secret,
+        ProjId: this.$route.query.projId
+      }
+      if (this.mess.buildId == "all") {
+        param.data.criteria.id = this.mess.ProjId
+      } else if (this.mess.buildId == "noKnow") {
+        param.data.criteria.id = this.mess.ProjId
+        param.data.directOnly = true
+      } else if (this.mess.floorId == "noKnow") {
+        param.data.criteria.id = this.mess.buildId
+        param.data.directOnly = true
+      } else if (this.mess.floorId && this.mess.floorId != "all") {
+        param.data.criteria.id = this.mess.floorId
+      } else if (this.mess.floorId == 'all') {
+        param.data.criteria.id = this.mess.buildId
+      } else {
+        param.data.criteria.id = this.$route.query.projId
+      }
+      param.data.criteria.type = [this.getType(this.category.category)]
+      console.log(param)
+      getBussines(param).then(res => {
+        this.tableData = res.data.Content
+        console.log(this.tableData, "this.tableData")
+        this.page.total = res.data.Count
+      })
+    },
+
+    getType(code) {
+      console.log(code, "code")
+      console.log(code.substring(2, 6))
+      let system = code.substring(2, 6), myCode = ""
+      this.codeListCode.map(item => {
+        if (!!item.content && item.content.length) {
+          item.content.map(child => {
+            if (child.code == system) {
+              myCode = item.code
+            }
+          })
+        }
+      })
+      console.log(myCode)
+      return myCode
+    }
+  },
+  watch: {
+    dialog: {
+      deep: true,
+      handler: function () {
+        if (this.dialog.changeRea) {
+          this.changePage()
+          this.radio = ""
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less">
+#changeRea {
+  .el-table thead {
+    tr {
+      th {
+        background-color: #f5f7fa;
+      }
+    }
+  }
+}
+</style>

+ 203 - 0
src/components/business_space/dialogs/detailsDialog.vue

@@ -0,0 +1,203 @@
+<template>
+    <el-dialog :visible.sync="isShow.details" :width="width">
+        <span slot="title">
+          <span v-show="step == 1">{{title}}</span>
+        <span v-show="step == 1" style="float:right;margin-right:40px;">
+            <el-button class="saga-padding0" @click="step = 3" type="text">编辑信息</el-button>
+            <!-- <el-button class="saga-padding0" @click="addMyEquip" type="text">添加设备</el-button> -->
+            <el-dropdown @command="addMyEquip" trigger="click">
+              <span class="el-dropdown-link" style="color: #409EFF">添加设备</span>
+        <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item command="EquipinSpace">空间内的设备</el-dropdown-item>
+            <el-dropdown-item command="EquipforSpace">服务于空间的设备</el-dropdown-item>
+            <el-dropdown-item command="EquipXSpace">其他关系的设备</el-dropdown-item>
+        </el-dropdown-menu>
+        </el-dropdown>
+        <el-button class="saga-padding0" @click="delSpace" type="text">删除该业务空间</el-button>
+        </span>
+        <span v-show="step == 2 || step == 3">
+            <el-button type="text" @click="goBack" icon="el-icon-arrow-left"></el-button>
+          </span>
+        </span>
+        <div id="detailsDialog" v-show="step == 1">
+            <p class="influence-p">{{list.name}}</p>
+            <el-tabs type="border-card">
+                <el-tab-pane label="空间详情">
+                    <div class="saga-details">
+                        <iframe height="400px" v-if="isShow.details" width="100%" :src="iframeSrc"></iframe>
+                    </div>
+                </el-tab-pane>
+                <el-tab-pane label="与空间相关联的设备">
+                    <div>
+                        <equip-table ref="myEquip"></equip-table>
+                    </div>
+                </el-tab-pane>
+            </el-tabs>
+        </div>
+        <div v-show="step == 2">
+            <add-equip :activeTabType="activeTabType" ref="addEquip"></add-equip>
+        </div>
+        <div id="detailsDialog" v-if="step == 3">
+            <iframe v-if="step == 3" height="400px" width="100%" :src="iframeSrc2"></iframe>
+        </div>
+    </el-dialog>
+</template>
+
+<script>
+    import {
+        delManySpace
+    } from "@/api/scan/request";
+    import equipTable from "@/components/business_space/table/equipTable";
+    import addEquip from "@/components/business_space/table/addEquip";
+    import {
+        mapGetters,
+        mapActions
+    } from "vuex";
+    export default {
+        components: {
+            equipTable,
+            addEquip
+        },
+        computed: {
+            ...mapGetters("peojMess", [
+                "projectId",
+                "secret",
+                "userId"
+            ])
+        },
+        props: {
+            width: {
+                type: String,
+                default: "70%"
+            },
+            title: {
+                type: String,
+                default: "头部信息"
+            },
+            isShow: {
+                type: Object
+            },
+            param: {
+                type: Object
+            },
+            activeTabType: {
+                type: Object,
+                default: function() {
+                    return {
+                        "code": "GeneralZone",
+                        "name": "默认分区",
+                        "rel_type": "99"
+                    }
+                }
+            }
+        },
+        data() {
+            return {
+                step: 1,
+                data: null,
+                proj: null,
+                activeName: "1",
+                list: {},
+                iframeSrc: "",
+                iframeSrc2: "",
+                buildMess: ""
+            };
+        },
+        created() {},
+        methods: {
+            handleClick() {},
+            getData(list, buildMess) {
+                this.step = 1;
+                this.list = list;
+                this.buildMess = buildMess;
+                this.$nextTick(() => {
+                    this.$refs.myEquip.getData(list, this.activeTabType);
+                });
+                let perjectId = this.projectId,
+                    secret = this.secret;
+                this.iframeSrc =
+                    process.env.BASE_URL +
+                    ":8889/#/details?perjectId=" +
+                    perjectId +
+                    "&secret=" +
+                    secret +
+                    "&FmId=" +
+                    list.id +
+                    "&type=0&code=" + this.activeTabType.code;
+                this.iframeSrc2 =
+                    process.env.BASE_URL +
+                    ":8889/#/changeMess?perjectId=" +
+                    perjectId +
+                    "&secret=" +
+                    secret +
+                    "&id=" +
+                    list.id +
+                    "&type=" + this.activeTabType.code;
+            },
+            addMyEquip(str) {
+                this.step = 2;
+                this.$nextTick(() => {
+                    console.log(this.activeTabType)
+                    this.$refs.addEquip.getData(this.list, this.buildMess, str, this.activeTabType);
+                });
+            },
+            goBack() {
+                this.step = 1;
+                this.$refs.myEquip.getData(this.list, this.activeTabType);
+            },
+            delSpace() {
+                this.$confirm("此操作将永久删除该业务空间, 是否继续?", "删除", {
+                        confirmButtonText: "确定",
+                        cancelButtonText: "取消",
+                        type: "warning"
+                    })
+                    .then(() => {
+                        let param = {
+                            ProjId: this.projectId,
+                            secret: this.secret,
+                            data: {
+                                criteria: {
+                                    ids: [this.list.id]
+                                }
+                            }
+                        };
+                        delManySpace(param)
+                            .then(res => {
+                                if (res.data.Result == "success") {
+                                    this.$message({
+                                        type: "success",
+                                        message: "删除成功!"
+                                    });
+                                    this.$emit("del");
+                                } else {
+                                    this.$message.error(res.data.ResultMsg);
+                                }
+                            })
+                            .catch(() => {
+                                this.$message.error("请求发生错误");
+                            });
+                    })
+                    .catch(() => {
+                        this.$message({
+                            type: "info",
+                            message: "已取消删除"
+                        });
+                    });
+            }
+        }
+    };
+</script>
+
+<style lang="less">
+    #detailsDialog {
+        .el-tabs__content {
+            max-height: 400px;
+        }
+        .influence-p {
+            height: 40px;
+            line-height: 40px;
+            font-size: 17px;
+            margin-bottom: 10px;
+        }
+    }
+</style>

+ 148 - 0
src/components/business_space/dialogs/facilityDialog.vue

@@ -0,0 +1,148 @@
+<template>
+  <el-dialog :visible.sync="isShow.facility" :width="width">
+    <span slot="title">
+      <span v-show="step == 1">业务空间详情</span>
+      <!-- <span v-show="step == 1" @click="addMyEquip" style="float: right; margin-right: 40px;">
+        <el-button class="saga-padding0" type="text">添加设备</el-button>
+      </span>-->
+      <span v-show="step == 1" style="float: right; margin-right: 40px;">
+        <el-dropdown @command="addMyEquip" trigger="click">
+          <span class="el-dropdown-link" style="color: #409EFF">添加设备</span>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item command="EquipinSpace">空间内的设备</el-dropdown-item>
+            <el-dropdown-item command="EquipforSpace">服务于空间的设备</el-dropdown-item>
+            <el-dropdown-item command="EquipXSpace">其他关系的设备</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </span>
+      <span v-show="step == 2">
+        <el-button class="saga-padding0" @click="goBack" type="text" icon="el-icon-arrow-left"></el-button>
+      </span>
+    </span>
+    <div id="detailsDialog" v-show="step == 1">
+      <p class="influence-p">{{data.infos.RoomLocalName || data.infos.RoomName || '--'}}</p>
+      <equip-table :activeTabType="activeTabType" ref="myEquip" :param="myDia"></equip-table>
+    </div>
+    <div v-show="step == 2">
+      <add-equip ref="addEquip"></add-equip>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import equipTable from "@/components/business_space/table/equipTable";
+import addEquip from "@/components/business_space/table/addEquip";
+import {
+        mapGetters,
+        mapActions
+    } from "vuex";
+export default {
+  components: {
+    equipTable,
+    addEquip
+  },
+  computed: {
+            ...mapGetters("peojMess", [
+                "projectId",
+                "secret",
+                "userId"
+            ])
+        },
+  props: {
+    width: {
+      type: String,
+      default: "60%"
+    },
+    title: {
+      type: String,
+      default: "头部信息"
+    },
+    isShow: {
+      type: Object
+    },
+    param: {
+      type: Object
+    },
+    activeTabType: {
+      type: Object,
+      default: function () {
+        return {
+          "code": "GeneralZone",
+          "name": "默认分区",
+          "rel_type": "99"
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      step: 1,
+      data: { infos: {} },
+      proj: null,
+      activeName: "1",
+      myDia: {
+        id: 'Sp11010800034c251365d66111e8bf0a15b159c8e729',
+        ProjId: this.projectId,
+        secret: this.secret
+      },
+      buildMess: {},
+    };
+  },
+  created() { 
+      this.myDia.ProjId = this.projectId
+      this.myDia.secret = this.secret
+  },
+  methods: {
+    handleClick() { },
+    getData(data, buildMess) {
+      this.data = data
+      this.buildMess = buildMess
+      console.log(this.data)
+      this.step = 1
+      this.$nextTick(() => {
+        this.$refs.myEquip.getData(this.data, this.activeTabType)
+      })
+    },
+    goBack() {
+      this.step = 1
+      this.$refs.myEquip.getData(this.data, this.activeTabType)
+    },
+    delSpace() {
+      this.$confirm('此操作将永久删除该业务空间, 是否继续?', '删除', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.$message({
+          type: 'success',
+          message: '删除成功!'
+        });
+      }).catch(() => {
+        this.$message({
+          type: 'info',
+          message: '已取消删除'
+        });
+      });
+    },
+    addMyEquip(str) {
+      console.log(this.data, this.buildMess)
+      this.step = 2
+      this.$refs.addEquip.getData(this.data, this.buildMess, str, this.activeTabType)
+    }
+  }
+};
+</script>
+
+<style lang="less">
+#detailsDialog {
+  .el-tabs__content {
+    max-height: 400px;
+  }
+  .influence-p {
+    height: 40px;
+    line-height: 40px;
+    font-size: 17px;
+    margin-bottom: 10px;
+  }
+}
+</style>

+ 150 - 0
src/components/business_space/dialogs/influenceDialog.vue

@@ -0,0 +1,150 @@
+<template>
+    <el-dialog :visible.sync="isShow.influence" :center="true" :width="width">
+        <span slot="title">{{title}}</span>
+        <div id="influenceDialog">
+            <p class="influence-p">受元空间变化影响,部分业务空间需重新划分</p>
+            <el-tabs type="border-card">
+                <el-tab-pane v-for="(item,index) in tabsList" v-if="index == 0" :key="index">
+                    <span slot="label">{{item.name}}</span>
+                    <p style="line-height:30px;font-size:20px;margin-bottom: 20px;">受影响的业务空间</p>
+                    <div v-for="(i,iIndex) in mess" :key="iIndex">
+                        <p style="line-height:20px;margin-left:10px;">{{i.RoomLocalName || '--'}} - {{i.RoomLocalID || '--'}}</p>
+                    </div>
+                </el-tab-pane>
+            </el-tabs>
+        </div>
+        <span slot="footer" class="dialog-footer center">
+            <el-button type="primary" @click="confirm">我知道了</el-button>
+        </span>
+    </el-dialog>
+</template>
+<script>
+    import {
+        getAllMess,
+        deleteNotice
+    } from "@/api/scan/request";
+    import {
+        mapGetters,
+        mapActions
+    } from "vuex";
+    export default {
+        props: {
+            width: {
+                type: String,
+                default: "40%"
+            },
+            title: {
+                type: String,
+                default: "提示"
+            },
+            isShow: {
+                type: Object
+            },
+            param: {
+                type: Object
+            },
+            tabsList: {
+                type: Array,
+                default: []
+            }
+        },
+        computed: {
+            ...mapGetters("peojMess", [
+                "projectId",
+                "secret",
+                "userId"
+            ])
+        },
+        data() {
+            return {
+                data: null,
+                proj: null,
+                activeName: "1",
+                allMess: [], //所有提示
+                mess: [], //实际提示
+                build: {}
+            };
+        },
+        created() {
+            this.getAllMess();
+        },
+        methods: {
+            //我知道了
+            confirm() {
+                let ids = [];
+                if (!!this.allMess[this.build.code]) {
+                    this.allMess[this.build.code].map(item => {
+                        ids.push(item.RoomID);
+                    });
+                }
+                let param = {
+                    data: {
+                        ids: ids
+                    },
+                    ProjId: this.projectId,
+                    secret: this.secret
+                };
+                deleteNotice(param)
+                    .then(res => {
+                        if (res.data.Result == "success") {
+                            this.isShow.influence = false;
+                        } else {
+                            this.$message.error(res.data.ResultMsg);
+                        }
+                    })
+                    .catch(() => {
+                        this.$message.error("请求失败");
+                    });
+            },
+            getData(build) {
+                this.build = build;
+                this.mess = this.allMess[build.code];
+            },
+            getAllMess() {
+                getAllMess({
+                        ProjId: this.projectId,
+                        secret: this.secret
+                    })
+                    .then(res => {
+                        if (res.data.Result == "success") {
+                            // this.allMess = res.data.Content
+                            this.changeMsg(res.data.Content);
+                            console.log(this.allMess);
+                        } else {
+                            this.$message.error(res.data.ResultMsg);
+                        }
+                    })
+                    .catch(() => {
+                        this.$message.error("请求错误");
+                    });
+            },
+            //获取楼层信息
+            changeMsg(arr) {
+                arr.map(item => {
+                    if (!!this.allMess[item.location.floor]) {
+                        this.allMess[item.location.floor].push(item.infos);
+                    } else {
+                        this.allMess[item.location.floor] = [];
+                        this.allMess[item.location.floor].push(item.infos);
+                    }
+                });
+            },
+            handleClick() {}
+        }
+    };
+</script>
+
+<style lang="less">
+    #influenceDialog {
+        .el-tabs__content {
+            max-height: 200px;
+            overflow-y: auto;
+        }
+        .influence-p {
+            height: 40px;
+            line-height: 40px;
+            font-size: 17px;
+            margin-bottom: 10px;
+        }
+    }
+</style>

+ 50 - 0
src/components/business_space/dialogs/list/filesDialog.vue

@@ -0,0 +1,50 @@
+<!--
+    上传文件的弹窗
+-->
+
+<template>
+  <el-dialog title="上传文件" :visible.sync="dialog.uploadFiles" width="30%">
+    <upload-files :readOnly="read" :keysArr="keysArr" @change="changeItem" max="2"></upload-files>
+  </el-dialog>
+</template>
+<script>
+import uploadFiles from "@/components/business_space/lib/uploadFiles";
+export default {
+  components: {
+    uploadFiles
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          uploadFiles: false
+        };
+      }
+    },
+    keysArr: {
+      type: Array,
+      default: function () {
+        return []
+      }
+    },
+    read: {
+      type: Boolean,
+      default: false
+    }
+  },
+  data() {
+    return {};
+  },
+  created() { },
+  mounted() { },
+  methods: {
+    changeItem(file) {
+      console.log(file)
+      this.$emit("changeFile", file)
+    }
+  },
+};
+</script>
+<style>
+</style>

+ 162 - 0
src/components/business_space/dialogs/list/firm.vue

@@ -0,0 +1,162 @@
+<!--
+    firm 厂商
+-->
+<template>
+  <el-dialog title="选择型号" :visible.sync="dialog.firm" width="600px">
+    <div>
+      <div id="firm">
+        <div class="title-search" style="margin-bottom: 10px;">
+          <el-input
+            placeholder="输入厂家名称、品牌名、型号进行查找"
+            v-model="search"
+            size="small"
+            style="width:400px;"
+            clearable
+          ></el-input>
+          <el-button @click="searchKey" size="small">查找</el-button>
+        </div>
+        <el-radio-group v-model="radio" style="width:100%;">
+          <el-table :data="tableData" style="width: 100%" height="300px">
+            <el-table-column label="生产厂家">
+              <template slot-scope="scope">
+                <el-radio v-model="radio" :label="scope.row">{{scope.row.venderName || "--"}}</el-radio>
+              </template>
+            </el-table-column>
+            <el-table-column prop="brand" label="品牌"></el-table-column>
+            <el-table-column prop="name" label="型号"></el-table-column>
+            <el-table-column label="技术参数">
+              <template slot-scope="scope">
+                <el-button type="text" @click="lookParam(scope.row)">技术参数</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-radio-group>
+        <my-pagination :page="page" @change="changed" style="margin-top:10px;"></my-pagination>
+        <p style="height:40px;line-height:40px;text-align:center;color:#9E9E9E;">
+          找不到想要的型号? 赶快去
+          <i style="color:#46B0FF;cursor: pointer;">厂家库</i>维护吧
+        </p>
+        <div class="footer">
+          <el-button
+            type="primary"
+            @click="getChange"
+            style="display:block;margin: 10px auto 0;"
+          >确 定</el-button>
+        </div>
+      </div>
+    </div>
+    <param-details :isLoad="isLoad" :data="data"></param-details>
+  </el-dialog>
+</template>
+<script>
+import myPagination from "@/components/common/myPagination";
+import paramDetails from "@/components/business_space/dialogs/list/paramDetails"
+import { getSpecList } from "@/api/scan/request"
+
+export default {
+  components: {
+    myPagination,
+    paramDetails
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          firm: false
+        };
+      }
+    },
+    mess: {
+      type: [Object, String]
+    }
+  },
+  data() {
+    return {
+      search: "", //搜索文案
+      radio: "",
+      tableData: [
+      ],
+      data: {},
+      isLoad: {
+        paramShow: false
+      },
+      page: {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      },
+    };
+  },
+  created() { },
+  mounted() { },
+  methods: {
+    changed() {
+      this.getData()
+    },
+    //查询技术参数
+    lookParam(data) {
+      this.data = data
+      this.isLoad.paramShow = true
+    },
+    getChange() {
+      if (!!this.radio) {
+        this.$emit("changeFirm", this.radio)
+        this.dialog.firm = false
+      } else {
+        this.$message("请选择型号")
+      }
+    },
+    searchKey() {
+      this.changePage()
+    },
+    changePage() {
+      this.page = {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      }
+      this.getData()
+    },
+    getData() {
+      getSpecList({
+        limit: {
+          skip: this.page.size * (this.page.currentPage - 1),
+          count: this.page.size
+        },
+        eqFamily: this.mess.deviceId,
+        name: this.search
+      }, res => {
+        this.page.total = res.totalCount
+        this.tableData = res.content
+        console.log(this.tableData)
+      })
+    }
+  },
+  watch: {
+    dialog: {
+      deep: true,
+      handler: function () {
+        if (this.dialog.firm) {
+          console.log(this.mess)
+          this.changePage()
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less">
+#firm {
+  .el-table thead {
+    tr {
+      th {
+        background-color: #f5f7fa;
+      }
+    }
+  }
+}
+</style>

+ 187 - 0
src/components/business_space/dialogs/list/guaranteeDialog.vue

@@ -0,0 +1,187 @@
+<!--
+    guarantee 保单选择
+-->
+
+<!--
+    guarantee 供应合同选择
+-->
+<template>
+  <el-dialog title="选择保单" :visible.sync="dialog.guarantee" width="600px">
+    <el-tabs type="border-card">
+      <el-tab-pane>
+        <span slot="label">选择保单</span>
+        <div id="firm">
+          <el-radio-group v-model="radio" style="width:100%;">
+            <el-table :data="tableData" style="width: 100%" height="300px">
+              <el-table-column label="保单号">
+                <template slot-scope="scope">
+                  <el-radio v-model="radio" :label="scope.row">{{scope.row.insuranceNo}}</el-radio>
+                </template>
+              </el-table-column>
+              <el-table-column label="下载">
+                <template slot-scope="scope">
+                  <el-button type="text">下载</el-button>
+                </template>
+              </el-table-column>
+            </el-table>
+          </el-radio-group>
+          <my-pagination :page="page" style="margin-top:10px;"></my-pagination>
+          <p style="height:40px;line-height:40px;text-align:center;color:#9E9E9E;">
+            保单信息有错误? 赶快去
+            <i style="color:#46B0FF;cursor: pointer;">厂家库</i>维护吧
+          </p>
+          <div class="footer">
+            <el-button
+              type="primary"
+              @click="getSelect"
+              style="display:block;margin: 10px auto 0;"
+            >确 定</el-button>
+          </div>
+        </div>
+      </el-tab-pane>
+      <el-tab-pane>
+        <span slot="label">新建保单</span>
+        <el-form label-width="80px" :rules="rules" :model="formData" ref="ruleForm">
+          <el-form-item label="保单号:" prop="name">
+            <el-input v-model="formData.name"></el-input>
+          </el-form-item>
+          <el-form-item label="保险文件">
+            <upload-files @change="changeFiles" ref="files" :keysArr="keysArr" :max="max"></upload-files>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm('ruleForm')">立即创建</el-button>
+            <el-button @click="resetForm('ruleForm')">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </el-tab-pane>
+    </el-tabs>
+  </el-dialog>
+</template>
+<script>
+import myPagination from "@/components/business_space/lib/myPagination";
+import uploadFiles from '@/components/business_space/lib/uploadFiles'
+import { getListForGuarantee, createGuarantee } from "@/api/scan/request"
+import tools from "@/utils/scan/tools"
+export default {
+  components: {
+    myPagination,
+    uploadFiles
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          guarantee: true
+        };
+      }
+    },
+    id: {
+      type: [Number, String],
+      default: 0
+    }
+  },
+  data() {
+    return {
+      search: "", //搜索文案
+      radio: "",
+      formData: {
+        name: ""
+      },
+      rules: {
+        name: { required: true, message: '请输入保单号', trigger: 'blur' },
+      },
+      tableData: [],
+      page: {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 400,
+        currentPage: 1
+      },
+      keysArr: [],
+      max: 1,
+      allData: []
+    };
+  },
+  created() { },
+  mounted() { },
+  methods: {
+    submitForm(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.createGree()
+        } else {
+          return false;
+        }
+      });
+    },
+    changeFiles(val) {
+      this.keysArr = val
+    },
+    resetForm() {
+      this.$refs.ruleForm.resetFields();
+    },
+    //新建保单
+    createGree() {
+      let param = {
+        data: {
+          venderId: this.id,
+          insuranceNo: this.formData.name,
+          projectId: this.$route.query.projId,
+          contractFile: this.keysArr[0]
+        }
+      }
+      let _this = this
+      createGuarantee(param, res => {
+        _this.$emit("change", param.data)
+        _this.dialog.guarantee = false
+      })
+    },
+    getList() {
+      this.radio = ""
+      let _this = this
+      this.page = {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      }
+      if (!!this.id) {
+        let param = {
+          data: {
+            projectId: this.$route.query.projId,
+            venderId: this.id
+          }
+        }
+        getListForGuarantee(param, res => {
+          _this.$refs.ruleForm.resetFields()
+          _this.$refs.files.resetFile()
+          _this.allData = res.content || []
+          _this.page.total = _this.allData.length
+          //   //分页
+          _this.pageChange()
+        })
+      }
+    },
+
+    getSelect() {
+      this.$emit("change", this.radio)
+      this.dialog.guarantee = false
+    },
+    pageChange() {
+      this.tableData = tools.pagination(this.page.currentPage, this.page.size, this.allData)
+    }
+  },
+  watch: {
+    dialog: {
+      deep: true,
+      handler: function (val) {
+        if (this.dialog.guarantee) {
+          this.getList()
+        }
+      }
+    }
+  }
+
+};
+</script>

+ 184 - 0
src/components/business_space/dialogs/list/insurerDialog.vue

@@ -0,0 +1,184 @@
+<!--
+    maintenance 选择供应商
+
+-->
+<template>
+  <el-dialog title="选择型号" :visible.sync="dialog.insurer" width="1000px">
+    <div>
+      <div id="firm">
+        <div class="title-search" style="margin-bottom: 10px;">
+          <el-input
+            placeholder="输入厂家名称进行查找"
+            v-model="search"
+            size="small"
+            style="width:400px;"
+            clearable
+          ></el-input>
+          <el-button size="small" @click="filterSearch">查找</el-button>
+        </div>
+        <el-radio-group v-model="radio" style="width:100%;">
+          <el-table ref="maintenance" :data="tableData" style="width: 100%" height="300px">
+            <el-table-column label="供应商名称">
+              <template slot-scope="scope">
+                <el-tooltip :content="scope.row.name" placement="top">
+                  <el-radio v-model="radio" :label="scope.row">{{scope.row.name}}</el-radio>
+                </el-tooltip>
+              </template>
+            </el-table-column>
+            <el-table-column prop="website" label="网址"></el-table-column>
+            <el-table-column prop="man" label="联系人"></el-table-column>
+            <el-table-column prop="phone" label="联系电话"></el-table-column>
+            <el-table-column prop="faxes" label="传真"></el-table-column>
+            <el-table-column prop="email" label="电子邮件"></el-table-column>
+          </el-table>
+        </el-radio-group>
+        <my-pagination :page="page" @change="changePage" style="margin-top:10px;"></my-pagination>
+        <p style="height:40px;line-height:40px;text-align:center;color:#9E9E9E;">
+          找不到想要的型号? 赶快去
+          <i style="color:#46B0FF;cursor: pointer;">厂家库</i>维护吧
+        </p>
+        <div class="footer">
+          <el-button
+            type="primary"
+            @click="getChange"
+            style="display:block;margin: 10px auto 0;"
+          >确 定</el-button>
+        </div>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+<script>
+import myPagination from "@/components/common/myPagination";
+import { getLib } from "@/api/scan/request"
+import tools from "@/utils/scan/tools"
+
+export default {
+  components: {
+    myPagination
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          insurer: true
+        };
+      }
+    }
+  },
+  data() {
+    return {
+      search: "", //搜索文案
+      radio: "",
+      tableData: [],
+      page: {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      },
+      projectId: this.$route.query.projId,
+    };
+  },
+  created() {
+    // this.getData()
+  },
+  mounted() { },
+  methods: {
+    //过滤
+    filterSearch() {
+      this.tableData = this.allData.map(item => {
+        if (item.name.indexOf(this.search) != -1) {
+          return item
+        } else {
+          return undefined
+        }
+      }).filter(cb => cb)
+    },
+    getData() {
+      console.log("insurer")
+      this.page = {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      }
+      getLib({ "type": ["insurance"] }).then(res => {
+        if (res.data.result == "success") {
+          let data = res.data.content.insurance.map(item => {
+            if (item.contacts && item.contacts.length) {
+              item.contacts.map(child => {
+                if (child.projectId == this.projectId) {
+                  item.phone = child.phone
+                  item.man = child.name
+                  item.fox = child.fox
+                  item.email = child.email
+                }
+                return child
+              })
+            }
+            if (!item.man) {
+              item.man = "--"
+              item.phone = "--"
+              item.fox = "--"
+              item.email = "--"
+            }
+            return item
+          })
+          this.allData = tools.deepCopy(data)
+          this.tableData = this.pagination(this.page.currentPage, this.page.size, this.allData)
+          this.page.total = data.length
+        } else {
+          this.$message.error("请求失败:" + res.data.resultMsg)
+        }
+      })
+    },
+
+    //分页改变
+    changePage() {
+      this.pagination(this.page.currentPage, this.page.size, this.allData)
+    },
+
+    //发生修改
+    getChange() {
+      if (!!this.radio) {
+        this.$emit("changeInsurer", this.radio)
+        this.dialog.insurer = false
+      } else {
+        this.$message("请选择一个生产厂商")
+      }
+    },
+
+    //分页事件
+    pagination(pageNo, pageSize, array) {
+      let offset = (pageNo - 1) * pageSize;
+      return offset + pageSize >= array.length
+        ? array.slice(offset, array.length)
+        : array.slice(offset, offset + pageSize);
+    }
+  },
+  watch: {
+    dialog: {
+      deep: true,
+      handler: function () {
+        if (this.dialog.insurer) {
+          this.getData()
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less">
+#firm {
+  .el-table thead {
+    tr {
+      th {
+        background-color: #f5f7fa;
+      }
+    }
+  }
+}
+</style>

+ 184 - 0
src/components/business_space/dialogs/list/maintainerDialog.vue

@@ -0,0 +1,184 @@
+<!--
+    maintenance 选择供应商
+
+-->
+<template>
+  <el-dialog title="选择维修商" :visible.sync="dialog.maintainer" width="1000px">
+    <div>
+      <div id="firm">
+        <div class="title-search" style="margin-bottom: 10px;">
+          <el-input
+            placeholder="输入厂家名称进行查找"
+            v-model="search"
+            size="small"
+            style="width:400px;"
+            clearable
+          ></el-input>
+          <el-button size="small" @click="filterSearch">查找</el-button>
+        </div>
+        <el-radio-group v-model="radio" style="width:100%;">
+          <el-table ref="maintenance" :data="tableData" style="width: 100%" height="300px">
+            <el-table-column label="维修商名称">
+              <template slot-scope="scope">
+                <el-tooltip :content="scope.row.name" placement="top">
+                  <el-radio v-model="radio" :label="scope.row">{{scope.row.name}}</el-radio>
+                </el-tooltip>
+              </template>
+            </el-table-column>
+            <el-table-column prop="website" label="网址"></el-table-column>
+            <el-table-column prop="man" label="联系人"></el-table-column>
+            <el-table-column prop="phone" label="联系电话"></el-table-column>
+            <el-table-column prop="faxes" label="传真"></el-table-column>
+            <el-table-column prop="email" label="电子邮件"></el-table-column>
+          </el-table>
+        </el-radio-group>
+        <my-pagination :page="page" @change="changePage" style="margin-top:10px;"></my-pagination>
+        <p style="height:40px;line-height:40px;text-align:center;color:#9E9E9E;">
+          找不到想要的维修商? 赶快去
+          <i style="color:#46B0FF;cursor: pointer;">厂家库</i>维护吧
+        </p>
+        <div class="footer">
+          <el-button
+            type="primary"
+            @click="getChange"
+            style="display:block;margin: 10px auto 0;"
+          >确 定</el-button>
+        </div>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+<script>
+import myPagination from "@/components/common/myPagination";
+import { getLib } from "@/api/scan/request"
+import tools from "@/utils/scan/tools"
+
+export default {
+  components: {
+    myPagination
+  },
+  props: {
+    dialog: {
+      type: Object,
+      default: function () {
+        return {
+          maintainer: true
+        };
+      }
+    }
+  },
+  data() {
+    return {
+      search: "", //搜索文案
+      radio: "",
+      tableData: [],
+      page: {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      },
+      projectId: this.$route.query.projId,
+    };
+  },
+  created() {
+    // this.getData()
+  },
+  mounted() { },
+  methods: {
+    //过滤
+    filterSearch() {
+      this.tableData = this.allData.map(item => {
+        if (item.name.indexOf(this.search) != -1) {
+          return item
+        } else {
+          return undefined
+        }
+      }).filter(cb => cb)
+    },
+    getData() {
+      this.page = {
+        size: 10,
+        sizes: [10, 30, 50, 100, 150, 200],
+        total: 0,
+        currentPage: 1
+      }
+      getLib({ "type": ["maintenance"] }).then(res => {
+        if (res.data.result == "success") {
+          let data = res.data.content.maintenance.map(item => {
+            if (item.contacts && item.contacts.length) {
+              item.contacts.map(child => {
+                if (child.projectId == this.projectId) {
+                  item.phone = child.phone
+                  item.man = child.name
+                  item.fox = child.fox
+                  item.email = child.email
+                }
+                return child
+              })
+            }
+            if (!item.man) {
+              item.man = "--"
+              item.phone = "--"
+              item.fox = "--"
+              item.email = "--"
+            }
+            return item
+          })
+          this.allData = tools.deepCopy(data)
+          this.tableData = this.pagination(this.page.currentPage, this.page.size, this.allData)
+          this.page.total = data.length
+        } else {
+          this.$message.error("请求失败:" + res.data.resultMsg)
+        }
+      })
+    },
+
+    //分页改变
+    changePage() {
+      this.pagination(this.page.currentPage, this.page.size, this.allData)
+    },
+
+    //发生修改
+    getChange() {
+      if (!!this.radio) {
+        console.log(this.radio, "radio")
+        this.$emit("changeMaintainer", this.radio)
+        this.dialog.maintainer = false
+      } else {
+        this.$message("请选择一个生产厂商")
+      }
+    },
+
+    //分页事件
+    pagination(pageNo, pageSize, array) {
+      let offset = (pageNo - 1) * pageSize;
+      return offset + pageSize >= array.length
+        ? array.slice(offset, array.length)
+        : array.slice(offset, offset + pageSize);
+    }
+  },
+  watch: {
+    dialog: {
+      deep: true,
+      handler: function () {
+        if (this.dialog.maintainer) {
+          this.getData()
+        }
+      }
+    }
+  }
+};
+</script>
+
+<style lang="less">
+#firm {
+  .el-table thead {
+    tr {
+      th {
+        background-color: #f5f7fa;
+      }
+    }
+  }
+}
+</style>

+ 88 - 0
src/components/business_space/dialogs/list/paramDetails.vue

@@ -0,0 +1,88 @@
+<template>
+  <el-dialog width="400px" title="型号参数" :visible.sync="isLoad.paramShow" append-to-body>
+    <h3 class="lag-h3">当前型号: {{data.brand || "--"}} - {{data.name || "--"}}</h3>
+    <div style="max-height: 400px;overflow-y:auto;">
+      <p
+        class="saga-poent"
+        v-for="item in labelArr"
+        v-if="!!data.infos && data.infos[item.infoPointCode]"
+      >{{item.infoPointName}}:{{data.infos ? data.infos[item.infoPointCode] || "--" : "--"}}</p>
+    </div>
+  </el-dialog>
+</template>
+<script>
+
+import { getSpaceHeader } from "@/api/scan/request"
+
+export default {
+  props: {
+    isLoad: {
+      type: Object,
+      default: function () {
+        return {
+          paramShow: false
+        }
+      }
+    },
+    data: {
+      type: Object,
+      default: function () {
+        return {
+        }
+      }
+    }
+  },
+  data() {
+    return {
+      labelArr: []
+    }
+  },
+  created() { },
+  mounted() { },
+  methods: {
+    getData() {
+      let param = {
+        ProjId: this.$route.query.projId,
+        code: this.data.eqFamily
+      }
+      getSpaceHeader(param).then(res => {
+        if (res.data.Result == "success") {
+          this.labelArr = res.data.Content.map(item => {
+            if (item.firstTag == "台账信息") {
+              return item
+            } else {
+              return undefined
+            }
+          }).filter(d => d)
+        } else {
+          this.$message.error("请求失败:" + res.data.ResultMsg)
+        }
+      }).catch(_ => {
+        this.$message.error("请求失败")
+      })
+    }
+  },
+  watch: {
+    isLoad: {
+      deep: true,
+      handler: function () {
+        if (this.isLoad.paramShow) {
+          this.getData()
+        }
+      }
+    }
+  }
+}
+</script>
+<style lang="less" scoped>
+.lag-h3 {
+  height: 35px;
+  line-height: 35px;
+  font-size: 20px;
+  font-weight: 500;
+  margin-bottom: 8px;
+}
+.saga-poent {
+  line-height: 28px;
+}
+</style>

+ 0 - 0
src/components/business_space/dialogs/list/picDialog.vue


Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff