[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    \"@vue/app\"\n  ]\n}\n"
  },
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nindent_style = space\nindent_size = 2\nend_of_line = lf\ninsert_final_newline = true\ntrim_trailing_whitespace = true\n"
  },
  {
    "path": ".eslintignore",
    "content": ""
  },
  {
    "path": ".eslintrc.js",
    "content": "module.exports = {\n  root: true,\n  'extends': [\n    'plugin:vue/essential',\n    '@vue/standard'\n  ],\n  rules: {\n    // allow async-await\n    'generator-star-spacing': 'off',\n    // allow debugger during development\n    'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',\n    'vue/no-parsing-error': [2, {\n      'x-invalid-end-tag': false\n    }],\n    'no-undef': 'off',\n    'camelcase': 'off'\n  },\n  parserOptions: {\n    parser: 'babel-eslint'\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n/dist\n\npackage-lock.json\n\n/tests/e2e/videos/\n/tests/e2e/screenshots/\n\n# local env files\n.env.local\n.env.*.local\n\n# Log files\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Editor directories and files\n.idea\n.vscode\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw*\n\nbuild/env.js\n"
  },
  {
    "path": ".postcssrc.js",
    "content": "module.exports = {\n  plugins: {\n    autoprefixer: {}\n  }\n}\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nnode_js: stable\nscript: npm run lint\nnotifications:\n  email: false\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017 TalkingData\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n    <a href=\"https://www.iviewui.com\">\n        <img width=\"200\" src=\"https://file.iviewui.com/logo-new.svg\">\n    </a>\n</p>\n\n<h1>\niView Admin\n    <h3>Vue.js 2.0 admin management system template based on iView.</h3>\n</h1>\n\n[![](https://img.shields.io/github/release/iview/iview-admin.svg)](https://github.com/iview/iview-admin/releases)\n[![](https://img.shields.io/travis/iview/iview-admin.svg?style=flat-square)](https://travis-ci.org/iview/iview-admin)\n[![vue](https://img.shields.io/badge/vue-2.5.17-brightgreen.svg?style=flat-square)](https://github.com/vuejs/vue)\n[![iview ui](https://img.shields.io/badge/iview-3.2.2-brightgreen.svg?style=flat-square)](https://github.com/iview/iview)\n[![npm](https://img.shields.io/npm/l/express.svg)]()\n\n## Introduction\n\niView Admin is a front-end management background integration solution. It based on [Vue.js](https://github.com/vuejs/vue) and use the UI Toolkit [iView](https://github.com/iview/iview).\n\n- [Document](https://lison16.github.io/iview-admin-doc/)\n- [Preview](https://admin.iviewui.com/)\n- [Base template recommends using](https://github.com/iview/iview-admin/tree/template)\n\n![image](https://file.iviewui.com/admin-dist/admin-preview.png)\n\n## Features\n\n- Login / Logout\n- Permission Authentication\n    - A list of filters\n    - Permission to switch\n- i18n\n- Components\n    - Rich Text Editor\n    - Markdown Editor\n    - City Cascader\n    - Photos preview and edit\n    - Draggable list\n    - File upload\n    - Digital gradient\n    - split-pane\n- Form\n    - The article published\n    - Workflow\n- Table\n    - Drag-and-drop sort\n    - Searchable form\n    - Table export data\n        - Export to Csv file\n        - Export to Xls file\n    - Table to picture\n- Error Page\n    - 403\n    - 404\n    - 500\n- Router\n    - Dynamic routing\n    - With reference page\n- Theme\n- Shrink the sidebar\n- Tag navigation\n- Breadcrumb navigation\n- Full screen / exit full screen\n- Lock screen\n- The message center\n- Personal center\n\n## Getting started\n```bush\n# clone the project\ngit clone https://github.com/iview/iview-admin.git\n\n// install dependencies\nnpm install\n\n// develop\nnpm run dev\n```\n\n## Build\n```bush\nnpm run build\n```\n\n## License\n[MIT](http://opensource.org/licenses/MIT)\n\nCopyright (c) 2016-present, TalkingData\n"
  },
  {
    "path": "cypress.json",
    "content": "{\n  \"pluginsFile\": \"tests/e2e/plugins/index.js\"\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"iview-admin\",\n  \"version\": \"2.0.0\",\n  \"author\": \"Lison<lison16new@163.com>\",\n  \"private\": false,\n  \"scripts\": {\n    \"dev\": \"vue-cli-service serve --open\",\n    \"build\": \"vue-cli-service build\",\n    \"lint\": \"vue-cli-service lint\",\n    \"test:unit\": \"vue-cli-service test:unit\",\n    \"test:e2e\": \"vue-cli-service test:e2e\"\n  },\n  \"dependencies\": {\n    \"axios\": \"^0.18.0\",\n    \"clipboard\": \"^2.0.0\",\n    \"codemirror\": \"^5.38.0\",\n    \"countup\": \"^1.8.2\",\n    \"cropperjs\": \"^1.2.2\",\n    \"dayjs\": \"^1.7.7\",\n    \"echarts\": \"^4.0.4\",\n    \"html2canvas\": \"^1.0.0-alpha.12\",\n    \"iview\": \"^3.2.2\",\n    \"iview-area\": \"^1.5.17\",\n    \"js-cookie\": \"^2.2.0\",\n    \"simplemde\": \"^1.11.2\",\n    \"sortablejs\": \"^1.7.0\",\n    \"tree-table-vue\": \"^1.1.0\",\n    \"v-org-tree\": \"^1.0.6\",\n    \"vue\": \"^2.5.10\",\n    \"vue-i18n\": \"^7.8.0\",\n    \"vue-router\": \"^3.0.1\",\n    \"vuedraggable\": \"^2.16.0\",\n    \"vuex\": \"^3.0.1\",\n    \"wangeditor\": \"^3.1.1\",\n    \"xlsx\": \"^0.13.3\"\n  },\n  \"devDependencies\": {\n    \"@vue/cli-plugin-babel\": \"^3.0.1\",\n    \"@vue/cli-plugin-eslint\": \"^3.0.1\",\n    \"@vue/cli-plugin-unit-mocha\": \"^3.0.1\",\n    \"@vue/cli-service\": \"^3.0.1\",\n    \"@vue/eslint-config-standard\": \"^3.0.0-beta.10\",\n    \"@vue/test-utils\": \"^1.0.0-beta.10\",\n    \"chai\": \"^4.1.2\",\n    \"eslint-plugin-cypress\": \"^2.0.1\",\n    \"less\": \"^2.7.3\",\n    \"less-loader\": \"^4.0.5\",\n    \"lint-staged\": \"^6.0.0\",\n    \"mockjs\": \"^1.0.1-beta3\",\n    \"vue-template-compiler\": \"^2.5.13\"\n  },\n  \"browserslist\": [\n    \"> 1%\",\n    \"last 2 versions\",\n    \"not ie <= 8\"\n  ],\n  \"gitHooks\": {\n    \"pre-commit\": \"lint-staged\"\n  },\n  \"lint-staged\": {\n    \"*.js\": [\n      \"vue-cli-service lint\",\n      \"git add\"\n    ],\n    \"*.vue\": [\n      \"vue-cli-service lint\",\n      \"git add\"\n    ]\n  }\n}\n"
  },
  {
    "path": "public/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\">\n    <link rel=\"icon\" href=\"<%= BASE_URL %>favicon.ico\">\n    <title></title>\n  </head>\n  <body>\n    <noscript>\n      <strong>We're sorry but iview-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>\n    </noscript>\n    <div id=\"app\"></div>\n    <!-- built files will be auto injected -->\n  </body>\n</html>\n"
  },
  {
    "path": "src/App.vue",
    "content": "<template>\n  <div id=\"app\">\n    <router-view/>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'App'\n}\n</script>\n\n<style lang=\"less\">\n.size{\n  width: 100%;\n  height: 100%;\n}\nhtml,body{\n  .size;\n  overflow: hidden;\n  margin: 0;\n  padding: 0;\n}\n#app {\n  .size;\n}\n</style>\n"
  },
  {
    "path": "src/api/data.js",
    "content": "import axios from '@/libs/api.request'\n\nexport const getTableData = () => {\n  return axios.request({\n    url: 'get_table_data',\n    method: 'get'\n  })\n}\n\nexport const getDragList = () => {\n  return axios.request({\n    url: 'get_drag_list',\n    method: 'get'\n  })\n}\n\nexport const errorReq = () => {\n  return axios.request({\n    url: 'error_url',\n    method: 'post'\n  })\n}\n\nexport const saveErrorLogger = info => {\n  return axios.request({\n    url: 'save_error_logger',\n    data: info,\n    method: 'post'\n  })\n}\n\nexport const uploadImg = formData => {\n  return axios.request({\n    url: 'image/upload',\n    data: formData\n  })\n}\n\nexport const getOrgData = () => {\n  return axios.request({\n    url: 'get_org_data',\n    method: 'get'\n  })\n}\n\nexport const getTreeSelectData = () => {\n  return axios.request({\n    url: 'get_tree_select_data',\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "src/api/routers.js",
    "content": "import axios from '@/libs/api.request'\n\nexport const getRouterReq = (access) => {\n  return axios.request({\n    url: 'get_router',\n    params: {\n      access\n    },\n    method: 'get'\n  })\n}\n"
  },
  {
    "path": "src/api/user.js",
    "content": "import axios from '@/libs/api.request'\n\nexport const login = ({ userName, password }) => {\n  const data = {\n    userName,\n    password\n  }\n  return axios.request({\n    url: 'login',\n    data,\n    method: 'post'\n  })\n}\n\nexport const getUserInfo = (token) => {\n  return axios.request({\n    url: 'get_info',\n    params: {\n      token\n    },\n    method: 'get'\n  })\n}\n\nexport const logout = (token) => {\n  return axios.request({\n    url: 'logout',\n    method: 'post'\n  })\n}\n\nexport const getUnreadCount = () => {\n  return axios.request({\n    url: 'message/count',\n    method: 'get'\n  })\n}\n\nexport const getMessage = () => {\n  return axios.request({\n    url: 'message/init',\n    method: 'get'\n  })\n}\n\nexport const getContentByMsgId = msg_id => {\n  return axios.request({\n    url: 'message/content',\n    method: 'get',\n    params: {\n      msg_id\n    }\n  })\n}\n\nexport const hasRead = msg_id => {\n  return axios.request({\n    url: 'message/has_read',\n    method: 'post',\n    data: {\n      msg_id\n    }\n  })\n}\n\nexport const removeReaded = msg_id => {\n  return axios.request({\n    url: 'message/remove_readed',\n    method: 'post',\n    data: {\n      msg_id\n    }\n  })\n}\n\nexport const restoreTrash = msg_id => {\n  return axios.request({\n    url: 'message/restore',\n    method: 'post',\n    data: {\n      msg_id\n    }\n  })\n}\n"
  },
  {
    "path": "src/assets/icons/iconfont.css",
    "content": "\n@font-face {font-family: \"iconfont\";\n  src: url('iconfont.eot?t=1541579316141'); /* IE9*/\n  src: url('iconfont.eot?t=1541579316141#iefix') format('embedded-opentype'), /* IE6-IE8 */\n  url('data:application/x-font-woff;charset=utf-8;base64,d09GRgABAAAAAAiEAAsAAAAADmgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAADMAAABCsP6z7U9TLzIAAAE8AAAARAAAAFY8eUnXY21hcAAAAYAAAACjAAACLi+YJuBnbHlmAAACJAAABAgAAAcg4dRWHmhlYWQAAAYsAAAAMQAAADYTL8piaGhlYQAABmAAAAAgAAAAJAfdA4xobXR4AAAGgAAAABQAAAAsLAD//2xvY2EAAAaUAAAAGAAAABgImgpGbWF4cAAABqwAAAAfAAAAIAEcAG5uYW1lAAAGzAAAAUUAAAJtPlT+fXBvc3QAAAgUAAAAbgAAAI54roygeJxjYGRgYOBikGPQYWB0cfMJYeBgYGGAAJAMY05meiJQDMoDyrGAaQ4gZoOIAgCKIwNPAHicY2BkYWCcwMDKwMHUyXSGgYGhH0IzvmYwYuRgYGBiYGVmwAoC0lxTGByeMTx/ytzwv4EhhrmBoRkozAiSAwDuUwzMeJzlkUEKwkAMRd/YabXFhQvxFF6qPYPrUujGY7jyIr1JoZNjtMnEhag3MOEN5MMk8D9QAoVyVSKEJwGrh6oh6wVN1iM3nc+cVImJVKdOehlklElmWdYVstp+ql8VdIv15a1NLW0zFXsO7Kjz3erH/3+rY37vr6kxnx1LKNWOJZlaxxJNnWOpSu+ot8jgqMvI6KjfyOSo88jsaAbI4tBsig89rQB4nLVUTWwbRRSeNzO767i2g7N/FP9s7MRrE5ON4/V6rSZyU0PiINSSNImES4IUoapWz6hEiqiMBDQqEojkAkiFStyKRC+9VSoFCeUEyqESVUAqEkcu3OAQb3hrJxAXwSGI3X0/szPz5vvm2x0i7O/vf8IJe5VkSJnUyUtklRBQJE1VIjRtUafkmk6pSu2ipleh4+xikkKxSksWTUeo8m8NoagpYtoslTmxrLl37z64e33esuJjU8P5Wd262LxoPVnPZ06Pxfe+C0YjkhSJygPhQCA8ABPOykwuN7NyuRvgUnAgLEnhATkaCQQiUe/7XKUyV6nQz+t2o7l66+rs7NVbq82GXTdrdjxjRGU5amTids2bUDMFtzCsqsMYMqr3IDY6OT05GjsI8Exv/6CSkOWEQigh+y3clxY5QVTcEZFIGtHLxDUJs6WsHR1y9SFKdr1HggCp3V1ICYL36OOpVmvKN9bC1u6R3vZ0qwWtVovgJfqOfUvfIYxIWL+fyETHNVJqSkIT1JTjW8ZWh3yDJDz0ctvsyt51etvrg9/QHhqGlzMM+vbmizPnDWPLMNbW19e7tffvsBzL99aWEfBRY46t+tbe3PypXv/IMDYN43WsQBe9HL2NC33RuxABrPsG+xH3o4bVRE2KgCRqulbWNf8W/UYVHM129aKra24VshZkq+CWD/Oy6Xt8cGYEthgHVlVliCfynAlqjo6oysTKlYUAD4docMI5/1ZioN+GwZNBcTwWUmTdBUqhTwX29QebXzF4An4JJMzwfMl+WQ01+IlQZVR4yhie53ycA16pOI/ODiYNGK4MChdCgXNnX5gIJXPCSYnf2OF850aQ+zJIyOs+u8+mMO8jQdwtg1TIWVRjKAnFcslMi8KfGUPoSUCergUyUk77dMyS69Ms6tijKZKYwUGKbpfdzu+iYeZYAHMFiOVi+MD7h9mb99qC0L7X8c+XatMfTj97KZ5IxJt/pd43tYYQKEjAnXMOB6kQEBrwg+LPjindAPOHNdC3q3ait0I3/ZIunZEARLNYNEUA6czSP3N/7j9wz6ZESdX0VNl1zGNS/szbQaQSIGk4DtVPcZf8AgXpf9A2OyTit5s2syZmand46bhEe2WtodLHkvaoqtTXuXN2/c42WADP9HGfbUcUW7JgqHss4xHtlMys679FqUomdP9VJBQBdnlPABBubpuNwqnmQj6/0HwNQzKxDUJFgKiXurBG6dqFjmeBzsvtRPJgGIZThYa5fdOvsReOticPh6JHHXxsv7ItJpOniYPYsmZ/x0QD/o5P105DeQwF6MH33ogoLi+KQp7zpY3HQV5bFMURzheXeds7gpP+jKNXljjHuYvXHke7cdCxLLZf6YX7B63UcCV4nGNgZGBgAOKAN2ZR8fw2Xxm4WRhA4AbHYRMY/f///1oWBuYGIJeDgQkkCgAvWgs2AAAAeJxjYGRgYG7438AQw8Lw/z8DAwsDA1AEBXADAHXiBHJ4nGNhYGBgYfj/nwVM48cATwECKwAAAAAAjAC6AOgBFAGAAf4CbgLqAzgDkHicY2BkYGDgZkhiYGcAASYg5gJCBob/YD4DABOmAYsAeJxlj01OwzAQhV/6B6QSqqhgh+QFYgEo/RGrblhUavdddN+mTpsqiSPHrdQDcB6OwAk4AtyAO/BIJ5s2lsffvHljTwDc4Acejt8t95E9XDI7cg0XuBeuU38QbpBfhJto41W4Rf1N2MczpsJtdGF5g9e4YvaEd2EPHXwI13CNT+E69S/hBvlbuIk7/Aq30PHqwj7mXle4jUcv9sdWL5xeqeVBxaHJIpM5v4KZXu+Sha3S6pxrW8QmU4OgX0lTnWlb3VPs10PnIhVZk6oJqzpJjMqt2erQBRvn8lGvF4kehCblWGP+tsYCjnEFhSUOjDFCGGSIyujoO1Vm9K+xQ8Jee1Y9zed0WxTU/3OFAQL0z1xTurLSeTpPgT1fG1J1dCtuy56UNJFezUkSskJe1rZUQuoBNmVXjhF6XNGJPyhnSP8ACVpuyAAAAHicbYhdDoIwEAb3a6k/YIIX8VArWewmdJFWJOnpJTG+OQ+TzJCjLy39p4ODR4OAA4444YwWHS7U3IVzn6Voldtb8ksHnvohrlqjjmw1rmzXsvdT7fEbblnCmOfNfJIYStJJfGIL27yb6AOCGR89AAA=') format('woff'),\n  url('iconfont.ttf?t=1541579316141') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/\n  url('iconfont.svg?t=1541579316141#iconfont') format('svg'); /* iOS 4.1- */\n}\n\n.iconfont {\n  font-family:\"iconfont\" !important;\n  font-size:16px;\n  font-style:normal;\n  -webkit-font-smoothing: antialiased;\n  -moz-osx-font-smoothing: grayscale;\n}\n\n.icon-bear:before { content: \"\\e600\"; }\n\n.icon-resize-vertical:before { content: \"\\e7c3\"; }\n\n.icon-chuizhifanzhuan:before { content: \"\\e661\"; }\n\n.icon-shuipingfanzhuan:before { content: \"\\e662\"; }\n\n.icon-qq:before { content: \"\\e609\"; }\n\n.icon-frown:before { content: \"\\e77e\"; }\n\n.icon-meh:before { content: \"\\e780\"; }\n\n.icon-smile:before { content: \"\\e783\"; }\n\n.icon-man:before { content: \"\\e7e2\"; }\n\n.icon-woman:before { content: \"\\e7e5\"; }\n\n"
  },
  {
    "path": "src/components/charts/bar.vue",
    "content": "<template>\n  <div ref=\"dom\" class=\"charts chart-bar\"></div>\n</template>\n\n<script>\nimport echarts from 'echarts'\nimport tdTheme from './theme.json'\nimport { on, off } from '@/libs/tools'\necharts.registerTheme('tdTheme', tdTheme)\nexport default {\n  name: 'ChartBar',\n  props: {\n    value: Object,\n    text: String,\n    subtext: String\n  },\n  data () {\n    return {\n      dom: null\n    }\n  },\n  methods: {\n    resize () {\n      this.dom.resize()\n    }\n  },\n  mounted () {\n    this.$nextTick(() => {\n      let xAxisData = Object.keys(this.value)\n      let seriesData = Object.values(this.value)\n      let option = {\n        title: {\n          text: this.text,\n          subtext: this.subtext,\n          x: 'center'\n        },\n        xAxis: {\n          type: 'category',\n          data: xAxisData\n        },\n        yAxis: {\n          type: 'value'\n        },\n        series: [{\n          data: seriesData,\n          type: 'bar'\n        }]\n      }\n      this.dom = echarts.init(this.$refs.dom, 'tdTheme')\n      this.dom.setOption(option)\n      on(window, 'resize', this.resize)\n    })\n  },\n  beforeDestroy () {\n    off(window, 'resize', this.resize)\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/charts/index.js",
    "content": "import ChartPie from './pie.vue'\nimport ChartBar from './bar.vue'\nexport { ChartPie, ChartBar }\n"
  },
  {
    "path": "src/components/charts/pie.vue",
    "content": "<template>\n  <div ref=\"dom\" class=\"charts chart-pie\"></div>\n</template>\n\n<script>\nimport echarts from 'echarts'\nimport tdTheme from './theme.json'\nimport { on, off } from '@/libs/tools'\necharts.registerTheme('tdTheme', tdTheme)\nexport default {\n  name: 'ChartPie',\n  props: {\n    value: Array,\n    text: String,\n    subtext: String\n  },\n  data () {\n    return {\n      dom: null\n    }\n  },\n  methods: {\n    resize () {\n      this.dom.resize()\n    }\n  },\n  mounted () {\n    this.$nextTick(() => {\n      let legend = this.value.map(_ => _.name)\n      let option = {\n        title: {\n          text: this.text,\n          subtext: this.subtext,\n          x: 'center'\n        },\n        tooltip: {\n          trigger: 'item',\n          formatter: '{a} <br/>{b} : {c} ({d}%)'\n        },\n        legend: {\n          orient: 'vertical',\n          left: 'left',\n          data: legend\n        },\n        series: [\n          {\n            type: 'pie',\n            radius: '55%',\n            center: ['50%', '60%'],\n            data: this.value,\n            itemStyle: {\n              emphasis: {\n                shadowBlur: 10,\n                shadowOffsetX: 0,\n                shadowColor: 'rgba(0, 0, 0, 0.5)'\n              }\n            }\n          }\n        ]\n      }\n      this.dom = echarts.init(this.$refs.dom, 'tdTheme')\n      this.dom.setOption(option)\n      on(window, 'resize', this.resize)\n    })\n  },\n  beforeDestroy () {\n    off(window, 'resize', this.resize)\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/charts/theme.json",
    "content": "\n{\n    \"color\": [\n        \"#2d8cf0\",\n        \"#19be6b\",\n        \"#ff9900\",\n        \"#E46CBB\",\n        \"#9A66E4\",\n        \"#ed3f14\"\n    ],\n    \"backgroundColor\": \"rgba(0,0,0,0)\",\n    \"textStyle\": {},\n    \"title\": {\n        \"textStyle\": {\n            \"color\": \"#516b91\"\n        },\n        \"subtextStyle\": {\n            \"color\": \"#93b7e3\"\n        }\n    },\n    \"line\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": \"2\"\n            }\n        },\n        \"lineStyle\": {\n            \"normal\": {\n                \"width\": \"2\"\n            }\n        },\n        \"symbolSize\": \"6\",\n        \"symbol\": \"emptyCircle\",\n        \"smooth\": true\n    },\n    \"radar\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": \"2\"\n            }\n        },\n        \"lineStyle\": {\n            \"normal\": {\n                \"width\": \"2\"\n            }\n        },\n        \"symbolSize\": \"6\",\n        \"symbol\": \"emptyCircle\",\n        \"smooth\": true\n    },\n    \"bar\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"barBorderWidth\": 0,\n                \"barBorderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"barBorderWidth\": 0,\n                \"barBorderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"pie\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"scatter\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"boxplot\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"parallel\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"sankey\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"funnel\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"gauge\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            },\n            \"emphasis\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        }\n    },\n    \"candlestick\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"color\": \"#edafda\",\n                \"color0\": \"transparent\",\n                \"borderColor\": \"#d680bc\",\n                \"borderColor0\": \"#8fd3e8\",\n                \"borderWidth\": \"2\"\n            }\n        }\n    },\n    \"graph\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"borderWidth\": 0,\n                \"borderColor\": \"#ccc\"\n            }\n        },\n        \"lineStyle\": {\n            \"normal\": {\n                \"width\": 1,\n                \"color\": \"#aaa\"\n            }\n        },\n        \"symbolSize\": \"6\",\n        \"symbol\": \"emptyCircle\",\n        \"smooth\": true,\n        \"color\": [\n            \"#2d8cf0\",\n            \"#19be6b\",\n            \"#f5ae4a\",\n            \"#9189d5\",\n            \"#56cae2\",\n            \"#cbb0e3\"\n        ],\n        \"label\": {\n            \"normal\": {\n                \"textStyle\": {\n                    \"color\": \"#eee\"\n                }\n            }\n        }\n    },\n    \"map\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"areaColor\": \"#f3f3f3\",\n                \"borderColor\": \"#516b91\",\n                \"borderWidth\": 0.5\n            },\n            \"emphasis\": {\n                \"areaColor\": \"rgba(165,231,240,1)\",\n                \"borderColor\": \"#516b91\",\n                \"borderWidth\": 1\n            }\n        },\n        \"label\": {\n            \"normal\": {\n                \"textStyle\": {\n                    \"color\": \"#000\"\n                }\n            },\n            \"emphasis\": {\n                \"textStyle\": {\n                    \"color\": \"rgb(81,107,145)\"\n                }\n            }\n        }\n    },\n    \"geo\": {\n        \"itemStyle\": {\n            \"normal\": {\n                \"areaColor\": \"#f3f3f3\",\n                \"borderColor\": \"#516b91\",\n                \"borderWidth\": 0.5\n            },\n            \"emphasis\": {\n                \"areaColor\": \"rgba(165,231,240,1)\",\n                \"borderColor\": \"#516b91\",\n                \"borderWidth\": 1\n            }\n        },\n        \"label\": {\n            \"normal\": {\n                \"textStyle\": {\n                    \"color\": \"#000\"\n                }\n            },\n            \"emphasis\": {\n                \"textStyle\": {\n                    \"color\": \"rgb(81,107,145)\"\n                }\n            }\n        }\n    },\n    \"categoryAxis\": {\n        \"axisLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": \"#cccccc\"\n            }\n        },\n        \"axisTick\": {\n            \"show\": false,\n            \"lineStyle\": {\n                \"color\": \"#333\"\n            }\n        },\n        \"axisLabel\": {\n            \"show\": true,\n            \"textStyle\": {\n                \"color\": \"#999999\"\n            }\n        },\n        \"splitLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": [\n                    \"#eeeeee\"\n                ]\n            }\n        },\n        \"splitArea\": {\n            \"show\": false,\n            \"areaStyle\": {\n                \"color\": [\n                    \"rgba(250,250,250,0.05)\",\n                    \"rgba(200,200,200,0.02)\"\n                ]\n            }\n        }\n    },\n    \"valueAxis\": {\n        \"axisLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": \"#cccccc\"\n            }\n        },\n        \"axisTick\": {\n            \"show\": false,\n            \"lineStyle\": {\n                \"color\": \"#333\"\n            }\n        },\n        \"axisLabel\": {\n            \"show\": true,\n            \"textStyle\": {\n                \"color\": \"#999999\"\n            }\n        },\n        \"splitLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": [\n                    \"#eeeeee\"\n                ]\n            }\n        },\n        \"splitArea\": {\n            \"show\": false,\n            \"areaStyle\": {\n                \"color\": [\n                    \"rgba(250,250,250,0.05)\",\n                    \"rgba(200,200,200,0.02)\"\n                ]\n            }\n        }\n    },\n    \"logAxis\": {\n        \"axisLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": \"#cccccc\"\n            }\n        },\n        \"axisTick\": {\n            \"show\": false,\n            \"lineStyle\": {\n                \"color\": \"#333\"\n            }\n        },\n        \"axisLabel\": {\n            \"show\": true,\n            \"textStyle\": {\n                \"color\": \"#999999\"\n            }\n        },\n        \"splitLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": [\n                    \"#eeeeee\"\n                ]\n            }\n        },\n        \"splitArea\": {\n            \"show\": false,\n            \"areaStyle\": {\n                \"color\": [\n                    \"rgba(250,250,250,0.05)\",\n                    \"rgba(200,200,200,0.02)\"\n                ]\n            }\n        }\n    },\n    \"timeAxis\": {\n        \"axisLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": \"#cccccc\"\n            }\n        },\n        \"axisTick\": {\n            \"show\": false,\n            \"lineStyle\": {\n                \"color\": \"#333\"\n            }\n        },\n        \"axisLabel\": {\n            \"show\": true,\n            \"textStyle\": {\n                \"color\": \"#999999\"\n            }\n        },\n        \"splitLine\": {\n            \"show\": true,\n            \"lineStyle\": {\n                \"color\": [\n                    \"#eeeeee\"\n                ]\n            }\n        },\n        \"splitArea\": {\n            \"show\": false,\n            \"areaStyle\": {\n                \"color\": [\n                    \"rgba(250,250,250,0.05)\",\n                    \"rgba(200,200,200,0.02)\"\n                ]\n            }\n        }\n    },\n    \"toolbox\": {\n        \"iconStyle\": {\n            \"normal\": {\n                \"borderColor\": \"#999\"\n            },\n            \"emphasis\": {\n                \"borderColor\": \"#666\"\n            }\n        }\n    },\n    \"legend\": {\n        \"textStyle\": {\n            \"color\": \"#999999\"\n        }\n    },\n    \"tooltip\": {\n        \"axisPointer\": {\n            \"lineStyle\": {\n                \"color\": \"#ccc\",\n                \"width\": 1\n            },\n            \"crossStyle\": {\n                \"color\": \"#ccc\",\n                \"width\": 1\n            }\n        }\n    },\n    \"timeline\": {\n        \"lineStyle\": {\n            \"color\": \"#8fd3e8\",\n            \"width\": 1\n        },\n        \"itemStyle\": {\n            \"normal\": {\n                \"color\": \"#8fd3e8\",\n                \"borderWidth\": 1\n            },\n            \"emphasis\": {\n                \"color\": \"#8fd3e8\"\n            }\n        },\n        \"controlStyle\": {\n            \"normal\": {\n                \"color\": \"#8fd3e8\",\n                \"borderColor\": \"#8fd3e8\",\n                \"borderWidth\": 0.5\n            },\n            \"emphasis\": {\n                \"color\": \"#8fd3e8\",\n                \"borderColor\": \"#8fd3e8\",\n                \"borderWidth\": 0.5\n            }\n        },\n        \"checkpointStyle\": {\n            \"color\": \"#8fd3e8\",\n            \"borderColor\": \"rgba(138,124,168,0.37)\"\n        },\n        \"label\": {\n            \"normal\": {\n                \"textStyle\": {\n                    \"color\": \"#8fd3e8\"\n                }\n            },\n            \"emphasis\": {\n                \"textStyle\": {\n                    \"color\": \"#8fd3e8\"\n                }\n            }\n        }\n    },\n    \"visualMap\": {\n        \"color\": [\n            \"#516b91\",\n            \"#59c4e6\",\n            \"#a5e7f0\"\n        ]\n    },\n    \"dataZoom\": {\n        \"backgroundColor\": \"rgba(0,0,0,0)\",\n        \"dataBackgroundColor\": \"rgba(255,255,255,0.3)\",\n        \"fillerColor\": \"rgba(167,183,204,0.4)\",\n        \"handleColor\": \"#a7b7cc\",\n        \"handleSize\": \"100%\",\n        \"textStyle\": {\n            \"color\": \"#333\"\n        }\n    },\n    \"markPoint\": {\n        \"label\": {\n            \"normal\": {\n                \"textStyle\": {\n                    \"color\": \"#eee\"\n                }\n            },\n            \"emphasis\": {\n                \"textStyle\": {\n                    \"color\": \"#eee\"\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/components/common/common.less",
    "content": ".no-select{\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n"
  },
  {
    "path": "src/components/common/util.js",
    "content": "export const showTitle = (item, vm) => {\n  return vm.$config.useI18n ? vm.$t(item.name) : ((item.meta && item.meta.title) || item.name)\n}\n"
  },
  {
    "path": "src/components/common-icon/common-icon.vue",
    "content": "<template>\n  <component :is=\"iconType\" :type=\"iconName\" :color=\"iconColor\" :size=\"iconSize\"/>\n</template>\n\n<script>\nimport Icons from '_c/icons'\nexport default {\n  name: 'CommonIcon',\n  components: { Icons },\n  props: {\n    type: {\n      type: String,\n      required: true\n    },\n    color: String,\n    size: Number\n  },\n  computed: {\n    iconType () {\n      return this.type.indexOf('_') === 0 ? 'Icons' : 'Icon'\n    },\n    iconName () {\n      return this.iconType === 'Icons' ? this.getCustomIconName(this.type) : this.type\n    },\n    iconSize () {\n      return this.size || (this.iconType === 'Icons' ? 12 : undefined)\n    },\n    iconColor () {\n      return this.color || ''\n    }\n  },\n  methods: {\n    getCustomIconName (iconName) {\n      return iconName.slice(1)\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/components/common-icon/index.js",
    "content": "import CommonIcon from './common-icon.vue'\nexport default CommonIcon\n"
  },
  {
    "path": "src/components/count-to/count-to.vue",
    "content": "<template>\n  <div class=\"count-to-wrapper\">\n    <slot name=\"left\"/>\n    <p class=\"content-outer\"><span :class=\"['count-to-count-text', countClass]\" :id=\"counterId\">{{ init }}</span><i :class=\"['count-to-unit-text', unitClass]\">{{ unitText }}</i></p>\n    <slot name=\"right\"/>\n  </div>\n</template>\n\n<script>\nimport CountUp from 'countup'\nimport './index.less'\nexport default {\n  name: 'CountTo',\n  props: {\n    init: {\n      type: Number,\n      default: 0\n    },\n    /**\n     * @description 起始值，即动画开始前显示的数值\n     */\n    startVal: {\n      type: Number,\n      default: 0\n    },\n    /**\n     * @description 结束值，即动画结束后显示的数值\n     */\n    end: {\n      type: Number,\n      required: true\n    },\n    /**\n     * @description 保留几位小数\n     */\n    decimals: {\n      type: Number,\n      default: 0\n    },\n    /**\n     * @description 分隔整数和小数的符号，默认是小数点\n     */\n    decimal: {\n      type: String,\n      default: '.'\n    },\n    /**\n     * @description 动画持续的时间，单位是秒\n     */\n    duration: {\n      type: Number,\n      default: 2\n    },\n    /**\n     * @description 动画延迟开始的时间，单位是秒\n     */\n    delay: {\n      type: Number,\n      default: 0\n    },\n    /**\n     * @description 是否禁用easing动画效果\n     */\n    uneasing: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * @description 是否使用分组，分组后每三位会用一个符号分隔\n     */\n    usegroup: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * @description 用于分组(usegroup)的符号\n     */\n    separator: {\n      type: String,\n      default: ','\n    },\n    /**\n     * @description 是否简化显示，设为true后会使用unit单位来做相关省略\n     */\n    simplify: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * @description 自定义单位，如[3, 'K+'], [6, 'M+']即大于3位数小于6位数的用k+来做省略\n     *              1000即显示为1K+\n     */\n    unit: {\n      type: Array,\n      default () {\n        return [[3, 'K+'], [6, 'M+'], [9, 'B+']]\n      }\n    },\n    countClass: {\n      type: String,\n      default: ''\n    },\n    unitClass: {\n      type: String,\n      default: ''\n    }\n  },\n  data () {\n    return {\n      counter: null,\n      unitText: ''\n    }\n  },\n  computed: {\n    counterId () {\n      return `count_to_${this._uid}`\n    }\n  },\n  methods: {\n    getHandleVal (val, len) {\n      return {\n        endVal: parseInt(val / Math.pow(10, this.unit[len - 1][0])),\n        unitText: this.unit[len - 1][1]\n      }\n    },\n    transformValue (val) {\n      let len = this.unit.length\n      let res = {\n        endVal: 0,\n        unitText: ''\n      }\n      if (val < Math.pow(10, this.unit[0][0])) res.endVal = val\n      else {\n        for (let i = 1; i < len; i++) {\n          if (val >= Math.pow(10, this.unit[i - 1][0]) && val < Math.pow(10, this.unit[i][0])) res = this.getHandleVal(val, i)\n        }\n      }\n      if (val > Math.pow(10, this.unit[len - 1][0])) res = this.getHandleVal(val, len)\n      return res\n    },\n    getValue (val) {\n      let res = 0\n      if (this.simplify) {\n        let { endVal, unitText } = this.transformValue(val)\n        this.unitText = unitText\n        res = endVal\n      } else {\n        res = val\n      }\n      return res\n    }\n  },\n  mounted () {\n    this.$nextTick(() => {\n      let endVal = this.getValue(this.end)\n      this.counter = new CountUp(this.counterId, this.startVal, endVal, this.decimals, this.duration, {\n        useEasing: !this.uneasing,\n        useGrouping: this.useGroup,\n        separator: this.separator,\n        decimal: this.decimal\n      })\n      setTimeout(() => {\n        if (!this.counter.error) this.counter.start()\n      }, this.delay)\n    })\n  },\n  watch: {\n    end (newVal) {\n      let endVal = this.getValue(newVal)\n      this.counter.update(endVal)\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/count-to/index.js",
    "content": "import countTo from './count-to.vue'\nexport default countTo\n"
  },
  {
    "path": "src/components/count-to/index.less",
    "content": "@prefix: ~\"count-to\";\n\n.@{prefix}-wrapper{\n  .content-outer{\n    display: inline-block;\n    .@{prefix}-unit-text{\n      font-style: normal;\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/cropper/index.js",
    "content": "import Cropper from './index.vue'\nexport default Cropper\n"
  },
  {
    "path": "src/components/cropper/index.less",
    "content": ".bg{\n  background-image: url(\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC\")\n}\n.cropper-wrapper{\n  width: 600px;\n  height: 340px;\n  .img-box{\n    height: 340px;\n    width: 430px;\n    border: 1px solid #ebebeb;\n    display: inline-block;\n    .bg;\n    img{\n      max-width: 100%;\n      display: block;\n    }\n  }\n  .right-con{\n    display: inline-block;\n    width: 170px;\n    vertical-align: top;\n    box-sizing: border-box;\n    padding: 0 10px;\n    .preview-box{\n      height: 150px !important;\n      width: 100% !important;\n      overflow: hidden;\n      border: 1px solid #ebebeb;\n      .bg;\n    }\n    .button-box{\n      padding: 10px 0 0;\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/cropper/index.vue",
    "content": "<template>\n  <div class=\"cropper-wrapper\">\n    <div class=\"img-box\">\n      <img class=\"cropper-image\" :id=\"imgId\" alt=\"\">\n    </div>\n    <div class=\"right-con\">\n      <div v-if=\"preview\" class=\"preview-box\" :id=\"previewId\"></div>\n      <div class=\"button-box\">\n        <slot>\n          <Upload action=\"image/upload\" :before-upload=\"beforeUpload\">\n            <Button style=\"width: 150px;\" type=\"primary\">上传图片</Button>\n          </Upload>\n        </slot>\n        <div v-show=\"insideSrc\">\n          <Button type=\"primary\" @click=\"rotate\">\n            <Icon type=\"md-refresh\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"shrink\">\n            <Icon type=\"md-remove\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"magnify\">\n            <Icon type=\"md-add\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"scale('X')\">\n            <Icon custom=\"iconfont icon-shuipingfanzhuan\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"scale('Y')\">\n            <Icon custom=\"iconfont icon-chuizhifanzhuan\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"move(0, -moveStep)\">\n            <Icon type=\"md-arrow-round-up\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"move(-moveStep, 0)\">\n            <Icon type=\"md-arrow-round-back\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"move(0, moveStep)\">\n            <Icon type=\"md-arrow-round-down\" :size=\"18\"/>\n          </Button>\n          <Button type=\"primary\" @click=\"move(moveStep, 0)\">\n            <Icon type=\"md-arrow-round-forward\" :size=\"18\"/>\n          </Button>\n          <Button style=\"width: 150px;margin-top: 10px;\" type=\"primary\" @click=\"crop\">{{ cropButtonText }}</Button>\n        </div>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport Cropper from 'cropperjs'\nimport './index.less'\nimport 'cropperjs/dist/cropper.min.css'\nexport default {\n  name: 'Cropper',\n  props: {\n    src: {\n      type: String,\n      default: ''\n    },\n    preview: {\n      type: Boolean,\n      default: true\n    },\n    moveStep: {\n      type: Number,\n      default: 4\n    },\n    cropButtonText: {\n      type: String,\n      default: '裁剪'\n    }\n  },\n  data () {\n    return {\n      cropper: null,\n      insideSrc: ''\n    }\n  },\n  computed: {\n    imgId () {\n      return `cropper${this._uid}`\n    },\n    previewId () {\n      return `cropper_preview${this._uid}`\n    }\n  },\n  watch: {\n    src (src) {\n      this.replace(src)\n    },\n    insideSrc (src) {\n      this.replace(src)\n    }\n  },\n  methods: {\n    beforeUpload (file) {\n      const reader = new FileReader()\n      reader.readAsDataURL(file)\n      reader.onload = (event) => {\n        this.insideSrc = event.srcElement.result\n      }\n      return false\n    },\n    replace (src) {\n      this.cropper.replace(src)\n      this.insideSrc = src\n    },\n    rotate () {\n      this.cropper.rotate(90)\n    },\n    shrink () {\n      this.cropper.zoom(-0.1)\n    },\n    magnify () {\n      this.cropper.zoom(0.1)\n    },\n    scale (d) {\n      this.cropper[`scale${d}`](-this.cropper.getData()[`scale${d}`])\n    },\n    move (...argu) {\n      this.cropper.move(...argu)\n    },\n    crop () {\n      this.cropper.getCroppedCanvas().toBlob(blob => {\n        this.$emit('on-crop', blob)\n      })\n    }\n  },\n  mounted () {\n    this.$nextTick(() => {\n      let dom = document.getElementById(this.imgId)\n      this.cropper = new Cropper(dom, {\n        preview: `#${this.previewId}`,\n        checkCrossOrigin: true\n      })\n    })\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/drag-drawer/drag-drawer-trigger.vue",
    "content": "<template>\n  <div :class=\"`${prefix}-move-trigger`\">\n    <div :class=\"`${prefix}-move-trigger-point`\">\n      <i></i><i></i><i></i><i></i><i></i>\n    </div>\n  </div>\n</template>\n\n<script>\nimport Mixin from './mixin'\nexport default {\n  name: 'DragDrawerTrigger',\n  mixins: [Mixin]\n}\n</script>\n\n<style>\n</style>\n"
  },
  {
    "path": "src/components/drag-drawer/drag-drawer.vue",
    "content": "<template>\n  <Drawer ref=\"drawerWrapper\"\n          :value=\"value\"\n          @input=\"handleInput\"\n          :width=\"width\"\n          :class-name=\"outerClasses\"\n          v-bind=\"$attrs\"\n          v-on=\"$listeners\">\n    <!-- 所有插槽内容显示在这里 ↓ -->\n\n    <template v-for=\"(slots, slotsName) in $slots\">\n      <template v-if=\"slotsName !== 'default'\">\n        <render-dom v-for=\"(render, index) in slots\"\n                    :key=\"`b_drawer_${slotsName}_${index}`\"\n                    :render=\"() => render\"\n                    :slot=\"slotsName\">\n        </render-dom>\n      </template>\n      <template v-else>\n        <div :class=\"`${prefix}-body-wrapper`\"\n             :key=\"`b_drawer_${slotsName}`\">\n          <render-dom v-for=\"(render, index) in slots\"\n                      :key=\"`b_drawer_${slotsName}_${index}`\"\n                      :render=\"() => render\"\n                      :slot=\"slotsName\">\n          </render-dom>\n        </div>\n      </template>\n    </template>\n    <!-- 所有插槽内容显示在这里 ↑ -->\n    <div v-if=\"draggable\"\n         :style=\"triggerStyle\"\n         :class=\"`${prefix}-trigger-wrapper`\"\n         @mousedown=\"handleTriggerMousedown\">\n      <slot name=\"trigger\">\n        <drag-drawer-trigger></drag-drawer-trigger>\n      </slot>\n    </div>\n    <div v-if=\"$slots.footer\"\n         :class=\"`${prefix}-footer`\">\n      <slot name=\"footer\"></slot>\n    </div>\n  </Drawer>\n</template>\n\n<script>\nimport RenderDom from '@/libs/render-dom'\nimport DragDrawerTrigger from './drag-drawer-trigger.vue'\nimport Mixin from './mixin'\nimport { on, off } from '@/libs/tools'\nimport './index.less'\nexport default {\n  name: 'BDrawer',\n  components: {\n    RenderDom,\n    DragDrawerTrigger\n  },\n  mixins: [Mixin],\n  props: {\n    value: {\n      type: Boolean,\n      default: false\n    },\n    width: {\n      type: [String, Number],\n      default: 256\n    },\n    // 是否可拖动修改宽度\n    draggable: {\n      type: Boolean,\n      default: false\n    },\n    // 最小拖动宽度\n    minWidth: {\n      type: [String, Number],\n      default: 256\n    }\n  },\n  data () {\n    return {\n      canMove: false,\n      wrapperWidth: 0,\n      wrapperLeft: 0\n    }\n  },\n  computed: {\n    outerClasses () {\n      const classesArray = [\n        `${this.prefix}-wrapper`,\n        this.canMove ? 'no-select pointer-events-none' : ''\n      ]\n      return classesArray.join(' ')\n    },\n    placement () {\n      return this.$attrs.placement\n    },\n    innerWidth () {\n      const width = this.width\n      return width <= 100 ? (this.wrapperWidth * width) / 100 : width\n    },\n    triggerStyle () {\n      return {\n        [this.placement]: `${this.innerWidth}px`,\n        position: this.$attrs.inner ? 'absolute' : 'fixed'\n      }\n    }\n  },\n  methods: {\n    handleInput (status) {\n      this.$emit('input', status)\n    },\n    handleTriggerMousedown (event) {\n      this.canMove = true\n      this.$emit('on-resize-start')\n      // 防止鼠标选中抽屉中文字，造成拖动trigger触发浏览器原生拖动行为\n      window.getSelection().removeAllRanges()\n    },\n    handleMousemove (event) {\n      if (!this.canMove) return\n      // 更新容器宽度和距离左侧页面距离，如果是window则距左侧距离为0\n      this.setWrapperWidth()\n      const left = event.pageX - this.wrapperLeft\n      // 如果抽屉方向为右边，宽度计算需用容器宽度减去left\n      let width = this.placement === 'right' ? this.wrapperWidth - left : left\n      // 限定做小宽度\n      width = Math.max(width, parseFloat(this.minWidth))\n      event.atMin = width === parseFloat(this.minWidth)\n      // 如果当前width不大于100，视为百分比\n      if (width <= 100) width = (width / this.wrapperWidth) * 100\n      this.$emit('update:width', parseInt(width))\n      this.$emit('on-resize', event)\n    },\n    handleMouseup (event) {\n      this.canMove = false\n      this.$emit('on-resize-end')\n    },\n    setWrapperWidth () {\n      const {\n        width,\n        left\n      } = this.$refs.drawerWrapper.$el.getBoundingClientRect()\n      this.wrapperWidth = width\n      this.wrapperLeft = left\n    }\n  },\n  mounted () {\n    on(document, 'mousemove', this.handleMousemove)\n    on(document, 'mouseup', this.handleMouseup)\n    this.setWrapperWidth()\n  },\n  beforeDestroy () {\n    off(document, 'mousemove', this.handleMousemove)\n    off(document, 'mouseup', this.handleMouseup)\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/drag-drawer/index.js",
    "content": "import DragDrawer from './drag-drawer.vue'\nexport default DragDrawer\n"
  },
  {
    "path": "src/components/drag-drawer/index.less",
    "content": "@prefix: ~\"drag-drawer\";\n@drag-drawer-trigger-height: 100px;\n@drag-drawer-trigger-width: 8px;\n\n.@{prefix}-wrapper{\n  &.no-select{\n    user-select: none;\n  }\n  &.pointer-events-none{\n    pointer-events: none;\n    & .@{prefix}-trigger-wrapper{\n      pointer-events: all;\n    }\n  }\n  .ivu-drawer{\n    &-header{\n      overflow: hidden !important;\n      box-sizing: border-box;\n    }\n    &-body{\n      padding: 0;\n      overflow: visible;\n      position: static;\n      display: flex;\n      flex-direction: column;\n    }\n  }\n  .@{prefix}-body-wrapper{\n    width: 100%;\n    height: 100%;\n    padding: 16px;\n    overflow: auto;\n  }\n  .@{prefix}-trigger-wrapper{\n    top: 0;\n    height: 100%;\n    width: 0;\n    .@{prefix}-move-trigger{\n      position: absolute;\n      top: 50%;\n      height: @drag-drawer-trigger-height;\n      width: @drag-drawer-trigger-width;\n      background: rgb(243, 243, 243);\n      transform: translate(-50%, -50%);\n      border-radius: ~\"4px / 6px\";\n      box-shadow: 0 0 1px 1px rgba(0, 0, 0, .2);\n      line-height: @drag-drawer-trigger-height;\n      cursor: col-resize;\n      &-point{\n        display: inline-block;\n        width: 50%;\n        transform: translateX(50%);\n        i{\n          display: block;\n          border-bottom: 1px solid rgb(192, 192, 192);\n          padding-bottom: 2px;\n        }\n      }\n    }\n  }\n  .@{prefix}-footer{\n    flex-grow: 1;\n    width: 100%;\n    bottom: 0;\n    left: 0;\n    border-top: 1px solid #e8e8e8;\n    padding: 10px 16px;\n    background: #fff;\n  }\n}\n"
  },
  {
    "path": "src/components/drag-drawer/mixin.js",
    "content": "export default {\n  data () {\n    return {\n      prefix: 'drag-drawer'\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/drag-list/drag-list.vue",
    "content": "<template>\n  <div class=\"drag-list-wrapper\">\n    <div class=\"drag-list-con con1\">\n      <slot name=\"left-title\"></slot>\n      <draggable class=\"drop-box1\" :class=\"dropConClass.left\" :options=\"options\" :value=\"list1\" @input=\"handleListChange($event, 'left')\" @end=\"handleEnd($event, 'left')\">\n        <div class=\"drag-list-item\" v-for=\"(itemLeft, index) in list1\" :key=\"`drag_li1_${index}`\">\n          <slot name=\"left\" :itemLeft=\"itemLeft\">{{ itemLeft }}</slot>\n        </div>\n      </draggable>\n    </div>\n    <div class=\"drag-list-con con2\">\n      <slot name=\"right-title\"></slot>\n      <draggable class=\"drop-box2\" :class=\"dropConClass.right\" :options=\"options\" :value=\"list2\" @input=\"handleListChange($event, 'right')\" @end=\"handleEnd($event, 'right')\">\n        <div class=\"drag-list-item\" v-for=\"(itemRight, index) in list2\" :key=\"`drag_li2_${index}`\">\n          <slot name=\"right\" :itemRight=\"itemRight\">{{ itemRight }}</slot>\n        </div>\n      </draggable>\n    </div>\n  </div>\n</template>\n<script>\nimport draggable from 'vuedraggable'\nexport default {\n  name: 'DragList',\n  components: {\n    draggable\n  },\n  props: {\n    list1: {\n      type: Array,\n      required: true\n    },\n    list2: {\n      type: Array,\n      default: () => []\n    },\n    dropConClass: {\n      type: Object,\n      default: () => ({})\n    }\n  },\n  data () {\n    return {\n      options: { group: 'drag_list' }\n    }\n  },\n  methods: {\n    handleListChange (value, type) {\n      if (type === 'left') this.$emit('update:list1', value)\n      else this.$emit('update:list2', value)\n    },\n    handleEnd (event, type) {\n      const srcClassName = (event.srcElement || event.target).classList[0]\n      const targetClassName = event.to.classList[0]\n      let src = ''\n      let target = ''\n      if (srcClassName === targetClassName) {\n        if (type === 'left') {\n          src = 'left'\n          target = 'left'\n        } else {\n          src = 'right'\n          target = 'right'\n        }\n      } else {\n        if (type === 'left') {\n          src = 'left'\n          target = 'right'\n        } else {\n          src = 'right'\n          target = 'left'\n        }\n      }\n      this.$emit('on-change', {\n        src: src,\n        target: target,\n        oldIndex: event.oldIndex,\n        newIndex: event.newIndex\n      })\n    }\n  }\n}\n</script>\n<style lang=\"less\">\n.drag-list-wrapper{\n  height: 100%;\n  .drag-list-con{\n    width: 50%;\n    float: left;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/components/drag-list/index.js",
    "content": "import DragList from './drag-list.vue'\nexport default DragList\n"
  },
  {
    "path": "src/components/editor/editor.vue",
    "content": "<template>\n  <div class=\"editor-wrapper\">\n    <div :id=\"editorId\"></div>\n  </div>\n</template>\n\n<script>\nimport Editor from 'wangeditor'\nimport 'wangeditor/release/wangEditor.min.css'\nimport { oneOf } from '@/libs/tools'\nexport default {\n  name: 'Editor',\n  props: {\n    value: {\n      type: String,\n      default: ''\n    },\n    /**\n     * 绑定的值的类型, enum: ['html', 'text']\n     */\n    valueType: {\n      type: String,\n      default: 'html',\n      validator: (val) => {\n        return oneOf(val, ['html', 'text'])\n      }\n    },\n    /**\n     * @description 设置change事件触发时间间隔\n     */\n    changeInterval: {\n      type: Number,\n      default: 200\n    },\n    /**\n     * @description 是否开启本地存储\n     */\n    cache: {\n      type: Boolean,\n      default: true\n    }\n  },\n  computed: {\n    editorId () {\n      return `editor${this._uid}`\n    }\n  },\n  methods: {\n    setHtml (val) {\n      this.editor.txt.html(val)\n    }\n  },\n  mounted () {\n    this.editor = new Editor(`#${this.editorId}`)\n    this.editor.customConfig.onchange = (html) => {\n      let text = this.editor.txt.text()\n      if (this.cache) localStorage.editorCache = html\n      this.$emit('input', this.valueType === 'html' ? html : text)\n      this.$emit('on-change', html, text)\n    }\n    this.editor.customConfig.onchangeTimeout = this.changeInterval\n    // create这个方法一定要在所有配置项之后调用\n    this.editor.create()\n    // 如果本地有存储加载本地存储内容\n    let html = this.value || localStorage.editorCache\n    if (html) this.editor.txt.html(html)\n  }\n}\n</script>\n\n<style lang=\"less\">\n.editor-wrapper *{\n  z-index: 100 !important;\n}\n</style>\n"
  },
  {
    "path": "src/components/editor/index.js",
    "content": "import Editor from './editor.vue'\nexport default Editor\n"
  },
  {
    "path": "src/components/icons/icons.vue",
    "content": "<template>\n  <i :class=\"`iconfont icon-${type}`\" :style=\"styles\"></i>\n</template>\n\n<script>\nexport default {\n  name: 'Icons',\n  props: {\n    type: {\n      type: String,\n      required: true\n    },\n    color: {\n      type: String,\n      default: '#5c6b77'\n    },\n    size: {\n      type: Number,\n      default: 16\n    }\n  },\n  computed: {\n    styles () {\n      return {\n        fontSize: `${this.size}px`,\n        color: this.color\n      }\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/components/icons/index.js",
    "content": "import Icons from './icons.vue'\nexport default Icons\n"
  },
  {
    "path": "src/components/info-card/index.js",
    "content": "import InforCard from './infor-card.vue'\nexport default InforCard\n"
  },
  {
    "path": "src/components/info-card/infor-card.vue",
    "content": "<template>\n  <Card :shadow=\"shadow\" class=\"info-card-wrapper\" :padding=\"0\">\n    <div class=\"content-con\">\n      <div class=\"left-area\" :style=\"{background: color, width: leftWidth}\">\n        <common-icon class=\"icon\" :type=\"icon\" :size=\"iconSize\" color=\"#fff\"/>\n      </div>\n      <div class=\"right-area\" :style=\"{width: rightWidth}\">\n        <div>\n          <slot></slot>\n        </div>\n      </div>\n    </div>\n  </Card>\n</template>\n\n<script>\nimport CommonIcon from '_c/common-icon'\nexport default {\n  name: 'InforCard',\n  components: {\n    CommonIcon\n  },\n  props: {\n    left: {\n      type: Number,\n      default: 36\n    },\n    color: {\n      type: String,\n      default: '#2d8cf0'\n    },\n    icon: {\n      type: String,\n      default: ''\n    },\n    iconSize: {\n      type: Number,\n      default: 20\n    },\n    shadow: {\n      type: Boolean,\n      default: false\n    }\n  },\n  computed: {\n    leftWidth () {\n      return `${this.left}%`\n    },\n    rightWidth () {\n      return `${100 - this.left}%`\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.common{\n  float: left;\n  height: 100%;\n  display: table;\n  text-align: center;\n}\n.size{\n  width: 100%;\n  height: 100%;\n}\n.middle-center{\n  display: table-cell;\n  vertical-align: middle;\n}\n.info-card-wrapper{\n  .size;\n  overflow: hidden;\n  .ivu-card-body{\n    .size;\n  }\n  .content-con{\n    .size;\n    position: relative;\n    .left-area{\n      .common;\n      & > .icon{\n        .middle-center;\n      }\n    }\n    .right-area{\n      .common;\n      & > div{\n        .middle-center;\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "src/components/login-form/index.js",
    "content": "import LoginForm from './login-form.vue'\nexport default LoginForm\n"
  },
  {
    "path": "src/components/login-form/login-form.vue",
    "content": "<template>\n  <Form ref=\"loginForm\" :model=\"form\" :rules=\"rules\" @keydown.enter.native=\"handleSubmit\">\n    <FormItem prop=\"userName\">\n      <Input v-model=\"form.userName\" placeholder=\"请输入用户名\">\n        <span slot=\"prepend\">\n          <Icon :size=\"16\" type=\"ios-person\"></Icon>\n        </span>\n      </Input>\n    </FormItem>\n    <FormItem prop=\"password\">\n      <Input type=\"password\" v-model=\"form.password\" placeholder=\"请输入密码\">\n        <span slot=\"prepend\">\n          <Icon :size=\"14\" type=\"md-lock\"></Icon>\n        </span>\n      </Input>\n    </FormItem>\n    <FormItem>\n      <Button @click=\"handleSubmit\" type=\"primary\" long>登录</Button>\n    </FormItem>\n  </Form>\n</template>\n<script>\nexport default {\n  name: 'LoginForm',\n  props: {\n    userNameRules: {\n      type: Array,\n      default: () => {\n        return [\n          { required: true, message: '账号不能为空', trigger: 'blur' }\n        ]\n      }\n    },\n    passwordRules: {\n      type: Array,\n      default: () => {\n        return [\n          { required: true, message: '密码不能为空', trigger: 'blur' }\n        ]\n      }\n    }\n  },\n  data () {\n    return {\n      form: {\n        userName: 'super_admin',\n        password: ''\n      }\n    }\n  },\n  computed: {\n    rules () {\n      return {\n        userName: this.userNameRules,\n        password: this.passwordRules\n      }\n    }\n  },\n  methods: {\n    handleSubmit () {\n      this.$refs.loginForm.validate((valid) => {\n        if (valid) {\n          this.$emit('on-success-valid', {\n            userName: this.form.userName,\n            password: this.form.password\n          })\n        }\n      })\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/a-back-top/index.js",
    "content": "import ABackTop from './index.vue'\nexport default ABackTop\n"
  },
  {
    "path": "src/components/main/components/a-back-top/index.vue",
    "content": "<template>\n    <div :class=\"classes\" :style=\"styles\" @click=\"back\">\n        <slot>\n            <div :class=\"innerClasses\">\n                <i class=\"ivu-icon ivu-icon-ios-arrow-up\"></i>\n            </div>\n        </slot>\n    </div>\n</template>\n<script>\nimport { scrollTop } from '@/libs/util'\nimport { on, off } from '@/libs/tools'\nconst prefixCls = 'ivu-back-top'\n\nexport default {\n  name: 'ABackTop',\n  props: {\n    height: {\n      type: Number,\n      default: 400\n    },\n    bottom: {\n      type: Number,\n      default: 30\n    },\n    right: {\n      type: Number,\n      default: 30\n    },\n    duration: {\n      type: Number,\n      default: 1000\n    },\n    container: {\n      type: null,\n      default: window\n    }\n  },\n  data () {\n    return {\n      backTop: false\n    }\n  },\n  mounted () {\n    // window.addEventListener('scroll', this.handleScroll, false)\n    // window.addEventListener('resize', this.handleScroll, false)\n    on(this.containerEle, 'scroll', this.handleScroll)\n    on(this.containerEle, 'resize', this.handleScroll)\n  },\n  beforeDestroy () {\n    // window.removeEventListener('scroll', this.handleScroll, false)\n    // window.removeEventListener('resize', this.handleScroll, false)\n    off(this.containerEle, 'scroll', this.handleScroll)\n    off(this.containerEle, 'resize', this.handleScroll)\n  },\n  computed: {\n    classes () {\n      return [\n        `${prefixCls}`,\n        {\n          [`${prefixCls}-show`]: this.backTop\n        }\n      ]\n    },\n    styles () {\n      return {\n        bottom: `${this.bottom}px`,\n        right: `${this.right}px`\n      }\n    },\n    innerClasses () {\n      return `${prefixCls}-inner`\n    },\n    containerEle () {\n      return this.container === window ? window : document.querySelector(this.container)\n    }\n  },\n  methods: {\n    handleScroll () {\n      this.backTop = this.containerEle.scrollTop >= this.height\n    },\n    back () {\n      let target = typeof this.container === 'string' ? this.containerEle : (document.documentElement || document.body)\n      const sTop = target.scrollTop\n      scrollTop(this.containerEle, sTop, 0, this.duration)\n      this.$emit('on-click')\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/error-store/error-store.vue",
    "content": "<template>\n  <div class=\"error-store\">\n    <Badge dot :count=\"countComputed\">\n      <Button type=\"text\" @click=\"openErrorLoggerPage\">\n        <Icon :size=\"20\" type=\"ios-bug\"/>\n      </Button>\n    </Badge>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'ErrorStore',\n  props: {\n    count: {\n      type: Number,\n      default: 0\n    },\n    hasRead: {\n      type: Boolean,\n      default: false\n    }\n  },\n  computed: {\n    countComputed () {\n      return this.hasRead ? 0 : this.count\n    }\n  },\n  methods: {\n    openErrorLoggerPage () {\n      this.$router.push({\n        name: 'error_logger_page'\n      })\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.error-store{\n  margin-right: 12px;\n  .ivu-badge-dot{\n    top: 20px;\n  }\n  .ivu-btn.ivu-btn-text{\n    padding: 5px 1px 6px;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/components/main/components/error-store/index.js",
    "content": "import ErrorStore from './error-store.vue'\nexport default ErrorStore\n"
  },
  {
    "path": "src/components/main/components/fullscreen/fullscreen.vue",
    "content": "<template>\n  <div v-if=\"showFullScreenBtn\" class=\"full-screen-btn-con\">\n    <Tooltip :content=\"value ? '退出全屏' : '全屏'\" placement=\"bottom\">\n      <Icon @click.native=\"handleChange\" :type=\"value ? 'md-contract' : 'md-expand'\" :size=\"23\"></Icon>\n    </Tooltip>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Fullscreen',\n  computed: {\n    showFullScreenBtn () {\n      return window.navigator.userAgent.indexOf('MSIE') < 0\n    }\n  },\n  props: {\n    value: {\n      type: Boolean,\n      default: false\n    }\n  },\n  methods: {\n    handleFullscreen () {\n      let main = document.body\n      if (this.value) {\n        if (document.exitFullscreen) {\n          document.exitFullscreen()\n        } else if (document.mozCancelFullScreen) {\n          document.mozCancelFullScreen()\n        } else if (document.webkitCancelFullScreen) {\n          document.webkitCancelFullScreen()\n        } else if (document.msExitFullscreen) {\n          document.msExitFullscreen()\n        }\n      } else {\n        if (main.requestFullscreen) {\n          main.requestFullscreen()\n        } else if (main.mozRequestFullScreen) {\n          main.mozRequestFullScreen()\n        } else if (main.webkitRequestFullScreen) {\n          main.webkitRequestFullScreen()\n        } else if (main.msRequestFullscreen) {\n          main.msRequestFullscreen()\n        }\n      }\n    },\n    handleChange () {\n      this.handleFullscreen()\n    }\n  },\n  mounted () {\n    let isFullscreen = document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.fullScreen || document.mozFullScreen || document.webkitIsFullScreen\n    isFullscreen = !!isFullscreen\n    document.addEventListener('fullscreenchange', () => {\n      this.$emit('input', !this.value)\n      this.$emit('on-change', !this.value)\n    })\n    document.addEventListener('mozfullscreenchange', () => {\n      this.$emit('input', !this.value)\n      this.$emit('on-change', !this.value)\n    })\n    document.addEventListener('webkitfullscreenchange', () => {\n      this.$emit('input', !this.value)\n      this.$emit('on-change', !this.value)\n    })\n    document.addEventListener('msfullscreenchange', () => {\n      this.$emit('input', !this.value)\n      this.$emit('on-change', !this.value)\n    })\n    this.$emit('input', isFullscreen)\n  }\n}\n</script>\n\n<style lang=\"less\">\n.full-screen-btn-con .ivu-tooltip-rel{\n  height: 64px;\n  line-height: 56px;\n  i{\n    cursor: pointer;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/components/main/components/fullscreen/index.js",
    "content": "import Fullscreen from './fullscreen.vue'\nexport default Fullscreen\n"
  },
  {
    "path": "src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.less",
    "content": ".custom-bread-crumb{\n  display: inline-block;\n  vertical-align: top;\n}\n"
  },
  {
    "path": "src/components/main/components/header-bar/custom-bread-crumb/custom-bread-crumb.vue",
    "content": "<template>\n  <div class=\"custom-bread-crumb\">\n    <Breadcrumb :style=\"{fontSize: `${fontSize}px`}\">\n      <BreadcrumbItem v-for=\"item in list\" :to=\"item.to\" :key=\"`bread-crumb-${item.name}`\">\n        <common-icon style=\"margin-right: 4px;\" :type=\"item.icon || ''\"/>\n        {{ showTitle(item) }}\n      </BreadcrumbItem>\n    </Breadcrumb>\n  </div>\n</template>\n<script>\nimport { showTitle } from '@/libs/util'\nimport CommonIcon from '_c/common-icon'\nimport './custom-bread-crumb.less'\nexport default {\n  name: 'customBreadCrumb',\n  components: {\n    CommonIcon\n  },\n  props: {\n    list: {\n      type: Array,\n      default: () => []\n    },\n    fontSize: {\n      type: Number,\n      default: 14\n    },\n    showIcon: {\n      type: Boolean,\n      default: false\n    }\n  },\n  methods: {\n    showTitle (item) {\n      return showTitle(item, this)\n    },\n    isCustomIcon (iconName) {\n      return iconName.indexOf('_') === 0\n    },\n    getCustomIconName (iconName) {\n      return iconName.slice(1)\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/header-bar/custom-bread-crumb/index.js",
    "content": "import customBreadCrumb from './custom-bread-crumb.vue'\nexport default customBreadCrumb\n"
  },
  {
    "path": "src/components/main/components/header-bar/header-bar.less",
    "content": ".header-bar{\n  width: 100%;\n  height: 100%;\n  position: relative;\n  .custom-content-con{\n    float: right;\n    height: auto;\n    padding-right: 20px;\n    line-height: 64px;\n    & > *{\n      float: right;\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/main/components/header-bar/header-bar.vue",
    "content": "<template>\n  <div class=\"header-bar\">\n    <sider-trigger :collapsed=\"collapsed\" icon=\"md-menu\" @on-change=\"handleCollpasedChange\"></sider-trigger>\n    <custom-bread-crumb show-icon style=\"margin-left: 30px;\" :list=\"breadCrumbList\"></custom-bread-crumb>\n    <div class=\"custom-content-con\">\n      <slot></slot>\n    </div>\n  </div>\n</template>\n<script>\nimport siderTrigger from './sider-trigger'\nimport customBreadCrumb from './custom-bread-crumb'\nimport './header-bar.less'\nexport default {\n  name: 'HeaderBar',\n  components: {\n    siderTrigger,\n    customBreadCrumb\n  },\n  props: {\n    collapsed: Boolean\n  },\n  computed: {\n    breadCrumbList () {\n      return this.$store.state.app.breadCrumbList\n    }\n  },\n  methods: {\n    handleCollpasedChange (state) {\n      this.$emit('on-coll-change', state)\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/header-bar/index.js",
    "content": "import HeaderBar from './header-bar'\nexport default HeaderBar\n"
  },
  {
    "path": "src/components/main/components/header-bar/sider-trigger/index.js",
    "content": "import siderTrigger from './sider-trigger.vue'\nexport default siderTrigger\n"
  },
  {
    "path": "src/components/main/components/header-bar/sider-trigger/sider-trigger.less",
    "content": ".trans{\n  transition: transform .2s ease;\n}\n@size: 40px;\n.sider-trigger-a{\n  padding: 6px;\n  width: @size;\n  height: @size;\n  display: inline-block;\n  text-align: center;\n  color: #5c6b77;\n  margin-top: 12px;\n  i{\n    .trans;\n    vertical-align: top;\n  }\n  &.collapsed i{\n    transform: rotateZ(90deg);\n    .trans;\n  }\n}\n"
  },
  {
    "path": "src/components/main/components/header-bar/sider-trigger/sider-trigger.vue",
    "content": "<template>\n  <a @click=\"handleChange\" type=\"text\" :class=\"['sider-trigger-a', collapsed ? 'collapsed' : '']\"><Icon :type=\"icon\" :size=\"size\" /></a>\n</template>\n<script>\nexport default {\n  name: 'siderTrigger',\n  props: {\n    collapsed: Boolean,\n    icon: {\n      type: String,\n      default: 'navicon-round'\n    },\n    size: {\n      type: Number,\n      default: 26\n    }\n  },\n  methods: {\n    handleChange () {\n      this.$emit('on-change', !this.collapsed)\n    }\n  }\n}\n</script>\n<style lang=\"less\">\n@import './sider-trigger.less';\n</style>\n"
  },
  {
    "path": "src/components/main/components/language/index.js",
    "content": "import Language from './language.vue'\nexport default Language\n"
  },
  {
    "path": "src/components/main/components/language/language.vue",
    "content": "<template>\n  <div>\n    <Dropdown trigger=\"click\" @on-click=\"selectLang\">\n      <a href=\"javascript:void(0)\">\n        {{ title }}\n        <Icon :size=\"18\" type=\"md-arrow-dropdown\" />\n      </a>\n      <DropdownMenu slot=\"list\">\n        <DropdownItem v-for=\"(value, key) in localList\" :name=\"key\" :key=\"`lang-${key}`\">{{ value }}</DropdownItem>\n      </DropdownMenu>\n    </Dropdown>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Language',\n  props: {\n    lang: String\n  },\n  data () {\n    return {\n      langList: {\n        'zh-CN': '语言',\n        'zh-TW': '語言',\n        'en-US': 'Lang'\n      },\n      localList: {\n        'zh-CN': '中文简体',\n        'zh-TW': '中文繁体',\n        'en-US': 'English'\n      }\n    }\n  },\n  watch: {\n    lang (lang) {\n      this.$i18n.locale = lang\n    }\n  },\n  computed: {\n    title () {\n      return this.langList[this.lang]\n    }\n  },\n  methods: {\n    selectLang (name) {\n      this.$emit('on-lang-change', name)\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/side-menu/collapsed-menu.vue",
    "content": "<template>\n  <Dropdown ref=\"dropdown\" @on-click=\"handleClick\" :class=\"hideTitle ? '' : 'collased-menu-dropdown'\" :transfer=\"hideTitle\" :placement=\"placement\">\n    <a class=\"drop-menu-a\" type=\"text\" @mouseover=\"handleMousemove($event, children)\" :style=\"{textAlign: !hideTitle ? 'left' : ''}\"><common-icon :size=\"rootIconSize\" :color=\"textColor\" :type=\"parentItem.icon\"/><span class=\"menu-title\" v-if=\"!hideTitle\">{{ showTitle(parentItem) }}</span><Icon style=\"float: right;\" v-if=\"!hideTitle\" type=\"ios-arrow-forward\" :size=\"16\"/></a>\n    <DropdownMenu ref=\"dropdown\" slot=\"list\">\n      <template v-for=\"child in children\">\n        <collapsed-menu v-if=\"showChildren(child)\" :icon-size=\"iconSize\" :parent-item=\"child\" :key=\"`drop-${child.name}`\"></collapsed-menu>\n        <DropdownItem v-else :key=\"`drop-${child.name}`\" :name=\"child.name\"><common-icon :size=\"iconSize\" :type=\"child.icon\"/><span class=\"menu-title\">{{ showTitle(child) }}</span></DropdownItem>\n      </template>\n    </DropdownMenu>\n  </Dropdown>\n</template>\n<script>\nimport mixin from './mixin'\nimport itemMixin from './item-mixin'\nimport { findNodeUpperByClasses } from '@/libs/util'\n\nexport default {\n  name: 'CollapsedMenu',\n  mixins: [ mixin, itemMixin ],\n  props: {\n    hideTitle: {\n      type: Boolean,\n      default: false\n    },\n    rootIconSize: {\n      type: Number,\n      default: 16\n    }\n  },\n  data () {\n    return {\n      placement: 'right-end'\n    }\n  },\n  methods: {\n    handleClick (name) {\n      this.$emit('on-click', name)\n    },\n    handleMousemove (event, children) {\n      const { pageY } = event\n      const height = children.length * 38\n      const isOverflow = pageY + height < window.innerHeight\n      this.placement = isOverflow ? 'right-start' : 'right-end'\n    }\n  },\n  mounted () {\n    let dropdown = findNodeUpperByClasses(this.$refs.dropdown.$el, ['ivu-select-dropdown', 'ivu-dropdown-transfer'])\n    if (dropdown) dropdown.style.overflow = 'visible'\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/side-menu/index.js",
    "content": "import SideMenu from './side-menu.vue'\nexport default SideMenu\n"
  },
  {
    "path": "src/components/main/components/side-menu/item-mixin.js",
    "content": "export default {\n  props: {\n    parentItem: {\n      type: Object,\n      default: () => {}\n    },\n    theme: String,\n    iconSize: Number\n  },\n  computed: {\n    parentName () {\n      return this.parentItem.name\n    },\n    children () {\n      return this.parentItem.children\n    },\n    textColor () {\n      return this.theme === 'dark' ? '#fff' : '#495060'\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/main/components/side-menu/mixin.js",
    "content": "import CommonIcon from '_c/common-icon'\nimport { showTitle } from '@/libs/util'\nexport default {\n  components: {\n    CommonIcon\n  },\n  methods: {\n    showTitle (item) {\n      return showTitle(item, this)\n    },\n    showChildren (item) {\n      return item.children && (item.children.length > 1 || (item.meta && item.meta.showAlways))\n    },\n    getNameOrHref (item, children0) {\n      return item.href ? `isTurnByHref_${item.href}` : (children0 ? item.children[0].name : item.name)\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/main/components/side-menu/side-menu-item.vue",
    "content": "<template>\n  <Submenu :name=\"`${parentName}`\">\n    <template slot=\"title\">\n      <common-icon :type=\"parentItem.icon || ''\"/>\n      <span>{{ showTitle(parentItem) }}</span>\n    </template>\n    <template v-for=\"item in children\">\n      <template v-if=\"item.children && item.children.length === 1\">\n        <side-menu-item v-if=\"showChildren(item)\" :key=\"`menu-${item.name}`\" :parent-item=\"item\"></side-menu-item>\n        <menu-item v-else :name=\"getNameOrHref(item, true)\" :key=\"`menu-${item.children[0].name}`\"><common-icon :type=\"item.children[0].icon || ''\"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>\n      </template>\n      <template v-else>\n        <side-menu-item v-if=\"showChildren(item)\" :key=\"`menu-${item.name}`\" :parent-item=\"item\"></side-menu-item>\n        <menu-item v-else :name=\"getNameOrHref(item)\" :key=\"`menu-${item.name}`\"><common-icon :type=\"item.icon || ''\"/><span>{{ showTitle(item) }}</span></menu-item>\n      </template>\n    </template>\n  </Submenu>\n</template>\n<script>\nimport mixin from './mixin'\nimport itemMixin from './item-mixin'\nexport default {\n  name: 'SideMenuItem',\n  mixins: [ mixin, itemMixin ]\n}\n</script>\n"
  },
  {
    "path": "src/components/main/components/side-menu/side-menu.less",
    "content": ".side-menu-wrapper{\n  user-select: none;\n  .menu-collapsed{\n    padding-top: 10px;\n\n    .ivu-dropdown{\n      width: 100%;\n      .ivu-dropdown-rel a{\n        width: 100%;\n      }\n    }\n    .ivu-tooltip{\n      width: 100%;\n      .ivu-tooltip-rel{\n        width: 100%;\n      }\n      .ivu-tooltip-popper .ivu-tooltip-content{\n        .ivu-tooltip-arrow{\n          border-right-color: #fff;\n        }\n        .ivu-tooltip-inner{\n          background: #fff;\n          color: #495060;\n        }\n      }\n    }\n\n\n  }\n  a.drop-menu-a{\n    display: inline-block;\n    padding: 6px 15px;\n    width: 100%;\n    text-align: center;\n    color: #495060;\n  }\n}\n.menu-title{\n  padding-left: 6px;\n}\n"
  },
  {
    "path": "src/components/main/components/side-menu/side-menu.vue",
    "content": "<template>\n  <div class=\"side-menu-wrapper\">\n    <slot></slot>\n    <Menu ref=\"menu\" v-show=\"!collapsed\" :active-name=\"activeName\" :open-names=\"openedNames\" :accordion=\"accordion\" :theme=\"theme\" width=\"auto\" @on-select=\"handleSelect\">\n      <template v-for=\"item in menuList\">\n        <template v-if=\"item.children && item.children.length === 1\">\n          <side-menu-item v-if=\"showChildren(item)\" :key=\"`menu-${item.name}`\" :parent-item=\"item\"></side-menu-item>\n          <menu-item v-else :name=\"getNameOrHref(item, true)\" :key=\"`menu-${item.children[0].name}`\"><common-icon :type=\"item.children[0].icon || ''\"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>\n        </template>\n        <template v-else>\n          <side-menu-item v-if=\"showChildren(item)\" :key=\"`menu-${item.name}`\" :parent-item=\"item\"></side-menu-item>\n          <menu-item v-else :name=\"getNameOrHref(item)\" :key=\"`menu-${item.name}`\"><common-icon :type=\"item.icon || ''\"/><span>{{ showTitle(item) }}</span></menu-item>\n        </template>\n      </template>\n    </Menu>\n    <div class=\"menu-collapsed\" v-show=\"collapsed\" :list=\"menuList\">\n      <template v-for=\"item in menuList\">\n        <collapsed-menu v-if=\"item.children && item.children.length > 1\" @on-click=\"handleSelect\" hide-title :root-icon-size=\"rootIconSize\" :icon-size=\"iconSize\" :theme=\"theme\" :parent-item=\"item\" :key=\"`drop-menu-${item.name}`\"></collapsed-menu>\n        <Tooltip transfer v-else :content=\"showTitle(item.children && item.children[0] ? item.children[0] : item)\" placement=\"right\" :key=\"`drop-menu-${item.name}`\">\n          <a @click=\"handleSelect(getNameOrHref(item, true))\" class=\"drop-menu-a\" :style=\"{textAlign: 'center'}\"><common-icon :size=\"rootIconSize\" :color=\"textColor\" :type=\"item.icon || (item.children && item.children[0].icon)\"/></a>\n        </Tooltip>\n      </template>\n    </div>\n  </div>\n</template>\n<script>\nimport SideMenuItem from './side-menu-item.vue'\nimport CollapsedMenu from './collapsed-menu.vue'\nimport { getUnion } from '@/libs/tools'\nimport mixin from './mixin'\n\nexport default {\n  name: 'SideMenu',\n  mixins: [ mixin ],\n  components: {\n    SideMenuItem,\n    CollapsedMenu\n  },\n  props: {\n    menuList: {\n      type: Array,\n      default () {\n        return []\n      }\n    },\n    collapsed: {\n      type: Boolean\n    },\n    theme: {\n      type: String,\n      default: 'dark'\n    },\n    rootIconSize: {\n      type: Number,\n      default: 20\n    },\n    iconSize: {\n      type: Number,\n      default: 16\n    },\n    accordion: Boolean,\n    activeName: {\n      type: String,\n      default: ''\n    },\n    openNames: {\n      type: Array,\n      default: () => []\n    }\n  },\n  data () {\n    return {\n      openedNames: []\n    }\n  },\n  methods: {\n    handleSelect (name) {\n      this.$emit('on-select', name)\n    },\n    getOpenedNamesByActiveName (name) {\n      return this.$route.matched.map(item => item.name).filter(item => item !== name)\n    },\n    updateOpenName (name) {\n      if (name === this.$config.homeName) this.openedNames = []\n      else this.openedNames = this.getOpenedNamesByActiveName(name)\n    }\n  },\n  computed: {\n    textColor () {\n      return this.theme === 'dark' ? '#fff' : '#495060'\n    }\n  },\n  watch: {\n    activeName (name) {\n      if (this.accordion) this.openedNames = this.getOpenedNamesByActiveName(name)\n      else this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))\n    },\n    openNames (newNames) {\n      this.openedNames = newNames\n    },\n    openedNames () {\n      this.$nextTick(() => {\n        this.$refs.menu.updateOpened()\n      })\n    }\n  },\n  mounted () {\n    this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))\n  }\n}\n</script>\n<style lang=\"less\">\n@import './side-menu.less';\n</style>\n"
  },
  {
    "path": "src/components/main/components/tags-nav/index.js",
    "content": "import TagsNav from './tags-nav.vue'\nexport default TagsNav\n"
  },
  {
    "path": "src/components/main/components/tags-nav/tags-nav.less",
    "content": ".no-select{\n  -webkit-touch-callout: none;\n  -webkit-user-select: none;\n  -khtml-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n}\n.size{\n  width: 100%;\n  height: 100%;\n}\n.tags-nav{\n  position: relative;\n  border-top: 1px solid #F0F0F0;\n  border-bottom: 1px solid #F0F0F0;\n  .no-select;\n  .size;\n  .close-con{\n    position: absolute;\n    right: 0;\n    top: 0;\n    height: 100%;\n    width: 32px;\n    background: #fff;\n    text-align: center;\n    z-index: 10;\n  }\n  .btn-con{\n    position: absolute;\n    top: 0px;\n    height: 100%;\n    background: #fff;\n    padding-top: 3px;\n    z-index: 10;\n    button{\n      padding: 6px 4px;\n      line-height: 14px;\n      text-align: center;\n    }\n    &.left-btn{\n      left: 0px;\n    }\n    &.right-btn{\n      right: 32px;\n      border-right: 1px solid #F0F0F0;\n    }\n  }\n  .scroll-outer{\n    position: absolute;\n    left: 28px;\n    right: 61px;\n    top: 0;\n    bottom: 0;\n    box-shadow: 0px 0 3px 2px rgba(100,100,100,.1) inset;\n    .scroll-body{\n      height: ~\"calc(100% - 1px)\";\n      display: inline-block;\n      padding: 1px 4px 0;\n      position: absolute;\n      overflow: visible;\n      white-space: nowrap;\n      transition: left .3s ease;\n      .ivu-tag-dot-inner{\n        transition: background .2s ease;\n      }\n    }\n  }\n  .contextmenu {\n    position: absolute;\n    margin: 0;\n    padding: 5px 0;\n    background: #fff;\n    z-index: 1000;\n    list-style-type: none;\n    border-radius: 4px;\n    box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .1);\n    li {\n      margin: 0;\n      padding: 5px 15px;\n      cursor: pointer;\n      &:hover {\n        background: #eee;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/main/components/tags-nav/tags-nav.vue",
    "content": "<template>\n  <div class=\"tags-nav\">\n    <div class=\"close-con\">\n      <Dropdown transfer @on-click=\"handleTagsOption\" style=\"margin-top:7px;\">\n        <Button size=\"small\" type=\"text\">\n          <Icon :size=\"18\" type=\"ios-close-circle-outline\" />\n        </Button>\n        <DropdownMenu slot=\"list\">\n          <DropdownItem name=\"close-all\">关闭所有</DropdownItem>\n          <DropdownItem name=\"close-others\">关闭其他</DropdownItem>\n        </DropdownMenu>\n      </Dropdown>\n    </div>\n    <ul v-show=\"visible\" :style=\"{left: contextMenuLeft + 'px', top: contextMenuTop + 'px'}\" class=\"contextmenu\">\n      <li v-for=\"(item, key) of menuList\" @click=\"handleTagsOption(key)\" :key=\"key\">{{item}}</li>\n    </ul>\n    <div class=\"btn-con left-btn\">\n      <Button type=\"text\" @click=\"handleScroll(240)\">\n        <Icon :size=\"18\" type=\"ios-arrow-back\" />\n      </Button>\n    </div>\n    <div class=\"btn-con right-btn\">\n      <Button type=\"text\" @click=\"handleScroll(-240)\">\n        <Icon :size=\"18\" type=\"ios-arrow-forward\" />\n      </Button>\n    </div>\n    <div class=\"scroll-outer\" ref=\"scrollOuter\" @DOMMouseScroll=\"handlescroll\" @mousewheel=\"handlescroll\">\n      <div ref=\"scrollBody\" class=\"scroll-body\" :style=\"{left: tagBodyLeft + 'px'}\">\n        <transition-group name=\"taglist-moving-animation\">\n          <Tag\n            type=\"dot\"\n            v-for=\"(item, index) in list\"\n            ref=\"tagsPageOpened\"\n            :key=\"`tag-nav-${index}`\"\n            :name=\"item.name\"\n            :data-route-item=\"item\"\n            @on-close=\"handleClose(item)\"\n            @click.native=\"handleClick(item)\"\n            :closable=\"item.name !== $config.homeName\"\n            :color=\"isCurrentTag(item) ? 'primary' : 'default'\"\n            @contextmenu.prevent.native=\"contextMenu(item, $event)\"\n          >{{ showTitleInside(item) }}</Tag>\n        </transition-group>\n      </div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { showTitle, routeEqual } from '@/libs/util'\nimport beforeClose from '@/router/before-close'\nexport default {\n  name: 'TagsNav',\n  props: {\n    value: Object,\n    list: {\n      type: Array,\n      default () {\n        return []\n      }\n    }\n  },\n  data () {\n    return {\n      tagBodyLeft: 0,\n      rightOffset: 40,\n      outerPadding: 4,\n      contextMenuLeft: 0,\n      contextMenuTop: 0,\n      visible: false,\n      menuList: {\n        others: '关闭其他',\n        all: '关闭所有'\n      }\n    }\n  },\n  computed: {\n    currentRouteObj () {\n      const { name, params, query } = this.value\n      return { name, params, query }\n    }\n  },\n  methods: {\n    handlescroll (e) {\n      var type = e.type\n      let delta = 0\n      if (type === 'DOMMouseScroll' || type === 'mousewheel') {\n        delta = (e.wheelDelta) ? e.wheelDelta : -(e.detail || 0) * 40\n      }\n      this.handleScroll(delta)\n    },\n    handleScroll (offset) {\n      const outerWidth = this.$refs.scrollOuter.offsetWidth\n      const bodyWidth = this.$refs.scrollBody.offsetWidth\n      if (offset > 0) {\n        this.tagBodyLeft = Math.min(0, this.tagBodyLeft + offset)\n      } else {\n        if (outerWidth < bodyWidth) {\n          if (this.tagBodyLeft < -(bodyWidth - outerWidth)) {\n            this.tagBodyLeft = this.tagBodyLeft\n          } else {\n            this.tagBodyLeft = Math.max(this.tagBodyLeft + offset, outerWidth - bodyWidth)\n          }\n        } else {\n          this.tagBodyLeft = 0\n        }\n      }\n    },\n    handleTagsOption (type) {\n      if (type.includes('all')) {\n        // 关闭所有，除了home\n        let res = this.list.filter(item => item.name === this.$config.homeName)\n        this.$emit('on-close', res, 'all')\n      } else if (type.includes('others')) {\n        // 关闭除当前页和home页的其他页\n        let res = this.list.filter(item => routeEqual(this.currentRouteObj, item) || item.name === this.$config.homeName)\n        this.$emit('on-close', res, 'others', this.currentRouteObj)\n        setTimeout(() => {\n          this.getTagElementByRoute(this.currentRouteObj)\n        }, 100)\n      }\n    },\n    handleClose (current) {\n      if (current.meta && current.meta.beforeCloseName && current.meta.beforeCloseName in beforeClose) {\n        new Promise(beforeClose[current.meta.beforeCloseName]).then(close => {\n          if (close) {\n            this.close(current)\n          }\n        })\n      } else {\n        this.close(current)\n      }\n    },\n    close (route) {\n      let res = this.list.filter(item => !routeEqual(route, item))\n      this.$emit('on-close', res, undefined, route)\n    },\n    handleClick (item) {\n      this.$emit('input', item)\n    },\n    showTitleInside (item) {\n      return showTitle(item, this)\n    },\n    isCurrentTag (item) {\n      return routeEqual(this.currentRouteObj, item)\n    },\n    moveToView (tag) {\n      const outerWidth = this.$refs.scrollOuter.offsetWidth\n      const bodyWidth = this.$refs.scrollBody.offsetWidth\n      if (bodyWidth < outerWidth) {\n        this.tagBodyLeft = 0\n      } else if (tag.offsetLeft < -this.tagBodyLeft) {\n        // 标签在可视区域左侧\n        this.tagBodyLeft = -tag.offsetLeft + this.outerPadding\n      } else if (tag.offsetLeft > -this.tagBodyLeft && tag.offsetLeft + tag.offsetWidth < -this.tagBodyLeft + outerWidth) {\n        // 标签在可视区域\n        this.tagBodyLeft = Math.min(0, outerWidth - tag.offsetWidth - tag.offsetLeft - this.outerPadding)\n      } else {\n        // 标签在可视区域右侧\n        this.tagBodyLeft = -(tag.offsetLeft - (outerWidth - this.outerPadding - tag.offsetWidth))\n      }\n    },\n    getTagElementByRoute (route) {\n      this.$nextTick(() => {\n        this.refsTag = this.$refs.tagsPageOpened\n        this.refsTag.forEach((item, index) => {\n          if (routeEqual(route, item.$attrs['data-route-item'])) {\n            let tag = this.refsTag[index].$el\n            this.moveToView(tag)\n          }\n        })\n      })\n    },\n    contextMenu (item, e) {\n      if (item.name === this.$config.homeName) {\n        return\n      }\n      this.visible = true\n      const offsetLeft = this.$el.getBoundingClientRect().left\n      this.contextMenuLeft = e.clientX - offsetLeft + 10\n      this.contextMenuTop = e.clientY - 64\n    },\n    closeMenu () {\n      this.visible = false\n    }\n  },\n  watch: {\n    '$route' (to) {\n      this.getTagElementByRoute(to)\n    },\n    visible (value) {\n      if (value) {\n        document.body.addEventListener('click', this.closeMenu)\n      } else {\n        document.body.removeEventListener('click', this.closeMenu)\n      }\n    }\n  },\n  mounted () {\n    setTimeout(() => {\n      this.getTagElementByRoute(this.$route)\n    }, 200)\n  }\n}\n</script>\n\n<style lang=\"less\">\n@import './tags-nav.less';\n</style>\n"
  },
  {
    "path": "src/components/main/components/user/index.js",
    "content": "import User from './user.vue'\nexport default User\n"
  },
  {
    "path": "src/components/main/components/user/user.less",
    "content": ".user{\n  &-avatar-dropdown{\n    cursor: pointer;\n    display: inline-block;\n    // height: 64px;\n    vertical-align: middle;\n    // line-height: 64px;\n    .ivu-badge-dot{\n      top: 16px;\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/main/components/user/user.vue",
    "content": "<template>\n  <div class=\"user-avatar-dropdown\">\n    <Dropdown @on-click=\"handleClick\">\n      <Badge :dot=\"!!messageUnreadCount\">\n        <Avatar :src=\"userAvatar\"/>\n      </Badge>\n      <Icon :size=\"18\" type=\"md-arrow-dropdown\"></Icon>\n      <DropdownMenu slot=\"list\">\n        <DropdownItem name=\"message\">\n          消息中心<Badge style=\"margin-left: 10px\" :count=\"messageUnreadCount\"></Badge>\n        </DropdownItem>\n        <DropdownItem name=\"logout\">退出登录</DropdownItem>\n      </DropdownMenu>\n    </Dropdown>\n  </div>\n</template>\n\n<script>\nimport './user.less'\nimport { mapActions } from 'vuex'\nexport default {\n  name: 'User',\n  props: {\n    userAvatar: {\n      type: String,\n      default: ''\n    },\n    messageUnreadCount: {\n      type: Number,\n      default: 0\n    }\n  },\n  methods: {\n    ...mapActions([\n      'handleLogOut'\n    ]),\n    logout () {\n      this.handleLogOut().then(() => {\n        this.$router.push({\n          name: 'login'\n        })\n      })\n    },\n    message () {\n      this.$router.push({\n        name: 'message_page'\n      })\n    },\n    handleClick (name) {\n      switch (name) {\n        case 'logout': this.logout()\n          break\n        case 'message': this.message()\n          break\n      }\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/main/index.js",
    "content": "import Main from './main.vue'\nexport default Main\n"
  },
  {
    "path": "src/components/main/main.less",
    "content": ".main{\n  .logo-con{\n    height: 64px;\n    padding: 10px;\n    img{\n      height: 44px;\n      width: auto;\n      display: block;\n      margin: 0 auto;\n    }\n  }\n  .header-con{\n    background: #fff;\n    padding: 0 20px;\n    width: 100%;\n  }\n  .main-layout-con{\n    height: 100%;\n    overflow: hidden;\n  }\n  .main-content-con{\n    height: ~\"calc(100% - 60px)\";\n    overflow: hidden;\n  }\n  .tag-nav-wrapper{\n    padding: 0;\n    height:40px;\n    background:#F0F0F0;\n  }\n  .content-wrapper{\n    padding: 18px;\n    height: ~\"calc(100% - 80px)\";\n    overflow: auto;\n  }\n  .left-sider{\n    .ivu-layout-sider-children{\n      overflow-y: scroll;\n      margin-right: -18px;\n    }\n  }\n}\n.ivu-menu-item > i{\n  margin-right: 12px !important;\n}\n.ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {\n  margin-right: 8px !important;\n}\n.collased-menu-dropdown{\n  width: 100%;\n  margin: 0;\n  line-height: normal;\n  padding: 7px 0 6px 16px;\n  clear: both;\n  font-size: 12px !important;\n  white-space: nowrap;\n  list-style: none;\n  cursor: pointer;\n  transition: background 0.2s ease-in-out;\n  &:hover{\n    background: rgba(100, 100, 100, 0.1);\n  }\n  & * {\n    color: #515a6e;\n  }\n  .ivu-menu-item > i{\n    margin-right: 12px !important;\n  }\n  .ivu-menu-submenu > .ivu-menu > .ivu-menu-item > i {\n    margin-right: 8px !important;\n  }\n}\n\n.ivu-select-dropdown.ivu-dropdown-transfer{\n  max-height: 400px;\n}\n"
  },
  {
    "path": "src/components/main/main.vue",
    "content": "<template>\n  <Layout style=\"height: 100%\" class=\"main\">\n    <Sider hide-trigger collapsible :width=\"256\" :collapsed-width=\"64\" v-model=\"collapsed\" class=\"left-sider\" :style=\"{overflow: 'hidden'}\">\n      <side-menu accordion ref=\"sideMenu\" :active-name=\"$route.name\" :collapsed=\"collapsed\" @on-select=\"turnToPage\" :menu-list=\"menuList\">\n        <!-- 需要放在菜单上面的内容，如Logo，写在side-menu标签内部，如下 -->\n        <div class=\"logo-con\">\n          <img v-show=\"!collapsed\" :src=\"maxLogo\" key=\"max-logo\" />\n          <img v-show=\"collapsed\" :src=\"minLogo\" key=\"min-logo\" />\n        </div>\n      </side-menu>\n    </Sider>\n    <Layout>\n      <Header class=\"header-con\">\n        <header-bar :collapsed=\"collapsed\" @on-coll-change=\"handleCollapsedChange\">\n          <user :message-unread-count=\"unreadCount\" :user-avatar=\"userAvatar\"/>\n          <language v-if=\"$config.useI18n\" @on-lang-change=\"setLocal\" style=\"margin-right: 10px;\" :lang=\"local\"/>\n          <error-store v-if=\"$config.plugin['error-store'] && $config.plugin['error-store'].showInHeader\" :has-read=\"hasReadErrorPage\" :count=\"errorCount\"></error-store>\n          <fullscreen v-model=\"isFullscreen\" style=\"margin-right: 10px;\"/>\n        </header-bar>\n      </Header>\n      <Content class=\"main-content-con\">\n        <Layout class=\"main-layout-con\">\n          <div class=\"tag-nav-wrapper\">\n            <tags-nav :value=\"$route\" @input=\"handleClick\" :list=\"tagNavList\" @on-close=\"handleCloseTag\"/>\n          </div>\n          <Content class=\"content-wrapper\">\n            <keep-alive :include=\"cacheList\">\n              <router-view/>\n            </keep-alive>\n            <ABackTop :height=\"100\" :bottom=\"80\" :right=\"50\" container=\".content-wrapper\"></ABackTop>\n          </Content>\n        </Layout>\n      </Content>\n    </Layout>\n  </Layout>\n</template>\n<script>\nimport SideMenu from './components/side-menu'\nimport HeaderBar from './components/header-bar'\nimport TagsNav from './components/tags-nav'\nimport User from './components/user'\nimport ABackTop from './components/a-back-top'\nimport Fullscreen from './components/fullscreen'\nimport Language from './components/language'\nimport ErrorStore from './components/error-store'\nimport { mapMutations, mapActions, mapGetters } from 'vuex'\nimport { getNewTagList, routeEqual } from '@/libs/util'\nimport routers from '@/router/routers'\nimport minLogo from '@/assets/images/logo-min.jpg'\nimport maxLogo from '@/assets/images/logo.jpg'\nimport './main.less'\nexport default {\n  name: 'Main',\n  components: {\n    SideMenu,\n    HeaderBar,\n    Language,\n    TagsNav,\n    Fullscreen,\n    ErrorStore,\n    User,\n    ABackTop\n  },\n  data () {\n    return {\n      collapsed: false,\n      minLogo,\n      maxLogo,\n      isFullscreen: false\n    }\n  },\n  computed: {\n    ...mapGetters([\n      'errorCount'\n    ]),\n    tagNavList () {\n      return this.$store.state.app.tagNavList\n    },\n    tagRouter () {\n      return this.$store.state.app.tagRouter\n    },\n    userAvatar () {\n      return this.$store.state.user.avatarImgPath\n    },\n    cacheList () {\n      const list = ['ParentView', ...this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []]\n      return list\n    },\n    menuList () {\n      return this.$store.getters.menuList\n    },\n    local () {\n      return this.$store.state.app.local\n    },\n    hasReadErrorPage () {\n      return this.$store.state.app.hasReadErrorPage\n    },\n    unreadCount () {\n      return this.$store.state.user.unreadCount\n    }\n  },\n  methods: {\n    ...mapMutations([\n      'setBreadCrumb',\n      'setTagNavList',\n      'addTag',\n      'setLocal',\n      'setHomeRoute',\n      'closeTag'\n    ]),\n    ...mapActions([\n      'handleLogin',\n      'getUnreadMessageCount'\n    ]),\n    turnToPage (route) {\n      let { name, params, query } = {}\n      if (typeof route === 'string') name = route\n      else {\n        name = route.name\n        params = route.params\n        query = route.query\n      }\n      if (name.indexOf('isTurnByHref_') > -1) {\n        window.open(name.split('_')[1])\n        return\n      }\n      this.$router.push({\n        name,\n        params,\n        query\n      })\n    },\n    handleCollapsedChange (state) {\n      this.collapsed = state\n    },\n    handleCloseTag (res, type, route) {\n      if (type !== 'others') {\n        if (type === 'all') {\n          this.turnToPage(this.$config.homeName)\n        } else {\n          if (routeEqual(this.$route, route)) {\n            this.closeTag(route)\n          }\n        }\n      }\n      this.setTagNavList(res)\n    },\n    handleClick (item) {\n      this.turnToPage(item)\n    }\n  },\n  watch: {\n    '$route' (newRoute) {\n      const { name, query, params, meta } = newRoute\n      this.addTag({\n        route: { name, query, params, meta },\n        type: 'push'\n      })\n      this.setBreadCrumb(newRoute)\n      this.setTagNavList(getNewTagList(this.tagNavList, newRoute))\n      this.$refs.sideMenu.updateOpenName(newRoute.name)\n    }\n  },\n  mounted () {\n    /**\n     * @description 初始化设置面包屑导航和标签导航\n     */\n    this.setTagNavList()\n    this.setHomeRoute(routers)\n    const { name, params, query, meta } = this.$route\n    this.addTag({\n      route: { name, params, query, meta }\n    })\n    this.setBreadCrumb(this.$route)\n    // 设置初始语言\n    this.setLocal(this.$i18n.locale)\n    // 如果当前打开页面不在标签栏中，跳到homeName页\n    if (!this.tagNavList.find(item => item.name === this.$route.name)) {\n      this.$router.push({\n        name: this.$config.homeName\n      })\n    }\n    // 获取未读消息条数\n    this.getUnreadMessageCount()\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/markdown/index.js",
    "content": "import MarkdownEditor from './markdown.vue'\nexport default MarkdownEditor\n"
  },
  {
    "path": "src/components/markdown/markdown.vue",
    "content": "<template>\n  <div class=\"markdown-wrapper\">\n    <textarea ref=\"editor\"></textarea>\n  </div>\n</template>\n\n<script>\nimport Simplemde from 'simplemde'\nimport 'simplemde/dist/simplemde.min.css'\nexport default {\n  name: 'MarkdownEditor',\n  props: {\n    value: {\n      type: String,\n      default: ''\n    },\n    options: {\n      type: Object,\n      default: () => {\n        return {}\n      }\n    },\n    localCache: {\n      type: Boolean,\n      default: true\n    }\n  },\n  data () {\n    return {\n      editor: null\n    }\n  },\n  methods: {\n    addEvents () {\n      this.editor.codemirror.on('change', () => {\n        let value = this.editor.value()\n        if (this.localCache) localStorage.markdownContent = value\n        this.$emit('input', value)\n        this.$emit('on-change', value)\n      })\n      this.editor.codemirror.on('focus', () => {\n        this.$emit('on-focus', this.editor.value())\n      })\n      this.editor.codemirror.on('blur', () => {\n        this.$emit('on-blur', this.editor.value())\n      })\n    }\n  },\n  mounted () {\n    this.editor = new Simplemde(Object.assign(this.options, {\n      element: this.$refs.editor\n    }))\n    /**\n     * 事件列表为Codemirror编辑器的事件，更多事件类型，请参考：\n     * https://codemirror.net/doc/manual.html#events\n     */\n    this.addEvents()\n    let content = localStorage.markdownContent\n    if (content) this.editor.value(content)\n  }\n}\n</script>\n\n<style lang=\"less\">\n.markdown-wrapper{\n  .editor-toolbar.fullscreen{\n    z-index: 9999;\n  }\n  .CodeMirror-fullscreen{\n    z-index: 9999;\n  }\n  .CodeMirror-fullscreen ~ .editor-preview-side{\n    z-index: 9999;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/components/parent-view/index.js",
    "content": "import ParentView from './parent-view.vue'\nexport default ParentView\n"
  },
  {
    "path": "src/components/parent-view/parent-view.vue",
    "content": "<template>\n  <keep-alive :include=\"cacheList\" :exclude=\"notCacheName\">\n    <router-view ref=\"child\"/>\n  </keep-alive>\n</template>\n<script>\nexport default {\n  name: 'ParentView',\n  computed: {\n    tagNavList () {\n      return this.$store.state.app.tagNavList\n    },\n    notCacheName () {\n      return [(this.$route.meta && this.$route.meta.notCache) ? this.$route.name : '']\n    },\n    cacheList () {\n      return ['ParentView', ...this.tagNavList.length ? this.tagNavList.filter(item => !(item.meta && item.meta.notCache)).map(item => item.name) : []]\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/paste-editor/index.js",
    "content": "import PasteEditor from './paste-editor.vue'\nexport default PasteEditor\n"
  },
  {
    "path": "src/components/paste-editor/paste-editor.less",
    "content": ".paste-editor-wrapper{\n\twidth: 100%;\n\theight: 100%;\n\tborder: 1px dashed gainsboro;\n\ttextarea.textarea-el{\n\t\twidth: 100%;\n\t\theight: 100%;\n\t}\n\t.CodeMirror{\n\t\theight: 100%;\n    padding: 0;\n\t\t.CodeMirror-code div .CodeMirror-line > span > span.cm-tab{\n\t\t\t&::after{\n\t\t\t\tcontent: '→';\n\t\t\t\tcolor: #BFBFBF;\n\t\t\t}\n\t\t}\n\t}\n\t.first-row{\n\t\tfont-weight: 700;\n\t\tfont-size: 14px;\n\t}\n\t.incorrect-row{\n\t\tbackground: #F5CBD1;\n\t}\n}\n"
  },
  {
    "path": "src/components/paste-editor/paste-editor.vue",
    "content": "<template>\n  <div class=\"paste-editor-wrapper\">\n    <textarea ref=\"codemirror\" class=\"textarea-el\"></textarea>\n  </div>\n</template>\n<script>\nimport CodeMirror from 'codemirror'\nimport 'codemirror/lib/codemirror.css'\nimport { forEach } from '@/libs/tools'\nimport createPlaceholder from './plugins/placeholder'\nexport default {\n  name: 'PasteEditor',\n  props: {\n    value: Array,\n    pasteData: {\n      type: String,\n      default: ''\n    },\n    placeholder: {\n      type: String,\n      default: '从网页或其他应用软件复制表格数据，粘贴到这里 。默认第一行是表头，使用回车键添加新行，使用Tab键区分列。'\n    }\n  },\n  data () {\n    return {\n      pasteDataArr: [],\n      rowArrLength: 0,\n      editor: null\n    }\n  },\n  watch: {\n    pasteData (val) {\n      if (val === '') {\n        this.editor.setValue('')\n      }\n    }\n  },\n  computed: {\n    rowNum () {\n      return this.pasteDataArr.length\n    },\n    colNum () {\n      return this.pasteDataArr[0] ? this.pasteDataArr[0].length : 0\n    }\n  },\n  methods: {\n    handleKeyup (e) {\n      this.handleAreaData()\n    },\n    /**\n     * @description 处理粘贴操作\n     */\n    handleContentChanged (content) {\n      let pasteData = content.trim()\n      this.$emit('on-content-change', pasteData)\n      let rows = pasteData.split((/[\\n\\u0085\\u2028\\u2029]|\\r\\n?/g)).map(row => {\n        return row.split('\\t')\n      })\n      if (content === '') rows = []\n      this.pasteDataArr = rows\n      this.clearLineClass()\n      this.checkColNumInEveryRow()\n      this.$emit('input', this.pasteDataArr)\n    },\n    /**\n     * @description 检查除第一行的每一行列数是否与第一行相同\n     */\n    checkColNumInEveryRow () {\n      let i = 0\n      const len = this.rowNum\n      if (len === 0) return\n      while (++i < len) {\n        let item = this.pasteDataArr[i]\n        if (item.length !== this.colNum && (!(i === len - 1 && item.length === 1 && item[0] === '') || i !== len - 1)) {\n          this.markIncorrectRow(i)\n          this.$emit('on-error', i)\n          return false\n        }\n      }\n      this.$emit('on-success', this.pasteDataArr)\n      return true\n    },\n    /**\n     * @description 标记不符合格式的一行\n     */\n    markIncorrectRow (index) {\n      this.editor.addLineClass(index, 'text', 'incorrect-row')\n    },\n    /**\n     * @description 标记不符合格式的一行\n     */\n    clearLineClass () {\n      forEach(this.pasteDataArr, (item, index) => {\n        this.editor.removeLineClass(index, 'text', 'incorrect-row')\n      })\n    }\n  },\n  mounted () {\n    createPlaceholder(CodeMirror)\n    this.editor = CodeMirror.fromTextArea(this.$refs.codemirror, {\n      lineNumbers: true,\n      tabSize: 1,\n      lineWrapping: true,\n      placeholder: this.placeholder\n    })\n    this.editor.on('change', (editor) => {\n      this.handleContentChanged(editor.getValue())\n    })\n    this.editor.addLineClass(0, 'text', 'first-row')\n  }\n}\n</script>\n<style lang=\"less\">\n@import './paste-editor.less';\n</style>\n"
  },
  {
    "path": "src/components/paste-editor/plugins/placeholder.js",
    "content": "export default (codemirror) => {\n  (function (mod) {\n    mod(codemirror)\n  })(function (CodeMirror) {\n    CodeMirror.defineOption('placeholder', '', function (cm, val, old) {\n      var prev = old && old !== CodeMirror.Init\n      if (val && !prev) {\n        cm.on('blur', onBlur)\n        cm.on('change', onChange)\n        cm.on('swapDoc', onChange)\n        onChange(cm)\n      } else if (!val && prev) {\n        cm.off('blur', onBlur)\n        cm.off('change', onChange)\n        cm.off('swapDoc', onChange)\n        clearPlaceholder(cm)\n        var wrapper = cm.getWrapperElement()\n        wrapper.className = wrapper.className.replace(' CodeMirror-empty', '')\n      }\n\n      if (val && !cm.hasFocus()) onBlur(cm)\n    })\n\n    function clearPlaceholder (cm) {\n      if (cm.state.placeholder) {\n        cm.state.placeholder.parentNode.removeChild(cm.state.placeholder)\n        cm.state.placeholder = null\n      }\n    }\n    function setPlaceholder (cm) {\n      clearPlaceholder(cm)\n      var elt = cm.state.placeholder = document.createElement('pre')\n      elt.style.cssText = 'height: 0; overflow: visible; color: #80848f;'\n      elt.style.direction = cm.getOption('direction')\n      elt.className = 'CodeMirror-placeholder'\n      var placeHolder = cm.getOption('placeholder')\n      if (typeof placeHolder === 'string') placeHolder = document.createTextNode(placeHolder)\n      elt.appendChild(placeHolder)\n      cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild)\n    }\n\n    function onBlur (cm) {\n      if (isEmpty(cm)) setPlaceholder(cm)\n    }\n    function onChange (cm) {\n      let wrapper = cm.getWrapperElement()\n      let empty = isEmpty(cm)\n      wrapper.className = wrapper.className.replace(' CodeMirror-empty', '') + (empty ? ' CodeMirror-empty' : '')\n\n      if (empty) setPlaceholder(cm)\n      else clearPlaceholder(cm)\n    }\n\n    function isEmpty (cm) {\n      return (cm.lineCount() === 1) && (cm.getLine(0) === '')\n    }\n  })\n}\n"
  },
  {
    "path": "src/components/split-pane/index.js",
    "content": "import Split from './split.vue'\nexport default Split\n"
  },
  {
    "path": "src/components/split-pane/index.less",
    "content": "@split-prefix-cls: ~\"ivu-split\";\n@box-shadow: 0 0 4px 0 rgba(28, 36, 56, 0.4);\n@trigger-bar-background: rgba(23, 35, 61, 0.25);\n@trigger-background: #F8F8F9;\n@trigger-width: 6px;\n@trigger-bar-width: 4px;\n@trigger-bar-offset: (@trigger-width - @trigger-bar-width) / 2;\n@trigger-bar-interval: 3px;\n@trigger-bar-weight: 1px;\n@trigger-bar-con-height: (@trigger-bar-weight + @trigger-bar-interval) * 8;\n\n.@{split-prefix-cls}{\n  &-wrapper{\n    position: relative;\n    width: 100%;\n    height: 100%;\n  }\n  &-pane{\n    position: absolute;\n    &.left-pane, &.right-pane{\n      top: 0px;\n      bottom: 0px;\n    }\n    &.left-pane{\n      left: 0px;\n    }\n    &.right-pane{\n      right: 0px;\n    }\n    &.top-pane, &.bottom-pane{\n      left: 0px;\n      right: 0px;\n    }\n    &.top-pane{\n      top: 0px;\n    }\n    &.bottom-pane{\n      bottom: 0px;\n    }\n  }\n  &-trigger{\n    &-con{\n      position: absolute;\n      transform: translate(-50%, -50%);\n      z-index: 10;\n    }\n    &-bar-con{\n      position: absolute;\n      overflow: hidden;\n      &.vertical{\n        left: @trigger-bar-offset;\n        top: 50%;\n        height: @trigger-bar-con-height;\n        transform: translate(0, -50%);\n      }\n      &.horizontal{\n        left: 50%;\n        top: @trigger-bar-offset;\n        width: @trigger-bar-con-height;\n        transform: translate(-50%, 0);\n      }\n    }\n    &-vertical{\n      width: @trigger-width;\n      height: 100%;\n      background: @trigger-background;\n      box-shadow: @box-shadow;\n      cursor: col-resize;\n      .@{split-prefix-cls}-trigger-bar{\n        width: @trigger-bar-width;\n        height: 1px;\n        background: @trigger-bar-background;\n        float: left;\n        margin-top: @trigger-bar-interval;\n      }\n    }\n    &-horizontal{\n      height: @trigger-width;\n      width: 100%;\n      background: @trigger-background;\n      box-shadow: @box-shadow;\n      cursor: row-resize;\n      .@{split-prefix-cls}-trigger-bar{\n        height: @trigger-bar-width;\n        width: 1px;\n        background: @trigger-bar-background;\n        float: left;\n        margin-right: @trigger-bar-interval;\n      }\n    }\n  }\n  &-horizontal{\n    .@{split-prefix-cls}-trigger-con{\n      top: 50%;\n      height: 100%;\n      width: 0;\n    }\n  }\n  &-vertical{\n    .@{split-prefix-cls}-trigger-con{\n      left: 50%;\n      height: 0;\n      width: 100%;\n    }\n  }\n  .no-select{\n    -webkit-touch-callout: none;\n    -webkit-user-select: none;\n    -khtml-user-select: none;\n    -moz-user-select: none;\n    -ms-user-select: none;\n    user-select: none;\n  }\n}\n"
  },
  {
    "path": "src/components/split-pane/split.vue",
    "content": "<template>\n  <div ref=\"outerWrapper\" :class=\"wrapperClasses\">\n    <div v-if=\"isHorizontal\" :class=\"`${prefix}-horizontal`\">\n      <div :style=\"{right: `${anotherOffset}%`}\" :class=\"[`${prefix}-pane`, 'left-pane']\"><slot name=\"left\"/></div>\n      <div :class=\"`${prefix}-trigger-con`\" :style=\"{left: `${offset}%`}\" @mousedown=\"handleMousedown\">\n        <slot name=\"trigger\">\n          <trigger mode=\"vertical\"/>\n        </slot>\n      </div>\n      <div :style=\"{left: `${offset}%`}\" :class=\"[`${prefix}-pane`, 'right-pane']\"><slot name=\"right\"/></div>\n    </div>\n    <div v-else :class=\"`${prefix}-vertical`\">\n      <div :style=\"{bottom: `${anotherOffset}%`}\" :class=\"[`${prefix}-pane`, 'top-pane']\"><slot name=\"top\"/></div>\n     <div :class=\"`${prefix}-trigger-con`\" :style=\"{top: `${offset}%`}\" @mousedown=\"handleMousedown\">\n        <slot name=\"trigger\">\n          <trigger mode=\"horizontal\"/>\n        </slot>\n      </div>\n      <div :style=\"{top: `${offset}%`}\" :class=\"[`${prefix}-pane`, 'bottom-pane']\"><slot name=\"bottom\"/></div>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { oneOf, on, off } from '@/libs/tools'\nimport Trigger from './trigger.vue'\nexport default {\n  name: 'SplitPane',\n  components: {\n    Trigger\n  },\n  props: {\n    value: {\n      type: [Number, String],\n      default: 0.5\n    },\n    mode: {\n      validator (value) {\n        return oneOf(value, ['horizontal', 'vertical'])\n      },\n      default: 'horizontal'\n    },\n    min: {\n      type: [Number, String],\n      default: '40px'\n    },\n    max: {\n      type: [Number, String],\n      default: '40px'\n    }\n  },\n  /**\n   * Events\n   * @on-move-start\n   * @on-moving 返回值：事件对象，但是在事件对象中加入了两个参数：atMin(当前是否在最小值处), atMax(当前是否在最大值处)\n   * @on-move-end\n   */\n  data () {\n    return {\n      prefix: 'ivu-split',\n      offset: 0,\n      oldOffset: 0,\n      isMoving: false\n    }\n  },\n  computed: {\n    wrapperClasses () {\n      return [\n        `${this.prefix}-wrapper`,\n        this.isMoving ? 'no-select' : ''\n      ]\n    },\n    isHorizontal () {\n      return this.mode === 'horizontal'\n    },\n    anotherOffset () {\n      return 100 - this.offset\n    },\n    valueIsPx () {\n      return typeof this.value === 'string'\n    },\n    offsetSize () {\n      return this.isHorizontal ? 'offsetWidth' : 'offsetHeight'\n    },\n    computedMin () {\n      return this.getComputedThresholdValue('min')\n    },\n    computedMax () {\n      return this.getComputedThresholdValue('max')\n    }\n  },\n  methods: {\n    px2percent (numerator, denominator) {\n      return parseFloat(numerator) / parseFloat(denominator)\n    },\n    getComputedThresholdValue (type) {\n      let size = this.$refs.outerWrapper[this.offsetSize]\n      if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type]\n      else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type]\n    },\n    getMin (value1, value2) {\n      if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`\n      else return Math.min(value1, value2)\n    },\n    getMax (value1, value2) {\n      if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`\n      else return Math.max(value1, value2)\n    },\n    getAnotherOffset (value) {\n      let res = 0\n      if (this.valueIsPx) res = `${this.$refs.outerWrapper[this.offsetSize] - parseFloat(value)}px`\n      else res = 1 - value\n      return res\n    },\n    handleMove (e) {\n      let pageOffset = this.isHorizontal ? e.pageX : e.pageY\n      let offset = pageOffset - this.initOffset\n      let outerWidth = this.$refs.outerWrapper[this.offsetSize]\n      let value = this.valueIsPx ? `${parseFloat(this.oldOffset) + offset}px` : (this.px2percent(outerWidth * this.oldOffset + offset, outerWidth))\n      let anotherValue = this.getAnotherOffset(value)\n      if (parseFloat(value) <= parseFloat(this.computedMin)) value = this.getMax(value, this.computedMin)\n      if (parseFloat(anotherValue) <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax))\n      e.atMin = this.value === this.computedMin\n      e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : this.getAnotherOffset(this.value).toFixed(5) === this.computedMax.toFixed(5)\n      this.$emit('input', value)\n      this.$emit('on-moving', e)\n    },\n    handleUp () {\n      this.isMoving = false\n      off(document, 'mousemove', this.handleMove)\n      off(document, 'mouseup', this.handleUp)\n      this.$emit('on-move-end')\n    },\n    handleMousedown (e) {\n      this.initOffset = this.isHorizontal ? e.pageX : e.pageY\n      this.oldOffset = this.value\n      this.isMoving = true\n      on(document, 'mousemove', this.handleMove)\n      on(document, 'mouseup', this.handleUp)\n      this.$emit('on-move-start')\n    }\n  },\n  watch: {\n    value () {\n      this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100\n    }\n  },\n  mounted () {\n    this.$nextTick(() => {\n      this.offset = (this.valueIsPx ? this.px2percent(this.value, this.$refs.outerWrapper[this.offsetSize]) : this.value) * 10000 / 100\n    })\n  }\n}\n</script>\n\n<style lang=\"less\">\n@import './index.less';\n</style>\n"
  },
  {
    "path": "src/components/split-pane/trigger.vue",
    "content": "<template>\n  <div :class=\"classes\">\n    <div :class=\"barConClasses\">\n      <i :class=\"`${prefix}-bar`\" v-once v-for=\"i in 8\" :key=\"`trigger-${i}`\"></i>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'Trigger',\n  props: {\n    mode: String\n  },\n  data () {\n    return {\n      prefix: 'ivu-split-trigger',\n      initOffset: 0\n    }\n  },\n  computed: {\n    isVertical () {\n      return this.mode === 'vertical'\n    },\n    classes () {\n      return [\n        this.prefix,\n        this.isVertical ? `${this.prefix}-vertical` : `${this.prefix}-horizontal`\n      ]\n    },\n    barConClasses () {\n      return [\n        `${this.prefix}-bar-con`,\n        this.isVertical ? 'vertical' : 'horizontal'\n      ]\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n@import './index.less';\n</style>\n"
  },
  {
    "path": "src/components/tables/edit.vue",
    "content": "<template>\n  <div class=\"tables-edit-outer\">\n    <div v-if=\"!isEditting\" class=\"tables-edit-con\">\n      <span class=\"value-con\">{{ value }}</span>\n      <Button v-if=\"editable\" @click=\"startEdit\" class=\"tables-edit-btn\" style=\"padding: 2px 4px;\" type=\"text\"><Icon type=\"md-create\"></Icon></Button>\n    </div>\n    <div v-else class=\"tables-editting-con\">\n      <Input :value=\"value\" @input=\"handleInput\" class=\"tables-edit-input\"/>\n      <Button @click=\"saveEdit\" style=\"padding: 6px 4px;\" type=\"text\"><Icon type=\"md-checkmark\"></Icon></Button>\n      <Button @click=\"canceltEdit\" style=\"padding: 6px 4px;\" type=\"text\"><Icon type=\"md-close\"></Icon></Button>\n    </div>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'TablesEdit',\n  props: {\n    value: [String, Number],\n    edittingCellId: String,\n    params: Object,\n    editable: Boolean\n  },\n  computed: {\n    isEditting () {\n      return this.edittingCellId === `editting-${this.params.index}-${this.params.column.key}`\n    }\n  },\n  methods: {\n    handleInput (val) {\n      this.$emit('input', val)\n    },\n    startEdit () {\n      this.$emit('on-start-edit', this.params)\n    },\n    saveEdit () {\n      this.$emit('on-save-edit', this.params)\n    },\n    canceltEdit () {\n      this.$emit('on-cancel-edit', this.params)\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.tables-edit-outer{\n  height: 100%;\n  .tables-edit-con{\n    position: relative;\n    height: 100%;\n    .value-con{\n      vertical-align: middle;\n    }\n    .tables-edit-btn{\n      position: absolute;\n      right: 10px;\n      top: 0;\n      display: none;\n    }\n    &:hover{\n      .tables-edit-btn{\n        display: inline-block;\n      }\n    }\n  }\n  .tables-editting-con{\n    .tables-edit-input{\n      width: ~\"calc(100% - 60px)\";\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "src/components/tables/handle-btns.js",
    "content": "const btns = {\n  delete: (h, params, vm) => {\n    return h('Poptip', {\n      props: {\n        confirm: true,\n        title: '你确定要删除吗?'\n      },\n      on: {\n        'on-ok': () => {\n          vm.$emit('on-delete', params)\n          vm.$emit('input', params.tableData.filter((item, index) => index !== params.row.initRowIndex))\n        }\n      }\n    }, [\n      h('Button', {\n        props: {\n          type: 'text',\n          ghost: true\n        }\n      }, [\n        h('Icon', {\n          props: {\n            type: 'md-trash',\n            size: 18,\n            color: '#000000'\n          }\n        })\n      ])\n    ])\n  }\n}\n\nexport default btns\n"
  },
  {
    "path": "src/components/tables/index.js",
    "content": "import Tables from './tables.vue'\nexport default Tables\n"
  },
  {
    "path": "src/components/tables/index.less",
    "content": ".search-con{\n  padding: 10px 0;\n  .search{\n    &-col{\n      display: inline-block;\n      width: 200px;\n    }\n    &-input{\n      display: inline-block;\n      width: 200px;\n      margin-left: 2px;\n    }\n    &-btn{\n      margin-left: 2px;\n    }\n  }\n}\n"
  },
  {
    "path": "src/components/tables/tables.vue",
    "content": "<template>\n  <div>\n    <div v-if=\"searchable && searchPlace === 'top'\" class=\"search-con search-con-top\">\n      <Select v-model=\"searchKey\" class=\"search-col\">\n        <Option v-for=\"item in columns\" v-if=\"item.key !== 'handle'\" :value=\"item.key\" :key=\"`search-col-${item.key}`\">{{ item.title }}</Option>\n      </Select>\n      <Input @on-change=\"handleClear\" clearable placeholder=\"输入关键字搜索\" class=\"search-input\" v-model=\"searchValue\"/>\n      <Button @click=\"handleSearch\" class=\"search-btn\" type=\"primary\"><Icon type=\"search\"/>&nbsp;&nbsp;搜索</Button>\n    </div>\n    <Table\n      ref=\"tablesMain\"\n      :data=\"insideTableData\"\n      :columns=\"insideColumns\"\n      :stripe=\"stripe\"\n      :border=\"border\"\n      :show-header=\"showHeader\"\n      :width=\"width\"\n      :height=\"height\"\n      :loading=\"loading\"\n      :disabled-hover=\"disabledHover\"\n      :highlight-row=\"highlightRow\"\n      :row-class-name=\"rowClassName\"\n      :size=\"size\"\n      :no-data-text=\"noDataText\"\n      :no-filtered-data-text=\"noFilteredDataText\"\n      @on-current-change=\"onCurrentChange\"\n      @on-select=\"onSelect\"\n      @on-select-cancel=\"onSelectCancel\"\n      @on-select-all=\"onSelectAll\"\n      @on-selection-change=\"onSelectionChange\"\n      @on-sort-change=\"onSortChange\"\n      @on-filter-change=\"onFilterChange\"\n      @on-row-click=\"onRowClick\"\n      @on-row-dblclick=\"onRowDblclick\"\n      @on-expand=\"onExpand\"\n    >\n      <slot name=\"header\" slot=\"header\"></slot>\n      <slot name=\"footer\" slot=\"footer\"></slot>\n      <slot name=\"loading\" slot=\"loading\"></slot>\n    </Table>\n    <div v-if=\"searchable && searchPlace === 'bottom'\" class=\"search-con search-con-top\">\n      <Select v-model=\"searchKey\" class=\"search-col\">\n        <Option v-for=\"item in columns\" v-if=\"item.key !== 'handle'\" :value=\"item.key\" :key=\"`search-col-${item.key}`\">{{ item.title }}</Option>\n      </Select>\n      <Input placeholder=\"输入关键字搜索\" class=\"search-input\" v-model=\"searchValue\"/>\n      <Button class=\"search-btn\" type=\"primary\"><Icon type=\"search\"/>&nbsp;&nbsp;搜索</Button>\n    </div>\n    <a id=\"hrefToExportTable\" style=\"display: none;width: 0px;height: 0px;\"></a>\n  </div>\n</template>\n\n<script>\nimport TablesEdit from './edit.vue'\nimport handleBtns from './handle-btns'\nimport './index.less'\nexport default {\n  name: 'Tables',\n  props: {\n    value: {\n      type: Array,\n      default () {\n        return []\n      }\n    },\n    columns: {\n      type: Array,\n      default () {\n        return []\n      }\n    },\n    size: String,\n    width: {\n      type: [Number, String]\n    },\n    height: {\n      type: [Number, String]\n    },\n    stripe: {\n      type: Boolean,\n      default: false\n    },\n    border: {\n      type: Boolean,\n      default: false\n    },\n    showHeader: {\n      type: Boolean,\n      default: true\n    },\n    highlightRow: {\n      type: Boolean,\n      default: false\n    },\n    rowClassName: {\n      type: Function,\n      default () {\n        return ''\n      }\n    },\n    context: {\n      type: Object\n    },\n    noDataText: {\n      type: String\n    },\n    noFilteredDataText: {\n      type: String\n    },\n    disabledHover: {\n      type: Boolean\n    },\n    loading: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * @description 全局设置是否可编辑\n     */\n    editable: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * @description 是否可搜索\n     */\n    searchable: {\n      type: Boolean,\n      default: false\n    },\n    /**\n     * @description 搜索控件所在位置，'top' / 'bottom'\n     */\n    searchPlace: {\n      type: String,\n      default: 'top'\n    }\n  },\n  /**\n   * Events\n   * @on-start-edit 返回值 {Object} ：同iview中render函数中的params对象 { row, index, column }\n   * @on-cancel-edit 返回值 {Object} 同上\n   * @on-save-edit 返回值 {Object} ：除上面三个参数外，还有一个value: 修改后的数据\n   */\n  data () {\n    return {\n      insideColumns: [],\n      insideTableData: [],\n      edittingCellId: '',\n      edittingText: '',\n      searchValue: '',\n      searchKey: ''\n    }\n  },\n  methods: {\n    suportEdit (item, index) {\n      item.render = (h, params) => {\n        return h(TablesEdit, {\n          props: {\n            params: params,\n            value: this.insideTableData[params.index][params.column.key],\n            edittingCellId: this.edittingCellId,\n            editable: this.editable\n          },\n          on: {\n            'input': val => {\n              this.edittingText = val\n            },\n            'on-start-edit': (params) => {\n              this.edittingCellId = `editting-${params.index}-${params.column.key}`\n              this.$emit('on-start-edit', params)\n            },\n            'on-cancel-edit': (params) => {\n              this.edittingCellId = ''\n              this.$emit('on-cancel-edit', params)\n            },\n            'on-save-edit': (params) => {\n              this.value[params.row.initRowIndex][params.column.key] = this.edittingText\n              this.$emit('input', this.value)\n              this.$emit('on-save-edit', Object.assign(params, { value: this.edittingText }))\n              this.edittingCellId = ''\n            }\n          }\n        })\n      }\n      return item\n    },\n    surportHandle (item) {\n      let options = item.options || []\n      let insideBtns = []\n      options.forEach(item => {\n        if (handleBtns[item]) insideBtns.push(handleBtns[item])\n      })\n      let btns = item.button ? [].concat(insideBtns, item.button) : insideBtns\n      item.render = (h, params) => {\n        params.tableData = this.value\n        return h('div', btns.map(item => item(h, params, this)))\n      }\n      return item\n    },\n    handleColumns (columns) {\n      this.insideColumns = columns.map((item, index) => {\n        let res = item\n        if (res.editable) res = this.suportEdit(res, index)\n        if (res.key === 'handle') res = this.surportHandle(res)\n        return res\n      })\n    },\n    setDefaultSearchKey () {\n      this.searchKey = this.columns[0].key !== 'handle' ? this.columns[0].key : (this.columns.length > 1 ? this.columns[1].key : '')\n    },\n    handleClear (e) {\n      if (e.target.value === '') this.insideTableData = this.value\n    },\n    handleSearch () {\n      this.insideTableData = this.value.filter(item => item[this.searchKey].indexOf(this.searchValue) > -1)\n    },\n    handleTableData () {\n      this.insideTableData = this.value.map((item, index) => {\n        let res = item\n        res.initRowIndex = index\n        return res\n      })\n    },\n    exportCsv (params) {\n      this.$refs.tablesMain.exportCsv(params)\n    },\n    clearCurrentRow () {\n      this.$refs.talbesMain.clearCurrentRow()\n    },\n    onCurrentChange (currentRow, oldCurrentRow) {\n      this.$emit('on-current-change', currentRow, oldCurrentRow)\n    },\n    onSelect (selection, row) {\n      this.$emit('on-select', selection, row)\n    },\n    onSelectCancel (selection, row) {\n      this.$emit('on-select-cancel', selection, row)\n    },\n    onSelectAll (selection) {\n      this.$emit('on-select-all', selection)\n    },\n    onSelectionChange (selection) {\n      this.$emit('on-selection-change', selection)\n    },\n    onSortChange (column, key, order) {\n      this.$emit('on-sort-change', column, key, order)\n    },\n    onFilterChange (row) {\n      this.$emit('on-filter-change', row)\n    },\n    onRowClick (row, index) {\n      this.$emit('on-row-click', row, index)\n    },\n    onRowDblclick (row, index) {\n      this.$emit('on-row-dblclick', row, index)\n    },\n    onExpand (row, status) {\n      this.$emit('on-expand', row, status)\n    }\n  },\n  watch: {\n    columns (columns) {\n      this.handleColumns(columns)\n      this.setDefaultSearchKey()\n    },\n    value (val) {\n      this.handleTableData()\n      if (this.searchable) this.handleSearch()\n    }\n  },\n  mounted () {\n    this.handleColumns(this.columns)\n    this.setDefaultSearchKey()\n    this.handleTableData()\n  }\n}\n</script>\n"
  },
  {
    "path": "src/components/tree-select/index.js",
    "content": "export { default } from './tree-select.vue'\n"
  },
  {
    "path": "src/components/tree-select/tree-select-tree.vue",
    "content": "<template>\n  <Tree\n    :data=\"data\"\n    @on-check-change=\"handleCheckSelect\"\n    v-on=\"parent.$listeners\"\n    v-bind=\"parent.$attrs\"\n    :load-data=\"loadDataCallback\"\n    show-checkbox\n  ></Tree>\n</template>\n\n<script>\nimport Emitter from 'iview/src/mixins/emitter.js'\n\nconst arrayEqual = (arr1, arr2) => {\n  // 判断数组的长度\n  if (arr1.length !== arr2.length) {\n    return false\n  } else {\n    // 循环遍历数组的值进行比较\n    for (let i = 0; i < arr1.length; i++) {\n      if (arr1[i] !== arr2[i]) {\n        return false\n      }\n    }\n    return true\n  }\n}\n\nexport default {\n  name: 'TreeSelectTree',\n  mixins: [Emitter],\n  props: {\n    data: {\n      type: Array,\n      default: () => []\n    },\n    selectedArray: {\n      type: Array,\n      default: () => []\n    },\n    loadData: Function\n  },\n  data () {\n    return {\n      flatDic: {},\n      checkedArray: []\n    }\n  },\n  inject: ['parent'],\n  computed: {\n    expandAll () {\n      return this.parent.$attrs['expand-all']\n    }\n  },\n  watch: {\n    data (newData, oldVal) {\n      this.updateFlagDic(newData)\n      let selectArray = []\n      this.selectedArray.forEach(id => {\n        if (id in this.flatDic) selectArray.push(id)\n      })\n      this.$emit('on-check', selectArray.map(id => this.flatDic[id]))\n      if (this.expandAll) this.checkData(newData, false, true)\n    },\n    selectedArray (newVal, oldVal) {\n      if (arrayEqual(newVal, oldVal)) return\n      const filtedNewVal = newVal.filter(id => id in this.flatDic)\n      this.$emit('on-check', filtedNewVal.map(id => this.flatDic[id]))\n      this.$emit('on-clear')\n      this.$nextTick(() => {\n        this.checkData(this.data, true)\n      })\n    }\n  },\n  methods: {\n    checkEmit (value, label) {\n      this.dispatch('iSelect', 'on-select-selected', {\n        value,\n        label\n      })\n      this.$emit('on-select-selected', {\n        value,\n        label\n      })\n    },\n    updateFlagDic (newData) {\n      let newFlagDic = {}\n      this.setFlagDic(newData, item => {\n        newFlagDic[item.id] = item\n      })\n      this.flatDic = newFlagDic\n    },\n    setFlagDic (data, callback) {\n      data.forEach(item => {\n        if (item.children && item.children.length) { this.setFlagDic(item.children, callback) }\n        callback(item)\n      })\n    },\n    handleCheckSelect (selectArray, selectItem) {\n      this.$emit('on-check', selectArray)\n      this.parent.$emit('on-change', selectArray)\n    },\n    checkData (data, emit, expandAll) {\n      data.forEach(item => {\n        if (this.selectedArray.includes(item.id)) {\n          this.$set(item, 'checked', true)\n          this.checkedArray.push(item)\n          if (emit) this.checkEmit(item.id, item.title)\n        } else this.$set(item, 'checked', false)\n        if (item.children && item.children.length) {\n          if (this.expandAll && expandAll) this.$set(item, 'expand', true)\n          this.checkData(item.children, emit, expandAll)\n        }\n      })\n    },\n    loadDataCallback (item, callback) {\n      this.loadData(item, data => {\n        return (() => {\n          callback(data)\n          this.updateFlagDic(this.data)\n        })(data)\n      })\n    }\n  },\n  mounted () {\n    this.checkData(this.data, false, true)\n    this.$nextTick(() => {\n      this.$emit('on-check', this.checkedArray)\n    })\n  }\n}\n</script>\n\n<style></style>\n"
  },
  {
    "path": "src/components/tree-select/tree-select.vue",
    "content": "<template>\n  <Select\n    ref=\"select\"\n    class=\"tree-select\"\n    v-bind=\"$attrs\"\n    @on-change=\"handleChange\"\n    multiple\n  >\n    <tree-select-tree-item\n      :selectedArray=\"value\"\n      :data=\"data\"\n      @on-clear=\"handleClear\"\n      :load-data=\"loadData\"\n      @on-check=\"handleTreeCheck\"\n    ></tree-select-tree-item>\n  </Select>\n</template>\n\n<script>\nimport Emitter from 'iview/src/mixins/emitter'\nimport TreeSelectTreeItem from './tree-select-tree.vue'\nexport default {\n  name: 'TreeSelect',\n  mixins: [Emitter],\n  components: {\n    TreeSelectTreeItem\n  },\n  props: {\n    value: {\n      type: Array,\n      default: () => []\n    },\n    data: {\n      type: Array,\n      default: () => []\n    },\n    loadData: Function\n  },\n  data () {\n    return {\n      isChangedByTree: true,\n      isInit: true\n    }\n  },\n  provide () {\n    return {\n      parent: this\n    }\n  },\n  methods: {\n    handleChange (selected) {\n      if (!this.isChangedByTree) this.$emit('input', selected)\n      this.isChangedByTree = false\n    },\n    handleTreeCheck (selectedArray) {\n      this.isChangedByTree = true\n      this.$emit('input', selectedArray.map(item => item.id))\n    },\n    handleClear () {\n      this.$refs.select.reset()\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.tree-select {\n  .ivu-select-dropdown {\n    padding: 0 6px;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/config/index.js",
    "content": "export default {\n  /**\n   * @description 配置显示在浏览器标签的title\n   */\n  title: 'iView-admin',\n  /**\n   * @description token在Cookie中存储的天数，默认1天\n   */\n  cookieExpires: 1,\n  /**\n   * @description 是否使用国际化，默认为false\n   *              如果不使用，则需要在路由中给需要在菜单中展示的路由设置meta: {title: 'xxx'}\n   *              用来在菜单中显示文字\n   */\n  useI18n: true,\n  /**\n   * @description api请求基础路径\n   */\n  baseUrl: {\n    dev: 'https://www.easy-mock.com/mock/5add9213ce4d0e69998a6f51/iview-admin/',\n    pro: 'https://produce.com'\n  },\n  /**\n   * @description 默认打开的首页的路由name值，默认为home\n   */\n  homeName: 'home',\n  /**\n   * @description 需要加载的插件\n   */\n  plugin: {\n    'error-store': {\n      showInHeader: true, // 设为false后不会在顶部显示错误日志徽标\n      developmentOff: true // 设为true后在开发环境不会收集错误信息，方便开发中排查错误\n    }\n  }\n}\n"
  },
  {
    "path": "src/directive/directives.js",
    "content": "import draggable from './module/draggable'\nimport clipboard from './module/clipboard'\n\nconst directives = {\n  draggable,\n  clipboard\n}\n\nexport default directives\n"
  },
  {
    "path": "src/directive/index.js",
    "content": "import directive from './directives'\n\nconst importDirective = Vue => {\n  /**\n   * 拖拽指令 v-draggable=\"options\"\n   * options = {\n   *  trigger: /这里传入作为拖拽触发器的CSS选择器/,\n   *  body:    /这里传入需要移动容器的CSS选择器/,\n   *  recover: /拖动结束之后是否恢复到原来的位置/\n   * }\n   */\n  Vue.directive('draggable', directive.draggable)\n  /**\n   * clipboard指令 v-draggable=\"options\"\n   * options = {\n   *  value:    /在输入框中使用v-model绑定的值/,\n   *  success:  /复制成功后的回调/,\n   *  error:    /复制失败后的回调/\n   * }\n   */\n  Vue.directive('clipboard', directive.clipboard)\n}\n\nexport default importDirective\n"
  },
  {
    "path": "src/directive/module/clipboard.js",
    "content": "import Clipboard from 'clipboard'\nexport default {\n  bind: (el, binding) => {\n    const clipboard = new Clipboard(el, {\n      text: () => binding.value.value\n    })\n    el.__success_callback__ = binding.value.success\n    el.__error_callback__ = binding.value.error\n    clipboard.on('success', e => {\n      const callback = el.__success_callback__\n      callback && callback(e)\n    })\n    clipboard.on('error', e => {\n      const callback = el.__error_callback__\n      callback && callback(e)\n    })\n    el.__clipboard__ = clipboard\n  },\n  update: (el, binding) => {\n    el.__clipboard__.text = () => binding.value.value\n    el.__success_callback__ = binding.value.success\n    el.__error_callback__ = binding.value.error\n  },\n  unbind: (el, binding) => {\n    delete el.__success_callback__\n    delete el.__error_callback__\n    el.__clipboard__.destroy()\n    delete el.__clipboard__\n  }\n}\n"
  },
  {
    "path": "src/directive/module/draggable.js",
    "content": "import { on } from '@/libs/tools'\nexport default {\n  inserted: (el, binding, vnode) => {\n    let triggerDom = document.querySelector(binding.value.trigger)\n    triggerDom.style.cursor = 'move'\n    let bodyDom = document.querySelector(binding.value.body)\n    let pageX = 0\n    let pageY = 0\n    let transformX = 0\n    let transformY = 0\n    let canMove = false\n    const handleMousedown = e => {\n      let transform = /\\(.*\\)/.exec(bodyDom.style.transform)\n      if (transform) {\n        transform = transform[0].slice(1, transform[0].length - 1)\n        let splitxy = transform.split('px, ')\n        transformX = parseFloat(splitxy[0])\n        transformY = parseFloat(splitxy[1].split('px')[0])\n      }\n      pageX = e.pageX\n      pageY = e.pageY\n      canMove = true\n    }\n    const handleMousemove = e => {\n      let xOffset = e.pageX - pageX + transformX\n      let yOffset = e.pageY - pageY + transformY\n      if (canMove) bodyDom.style.transform = `translate(${xOffset}px, ${yOffset}px)`\n    }\n    const handleMouseup = e => {\n      canMove = false\n    }\n    on(triggerDom, 'mousedown', handleMousedown)\n    on(document, 'mousemove', handleMousemove)\n    on(document, 'mouseup', handleMouseup)\n  },\n  update: (el, binding, vnode) => {\n    if (!binding.value.recover) return\n    let bodyDom = document.querySelector(binding.value.body)\n    bodyDom.style.transform = ''\n  }\n}\n"
  },
  {
    "path": "src/index.less",
    "content": "@import '~iview/src/styles/index.less';\n\n@menu-dark-title: #001529;\n@menu-dark-active-bg: #000c17;\n@layout-sider-background: #001529;\n"
  },
  {
    "path": "src/libs/api.request.js",
    "content": "import HttpRequest from '@/libs/axios'\nimport config from '@/config'\nconst baseUrl = process.env.NODE_ENV === 'development' ? config.baseUrl.dev : config.baseUrl.pro\n\nconst axios = new HttpRequest(baseUrl)\nexport default axios\n"
  },
  {
    "path": "src/libs/axios.js",
    "content": "import axios from 'axios'\nimport store from '@/store'\n// import { Spin } from 'iview'\nconst addErrorLog = errorInfo => {\n  const { statusText, status, request: { responseURL } } = errorInfo\n  let info = {\n    type: 'ajax',\n    code: status,\n    mes: statusText,\n    url: responseURL\n  }\n  if (!responseURL.includes('save_error_logger')) store.dispatch('addErrorLog', info)\n}\n\nclass HttpRequest {\n  constructor (baseUrl = baseURL) {\n    this.baseUrl = baseUrl\n    this.queue = {}\n  }\n  getInsideConfig () {\n    const config = {\n      baseURL: this.baseUrl,\n      headers: {\n        //\n      }\n    }\n    return config\n  }\n  destroy (url) {\n    delete this.queue[url]\n    if (!Object.keys(this.queue).length) {\n      // Spin.hide()\n    }\n  }\n  interceptors (instance, url) {\n    // 请求拦截\n    instance.interceptors.request.use(config => {\n      // 添加全局的loading...\n      if (!Object.keys(this.queue).length) {\n        // Spin.show() // 不建议开启，因为界面不友好\n      }\n      this.queue[url] = true\n      return config\n    }, error => {\n      return Promise.reject(error)\n    })\n    // 响应拦截\n    instance.interceptors.response.use(res => {\n      this.destroy(url)\n      const { data, status } = res\n      return { data, status }\n    }, error => {\n      this.destroy(url)\n      let errorInfo = error.response\n      if (!errorInfo) {\n        const { request: { statusText, status }, config } = JSON.parse(JSON.stringify(error))\n        errorInfo = {\n          statusText,\n          status,\n          request: { responseURL: config.url }\n        }\n      }\n      addErrorLog(errorInfo)\n      return Promise.reject(error)\n    })\n  }\n  request (options) {\n    const instance = axios.create()\n    options = Object.assign(this.getInsideConfig(), options)\n    this.interceptors(instance, options.url)\n    return instance(options)\n  }\n}\nexport default HttpRequest\n"
  },
  {
    "path": "src/libs/excel.js",
    "content": "/* eslint-disable */\nimport XLSX from 'xlsx';\n\nfunction auto_width(ws, data){\n    /*set worksheet max width per col*/\n    const colWidth = data.map(row => row.map(val => {\n        /*if null/undefined*/\n        if (val == null) {\n            return {'wch': 10};\n        }\n        /*if chinese*/\n        else if (val.toString().charCodeAt(0) > 255) {\n            return {'wch': val.toString().length * 2};\n        } else {\n            return {'wch': val.toString().length};\n        }\n    }))\n    /*start in the first row*/\n    let result = colWidth[0];\n    for (let i = 1; i < colWidth.length; i++) {\n        for (let j = 0; j < colWidth[i].length; j++) {\n            if (result[j]['wch'] < colWidth[i][j]['wch']) {\n                result[j]['wch'] = colWidth[i][j]['wch'];\n            }\n        }\n    }\n    ws['!cols'] = result;\n}\n\nfunction json_to_array(key, jsonData){\n    return jsonData.map(v => key.map(j => { return v[j] }));\n}\n\n// fix data,return string\nfunction fixdata(data) {\n    let o = ''\n    let l = 0\n    const w = 10240\n    for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))\n    o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))\n    return o\n}\n\n// get head from excel file,return array\nfunction get_header_row(sheet) {\n    const headers = []\n    const range = XLSX.utils.decode_range(sheet['!ref'])\n    let C\n    const R = range.s.r /* start in the first row */\n    for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */\n        var cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })] /* find the cell in the first row */\n        var hdr = 'UNKNOWN ' + C // <-- replace with your desired default\n        if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)\n        headers.push(hdr)\n    }\n    return headers\n}\n\nexport const export_table_to_excel= (id, filename) => {\n    const table = document.getElementById(id);\n    const wb = XLSX.utils.table_to_book(table);\n    XLSX.writeFile(wb, filename);\n\n    /* the second way */\n    // const table = document.getElementById(id);\n    // const wb = XLSX.utils.book_new();\n    // const ws = XLSX.utils.table_to_sheet(table);\n    // XLSX.utils.book_append_sheet(wb, ws, filename);\n    // XLSX.writeFile(wb, filename);\n}\n\nexport const export_json_to_excel = ({data, key, title, filename, autoWidth}) => {\n    const wb = XLSX.utils.book_new();\n    data.unshift(title);\n    const ws = XLSX.utils.json_to_sheet(data, {header: key, skipHeader: true});\n    if(autoWidth){\n        const arr = json_to_array(key, data);\n        auto_width(ws, arr);\n    }\n    XLSX.utils.book_append_sheet(wb, ws, filename);\n    XLSX.writeFile(wb, filename + '.xlsx');\n}\n\nexport const export_array_to_excel = ({key, data, title, filename, autoWidth}) => {\n    const wb = XLSX.utils.book_new();\n    const arr = json_to_array(key, data);\n    arr.unshift(title);\n    const ws = XLSX.utils.aoa_to_sheet(arr);\n    if(autoWidth){\n        auto_width(ws, arr);\n    }\n    XLSX.utils.book_append_sheet(wb, ws, filename);\n    XLSX.writeFile(wb, filename + '.xlsx');\n}\n\nexport const read = (data, type) => {\n    /* if type == 'base64' must fix data first */\n    // const fixedData = fixdata(data)\n    // const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })\n    const workbook = XLSX.read(data, { type: type });\n    const firstSheetName = workbook.SheetNames[0];\n    const worksheet = workbook.Sheets[firstSheetName];\n    const header = get_header_row(worksheet);\n    const results = XLSX.utils.sheet_to_json(worksheet);\n    return {header, results};\n}\n\nexport default {\n  export_table_to_excel,\n  export_array_to_excel,\n  export_json_to_excel,\n  read\n}\n"
  },
  {
    "path": "src/libs/render-dom.js",
    "content": "export default {\n  name: 'RenderDom',\n  functional: true,\n  props: {\n    render: Function\n  },\n  render: (h, ctx) => {\n    return ctx.props.render(h)\n  }\n}\n"
  },
  {
    "path": "src/libs/tools.js",
    "content": "export const forEach = (arr, fn) => {\n  if (!arr.length || !fn) return\n  let i = -1\n  let len = arr.length\n  while (++i < len) {\n    let item = arr[i]\n    fn(item, i, arr)\n  }\n}\n\n/**\n * @param {Array} arr1\n * @param {Array} arr2\n * @description 得到两个数组的交集, 两个数组的元素为数值或字符串\n */\nexport const getIntersection = (arr1, arr2) => {\n  let len = Math.min(arr1.length, arr2.length)\n  let i = -1\n  let res = []\n  while (++i < len) {\n    const item = arr2[i]\n    if (arr1.indexOf(item) > -1) res.push(item)\n  }\n  return res\n}\n\n/**\n * @param {Array} arr1\n * @param {Array} arr2\n * @description 得到两个数组的并集, 两个数组的元素为数值或字符串\n */\nexport const getUnion = (arr1, arr2) => {\n  return Array.from(new Set([...arr1, ...arr2]))\n}\n\n/**\n * @param {Array} target 目标数组\n * @param {Array} arr 需要查询的数组\n * @description 判断要查询的数组是否至少有一个元素包含在目标数组中\n */\nexport const hasOneOf = (targetarr, arr) => {\n  return targetarr.some(_ => arr.indexOf(_) > -1)\n}\n\n/**\n * @param {String|Number} value 要验证的字符串或数值\n * @param {*} validList 用来验证的列表\n */\nexport function oneOf (value, validList) {\n  for (let i = 0; i < validList.length; i++) {\n    if (value === validList[i]) {\n      return true\n    }\n  }\n  return false\n}\n\n/**\n * @param {Number} timeStamp 判断时间戳格式是否是毫秒\n * @returns {Boolean}\n */\nconst isMillisecond = timeStamp => {\n  const timeStr = String(timeStamp)\n  return timeStr.length > 10\n}\n\n/**\n * @param {Number} timeStamp 传入的时间戳\n * @param {Number} currentTime 当前时间时间戳\n * @returns {Boolean} 传入的时间戳是否早于当前时间戳\n */\nconst isEarly = (timeStamp, currentTime) => {\n  return timeStamp < currentTime\n}\n\n/**\n * @param {Number} num 数值\n * @returns {String} 处理后的字符串\n * @description 如果传入的数值小于10，即位数只有1位，则在前面补充0\n */\nconst getHandledValue = num => {\n  return num < 10 ? '0' + num : num\n}\n\n/**\n * @param {Number} timeStamp 传入的时间戳\n * @param {Number} startType 要返回的时间字符串的格式类型，传入'year'则返回年开头的完整时间\n */\nconst getDate = (timeStamp, startType) => {\n  const d = new Date(timeStamp * 1000)\n  const year = d.getFullYear()\n  const month = getHandledValue(d.getMonth() + 1)\n  const date = getHandledValue(d.getDate())\n  const hours = getHandledValue(d.getHours())\n  const minutes = getHandledValue(d.getMinutes())\n  const second = getHandledValue(d.getSeconds())\n  let resStr = ''\n  if (startType === 'year') resStr = year + '-' + month + '-' + date + ' ' + hours + ':' + minutes + ':' + second\n  else resStr = month + '-' + date + ' ' + hours + ':' + minutes\n  return resStr\n}\n\n/**\n * @param {String|Number} timeStamp 时间戳\n * @returns {String} 相对时间字符串\n */\nexport const getRelativeTime = timeStamp => {\n  // 判断当前传入的时间戳是秒格式还是毫秒\n  const IS_MILLISECOND = isMillisecond(timeStamp)\n  // 如果是毫秒格式则转为秒格式\n  if (IS_MILLISECOND) Math.floor(timeStamp /= 1000)\n  // 传入的时间戳可以是数值或字符串类型，这里统一转为数值类型\n  timeStamp = Number(timeStamp)\n  // 获取当前时间时间戳\n  const currentTime = Math.floor(Date.parse(new Date()) / 1000)\n  // 判断传入时间戳是否早于当前时间戳\n  const IS_EARLY = isEarly(timeStamp, currentTime)\n  // 获取两个时间戳差值\n  let diff = currentTime - timeStamp\n  // 如果IS_EARLY为false则差值取反\n  if (!IS_EARLY) diff = -diff\n  let resStr = ''\n  const dirStr = IS_EARLY ? '前' : '后'\n  // 少于等于59秒\n  if (diff <= 59) resStr = diff + '秒' + dirStr\n  // 多于59秒，少于等于59分钟59秒\n  else if (diff > 59 && diff <= 3599) resStr = Math.floor(diff / 60) + '分钟' + dirStr\n  // 多于59分钟59秒，少于等于23小时59分钟59秒\n  else if (diff > 3599 && diff <= 86399) resStr = Math.floor(diff / 3600) + '小时' + dirStr\n  // 多于23小时59分钟59秒，少于等于29天59分钟59秒\n  else if (diff > 86399 && diff <= 2623859) resStr = Math.floor(diff / 86400) + '天' + dirStr\n  // 多于29天59分钟59秒，少于364天23小时59分钟59秒，且传入的时间戳早于当前\n  else if (diff > 2623859 && diff <= 31567859 && IS_EARLY) resStr = getDate(timeStamp)\n  else resStr = getDate(timeStamp, 'year')\n  return resStr\n}\n\n/**\n * @returns {String} 当前浏览器名称\n */\nexport const getExplorer = () => {\n  const ua = window.navigator.userAgent\n  const isExplorer = (exp) => {\n    return ua.indexOf(exp) > -1\n  }\n  if (isExplorer('MSIE')) return 'IE'\n  else if (isExplorer('Firefox')) return 'Firefox'\n  else if (isExplorer('Chrome')) return 'Chrome'\n  else if (isExplorer('Opera')) return 'Opera'\n  else if (isExplorer('Safari')) return 'Safari'\n}\n\n/**\n * @description 绑定事件 on(element, event, handler)\n */\nexport const on = (function () {\n  if (document.addEventListener) {\n    return function (element, event, handler) {\n      if (element && event && handler) {\n        element.addEventListener(event, handler, false)\n      }\n    }\n  } else {\n    return function (element, event, handler) {\n      if (element && event && handler) {\n        element.attachEvent('on' + event, handler)\n      }\n    }\n  }\n})()\n\n/**\n * @description 解绑事件 off(element, event, handler)\n */\nexport const off = (function () {\n  if (document.removeEventListener) {\n    return function (element, event, handler) {\n      if (element && event) {\n        element.removeEventListener(event, handler, false)\n      }\n    }\n  } else {\n    return function (element, event, handler) {\n      if (element && event) {\n        element.detachEvent('on' + event, handler)\n      }\n    }\n  }\n})()\n\n/**\n * 判断一个对象是否存在key，如果传入第二个参数key，则是判断这个obj对象是否存在key这个属性\n * 如果没有传入key这个参数，则判断obj对象是否有键值对\n */\nexport const hasKey = (obj, key) => {\n  if (key) return key in obj\n  else {\n    let keysArr = Object.keys(obj)\n    return keysArr.length\n  }\n}\n\n/**\n * @param {*} obj1 对象\n * @param {*} obj2 对象\n * @description 判断两个对象是否相等，这两个对象的值只能是数字或字符串\n */\nexport const objEqual = (obj1, obj2) => {\n  const keysArr1 = Object.keys(obj1)\n  const keysArr2 = Object.keys(obj2)\n  if (keysArr1.length !== keysArr2.length) return false\n  else if (keysArr1.length === 0 && keysArr2.length === 0) return true\n  /* eslint-disable-next-line */\n  else return !keysArr1.some(key => obj1[key] != obj2[key])\n}\n"
  },
  {
    "path": "src/libs/util.js",
    "content": "import Cookies from 'js-cookie'\n// cookie保存的天数\nimport config from '@/config'\nimport { forEach, hasOneOf, objEqual } from '@/libs/tools'\nconst { title, cookieExpires, useI18n } = config\n\nexport const TOKEN_KEY = 'token'\n\nexport const setToken = (token) => {\n  Cookies.set(TOKEN_KEY, token, { expires: cookieExpires || 1 })\n}\n\nexport const getToken = () => {\n  const token = Cookies.get(TOKEN_KEY)\n  if (token) return token\n  else return false\n}\n\nexport const hasChild = (item) => {\n  return item.children && item.children.length !== 0\n}\n\nconst showThisMenuEle = (item, access) => {\n  if (item.meta && item.meta.access && item.meta.access.length) {\n    if (hasOneOf(item.meta.access, access)) return true\n    else return false\n  } else return true\n}\n/**\n * @param {Array} list 通过路由列表得到菜单列表\n * @returns {Array}\n */\nexport const getMenuByRouter = (list, access) => {\n  let res = []\n  forEach(list, item => {\n    if (!item.meta || (item.meta && !item.meta.hideInMenu)) {\n      let obj = {\n        icon: (item.meta && item.meta.icon) || '',\n        name: item.name,\n        meta: item.meta\n      }\n      if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {\n        obj.children = getMenuByRouter(item.children, access)\n      }\n      if (item.meta && item.meta.href) obj.href = item.meta.href\n      if (showThisMenuEle(item, access)) res.push(obj)\n    }\n  })\n  return res\n}\n\n/**\n * @param {Array} routeMetched 当前路由metched\n * @returns {Array}\n */\nexport const getBreadCrumbList = (route, homeRoute) => {\n  let homeItem = { ...homeRoute, icon: homeRoute.meta.icon }\n  let routeMetched = route.matched\n  if (routeMetched.some(item => item.name === homeRoute.name)) return [homeItem]\n  let res = routeMetched.filter(item => {\n    return item.meta === undefined || !item.meta.hideInBread\n  }).map(item => {\n    let meta = { ...item.meta }\n    if (meta.title && typeof meta.title === 'function') {\n      meta.__titleIsFunction__ = true\n      meta.title = meta.title(route)\n    }\n    let obj = {\n      icon: (item.meta && item.meta.icon) || '',\n      name: item.name,\n      meta: meta\n    }\n    return obj\n  })\n  res = res.filter(item => {\n    return !item.meta.hideInMenu\n  })\n  return [{ ...homeItem, to: homeRoute.path }, ...res]\n}\n\nexport const getRouteTitleHandled = (route) => {\n  let router = { ...route }\n  let meta = { ...route.meta }\n  let title = ''\n  if (meta.title) {\n    if (typeof meta.title === 'function') {\n      meta.__titleIsFunction__ = true\n      title = meta.title(router)\n    } else title = meta.title\n  }\n  meta.title = title\n  router.meta = meta\n  return router\n}\n\nexport const showTitle = (item, vm) => {\n  let { title, __titleIsFunction__ } = item.meta\n  if (!title) return\n  if (useI18n) {\n    if (title.includes('{{') && title.includes('}}') && useI18n) title = title.replace(/({{[\\s\\S]+?}})/, (m, str) => str.replace(/{{([\\s\\S]*)}}/, (m, _) => vm.$t(_.trim())))\n    else if (__titleIsFunction__) title = item.meta.title\n    else title = vm.$t(item.name)\n  } else title = (item.meta && item.meta.title) || item.name\n  return title\n}\n\n/**\n * @description 本地存储和获取标签导航列表\n */\nexport const setTagNavListInLocalstorage = list => {\n  localStorage.tagNaveList = JSON.stringify(list)\n}\n/**\n * @returns {Array} 其中的每个元素只包含路由原信息中的name, path, meta三项\n */\nexport const getTagNavListFromLocalstorage = () => {\n  const list = localStorage.tagNaveList\n  return list ? JSON.parse(list) : []\n}\n\n/**\n * @param {Array} routers 路由列表数组\n * @description 用于找到路由列表中name为home的对象\n */\nexport const getHomeRoute = (routers, homeName = 'home') => {\n  let i = -1\n  let len = routers.length\n  let homeRoute = {}\n  while (++i < len) {\n    let item = routers[i]\n    if (item.children && item.children.length) {\n      let res = getHomeRoute(item.children, homeName)\n      if (res.name) return res\n    } else {\n      if (item.name === homeName) homeRoute = item\n    }\n  }\n  return homeRoute\n}\n\n/**\n * @param {*} list 现有标签导航列表\n * @param {*} newRoute 新添加的路由原信息对象\n * @description 如果该newRoute已经存在则不再添加\n */\nexport const getNewTagList = (list, newRoute) => {\n  const { name, path, meta } = newRoute\n  let newList = [...list]\n  if (newList.findIndex(item => item.name === name) >= 0) return newList\n  else newList.push({ name, path, meta })\n  return newList\n}\n\n/**\n * @param {*} access 用户权限数组，如 ['super_admin', 'admin']\n * @param {*} route 路由列表\n */\nconst hasAccess = (access, route) => {\n  if (route.meta && route.meta.access) return hasOneOf(access, route.meta.access)\n  else return true\n}\n\n/**\n * 权鉴\n * @param {*} name 即将跳转的路由name\n * @param {*} access 用户权限数组\n * @param {*} routes 路由列表\n * @description 用户是否可跳转到该页\n */\nexport const canTurnTo = (name, access, routes) => {\n  const routePermissionJudge = (list) => {\n    return list.some(item => {\n      if (item.children && item.children.length) {\n        return routePermissionJudge(item.children)\n      } else if (item.name === name) {\n        return hasAccess(access, item)\n      }\n    })\n  }\n\n  return routePermissionJudge(routes)\n}\n\n/**\n * @param {String} url\n * @description 从URL中解析参数\n */\nexport const getParams = url => {\n  const keyValueArr = url.split('?')[1].split('&')\n  let paramObj = {}\n  keyValueArr.forEach(item => {\n    const keyValue = item.split('=')\n    paramObj[keyValue[0]] = keyValue[1]\n  })\n  return paramObj\n}\n\n/**\n * @param {Array} list 标签列表\n * @param {String} name 当前关闭的标签的name\n */\nexport const getNextRoute = (list, route) => {\n  let res = {}\n  if (list.length === 2) {\n    res = getHomeRoute(list)\n  } else {\n    const index = list.findIndex(item => routeEqual(item, route))\n    if (index === list.length - 1) res = list[list.length - 2]\n    else res = list[index + 1]\n  }\n  return res\n}\n\n/**\n * @param {Number} times 回调函数需要执行的次数\n * @param {Function} callback 回调函数\n */\nexport const doCustomTimes = (times, callback) => {\n  let i = -1\n  while (++i < times) {\n    callback(i)\n  }\n}\n\n/**\n * @param {Object} file 从上传组件得到的文件对象\n * @returns {Promise} resolve参数是解析后的二维数组\n * @description 从Csv文件中解析出表格，解析成二维数组\n */\nexport const getArrayFromFile = (file) => {\n  let nameSplit = file.name.split('.')\n  let format = nameSplit[nameSplit.length - 1]\n  return new Promise((resolve, reject) => {\n    let reader = new FileReader()\n    reader.readAsText(file) // 以文本格式读取\n    let arr = []\n    reader.onload = function (evt) {\n      let data = evt.target.result // 读到的数据\n      let pasteData = data.trim()\n      arr = pasteData.split((/[\\n\\u0085\\u2028\\u2029]|\\r\\n?/g)).map(row => {\n        return row.split('\\t')\n      }).map(item => {\n        return item[0].split(',')\n      })\n      if (format === 'csv') resolve(arr)\n      else reject(new Error('[Format Error]:你上传的不是Csv文件'))\n    }\n  })\n}\n\n/**\n * @param {Array} array 表格数据二维数组\n * @returns {Object} { columns, tableData }\n * @description 从二维数组中获取表头和表格数据，将第一行作为表头，用于在iView的表格中展示数据\n */\nexport const getTableDataFromArray = (array) => {\n  let columns = []\n  let tableData = []\n  if (array.length > 1) {\n    let titles = array.shift()\n    columns = titles.map(item => {\n      return {\n        title: item,\n        key: item\n      }\n    })\n    tableData = array.map(item => {\n      let res = {}\n      item.forEach((col, i) => {\n        res[titles[i]] = col\n      })\n      return res\n    })\n  }\n  return {\n    columns,\n    tableData\n  }\n}\n\nexport const findNodeUpper = (ele, tag) => {\n  if (ele.parentNode) {\n    if (ele.parentNode.tagName === tag.toUpperCase()) {\n      return ele.parentNode\n    } else {\n      return findNodeUpper(ele.parentNode, tag)\n    }\n  }\n}\n\nexport const findNodeUpperByClasses = (ele, classes) => {\n  let parentNode = ele.parentNode\n  if (parentNode) {\n    let classList = parentNode.classList\n    if (classList && classes.every(className => classList.contains(className))) {\n      return parentNode\n    } else {\n      return findNodeUpperByClasses(parentNode, classes)\n    }\n  }\n}\n\nexport const findNodeDownward = (ele, tag) => {\n  const tagName = tag.toUpperCase()\n  if (ele.childNodes.length) {\n    let i = -1\n    let len = ele.childNodes.length\n    while (++i < len) {\n      let child = ele.childNodes[i]\n      if (child.tagName === tagName) return child\n      else return findNodeDownward(child, tag)\n    }\n  }\n}\n\nexport const showByAccess = (access, canViewAccess) => {\n  return hasOneOf(canViewAccess, access)\n}\n\n/**\n * @description 根据name/params/query判断两个路由对象是否相等\n * @param {*} route1 路由对象\n * @param {*} route2 路由对象\n */\nexport const routeEqual = (route1, route2) => {\n  const params1 = route1.params || {}\n  const params2 = route2.params || {}\n  const query1 = route1.query || {}\n  const query2 = route2.query || {}\n  return (route1.name === route2.name) && objEqual(params1, params2) && objEqual(query1, query2)\n}\n\n/**\n * 判断打开的标签列表里是否已存在这个新添加的路由对象\n */\nexport const routeHasExist = (tagNavList, routeItem) => {\n  let len = tagNavList.length\n  let res = false\n  doCustomTimes(len, (index) => {\n    if (routeEqual(tagNavList[index], routeItem)) res = true\n  })\n  return res\n}\n\nexport const localSave = (key, value) => {\n  localStorage.setItem(key, value)\n}\n\nexport const localRead = (key) => {\n  return localStorage.getItem(key) || ''\n}\n\n// scrollTop animation\nexport const scrollTop = (el, from = 0, to, duration = 500, endCallback) => {\n  if (!window.requestAnimationFrame) {\n    window.requestAnimationFrame = (\n      window.webkitRequestAnimationFrame ||\n      window.mozRequestAnimationFrame ||\n      window.msRequestAnimationFrame ||\n      function (callback) {\n        return window.setTimeout(callback, 1000 / 60)\n      }\n    )\n  }\n  const difference = Math.abs(from - to)\n  const step = Math.ceil(difference / duration * 50)\n\n  const scroll = (start, end, step) => {\n    if (start === end) {\n      endCallback && endCallback()\n      return\n    }\n\n    let d = (start + step > end) ? end : start + step\n    if (start > end) {\n      d = (start - step < end) ? end : start - step\n    }\n\n    if (el === window) {\n      window.scrollTo(d, d)\n    } else {\n      el.scrollTop = d\n    }\n    window.requestAnimationFrame(() => scroll(d, end, step))\n  }\n  scroll(from, to, step)\n}\n\n/**\n * @description 根据当前跳转的路由设置显示在浏览器标签的title\n * @param {Object} routeItem 路由对象\n * @param {Object} vm Vue实例\n */\nexport const setTitle = (routeItem, vm) => {\n  const handledRoute = getRouteTitleHandled(routeItem)\n  const pageTitle = showTitle(handledRoute, vm)\n  const resTitle = pageTitle ? `${title} - ${pageTitle}` : title\n  window.document.title = resTitle\n}\n"
  },
  {
    "path": "src/locale/index.js",
    "content": "import Vue from 'vue'\nimport VueI18n from 'vue-i18n'\nimport { localRead } from '@/libs/util'\nimport customZhCn from './lang/zh-CN'\nimport customZhTw from './lang/zh-TW'\nimport customEnUs from './lang/en-US'\nimport zhCnLocale from 'iview/src/locale/lang/zh-CN'\nimport enUsLocale from 'iview/src/locale/lang/en-US'\nimport zhTwLocale from 'iview/src/locale/lang/zh-TW'\n\nVue.use(VueI18n)\n\n// 自动根据浏览器系统语言设置语言\nconst navLang = navigator.language\nconst localLang = (navLang === 'zh-CN' || navLang === 'en-US') ? navLang : false\nlet lang = localLang || localRead('local') || 'zh-CN'\n\nVue.config.lang = lang\n\n// vue-i18n 6.x+写法\nVue.locale = () => {}\nconst messages = {\n  'zh-CN': Object.assign(zhCnLocale, customZhCn),\n  'zh-TW': Object.assign(zhTwLocale, customZhTw),\n  'en-US': Object.assign(enUsLocale, customEnUs)\n}\nconst i18n = new VueI18n({\n  locale: lang,\n  messages\n})\n\nexport default i18n\n\n// vue-i18n 5.x写法\n// Vue.locale('zh-CN', Object.assign(zhCnLocale, customZhCn))\n// Vue.locale('en-US', Object.assign(zhTwLocale, customZhTw))\n// Vue.locale('zh-TW', Object.assign(enUsLocale, customEnUs))\n"
  },
  {
    "path": "src/locale/lang/en-US.js",
    "content": "export default {\n  home: 'Home',\n  login: 'Login',\n  components: 'Components',\n  count_to_page: 'Count-to',\n  tables_page: 'Table',\n  split_pane_page: 'Split-pane',\n  markdown_page: 'Markdown-editor',\n  editor_page: 'Rich-Text-Editor',\n  icons_page: 'Custom-icon',\n  img_cropper_page: 'Image-editor',\n  update: 'Update',\n  doc: 'Document',\n  join_page: 'QQ Group',\n  update_table_page: 'Update .CSV',\n  update_paste_page: 'Paste Table Data',\n  multilevel: 'multilevel',\n  directive_page: 'Directive',\n  level_1: 'Level-1',\n  level_2: 'Level-2',\n  level_2_1: 'Level-2-1',\n  level_2_3: 'Level-2-3',\n  level_2_2: 'Level-2-2',\n  level_2_2_1: 'Level-2-2-1',\n  level_2_2_2: 'Level-2-2-2',\n  excel: 'Excel',\n  'upload-excel': 'Upload Excel',\n  'export-excel': 'Export Excel',\n  tools_methods_page: 'Tools Methods',\n  drag_list_page: 'Drag-list',\n  i18n_page: 'Internationalization',\n  modalTitle: 'Modal Title',\n  content: 'This is the modal box content.',\n  buttonText: 'Show Modal',\n  'i18n-tip': 'Note: Only this page is multi-language, other pages do not add language content to the multi-language package.',\n  error_store_page: 'Error Collection',\n  error_logger_page: 'Error Logger',\n  query: 'Query',\n  params: 'Params',\n  cropper_page: 'Cropper',\n  message_page: 'Message Center',\n  tree_table_page: 'Tree Table',\n  org_tree_page: 'Org Tree',\n  drag_drawer_page: 'Draggable Drawer',\n  tree_select_page: 'Tree Selector'\n}\n"
  },
  {
    "path": "src/locale/lang/zh-CN.js",
    "content": "export default {\n  home: '首页',\n  login: '登录',\n  components: '组件',\n  count_to_page: '数字渐变',\n  tables_page: '多功能表格',\n  split_pane_page: '分割窗口',\n  markdown_page: 'Markdown编辑器',\n  editor_page: '富文本编辑器',\n  icons_page: '自定义图标',\n  img_cropper_page: '图片编辑器',\n  update: '上传数据',\n  join_page: 'QQ群',\n  doc: '文档',\n  update_table_page: '上传CSV文件',\n  update_paste_page: '粘贴表格数据',\n  multilevel: '多级菜单',\n  directive_page: '指令',\n  level_1: 'Level-1',\n  level_2: 'Level-2',\n  level_2_1: 'Level-2-1',\n  level_2_3: 'Level-2-3',\n  level_2_2: 'Level-2-2',\n  level_2_2_1: 'Level-2-2-1',\n  level_2_2_2: 'Level-2-2-2',\n  excel: 'Excel',\n  'upload-excel': '上传excel',\n  'export-excel': '导出excel',\n  tools_methods_page: '工具函数',\n  drag_list_page: '拖拽列表',\n  i18n_page: '多语言',\n  modalTitle: '模态框题目',\n  content: '这是模态框内容',\n  buttonText: '显示模态框',\n  'i18n-tip': '注：仅此页做了多语言，其他页面没有在多语言包中添加语言内容',\n  error_store_page: '错误收集',\n  error_logger_page: '错误日志',\n  query: '带参路由',\n  params: '动态路由',\n  cropper_page: '图片裁剪',\n  message_page: '消息中心',\n  tree_table_page: '树状表格',\n  org_tree_page: '组织结构树',\n  drag_drawer_page: '可拖动抽屉',\n  tree_select_page: '树状下拉选择器'\n}\n"
  },
  {
    "path": "src/locale/lang/zh-TW.js",
    "content": "export default {\n  home: '首頁',\n  login: '登錄',\n  components: '组件',\n  count_to_page: '数字渐变',\n  tables_page: '多功能表格',\n  split_pane_page: '分割窗口',\n  markdown_page: 'Markdown編輯器',\n  editor_page: '富文本編輯器',\n  icons_page: '自定義圖標',\n  img_cropper_page: '圖片編輯器',\n  update: '上傳數據',\n  join_page: 'QQ群',\n  doc: '文檔',\n  update_table_page: '上傳CSV文件',\n  update_paste_page: '粘貼表格數據',\n  multilevel: '多级菜单',\n  directive_page: '指令',\n  level_1: 'Level-1',\n  level_2: 'Level-2',\n  level_2_1: 'Level-2-1',\n  level_2_3: 'Level-2-3',\n  level_2_2: 'Level-2-2',\n  level_2_2_1: 'Level-2-2-1',\n  level_2_2_2: 'Level-2-2-2',\n  excel: 'Excel',\n  'upload-excel': '上傳excel',\n  'export-excel': '導出excel',\n  tools_methods_page: '工具函數',\n  drag_list_page: '拖拽列表',\n  i18n_page: '多語言',\n  modalTitle: '模態框題目',\n  content: '這是模態框內容',\n  buttonText: '顯示模態框',\n  'i18n-tip': '注：僅此頁做了多語言，其他頁面沒有在多語言包中添加語言內容',\n  error_store_page: '錯誤收集',\n  error_logger_page: '錯誤日誌',\n  query: '帶參路由',\n  params: '動態路由',\n  cropper_page: '圖片裁剪',\n  message_page: '消息中心',\n  tree_table_page: '樹狀表格',\n  org_tree_page: '組織結構樹',\n  drag_drawer_page: '可拖動抽屜',\n  tree_select_page: '樹狀下拉選擇器'\n}\n"
  },
  {
    "path": "src/main.js",
    "content": "// The Vue build version to load with the `import` command\n// (runtime-only or standalone) has been set in webpack.base.conf with an alias.\nimport Vue from 'vue'\nimport App from './App'\nimport router from './router'\nimport store from './store'\nimport iView from 'iview'\nimport i18n from '@/locale'\nimport config from '@/config'\nimport importDirective from '@/directive'\nimport { directive as clickOutside } from 'v-click-outside-x'\nimport installPlugin from '@/plugin'\nimport './index.less'\nimport '@/assets/icons/iconfont.css'\nimport TreeTable from 'tree-table-vue'\nimport VOrgTree from 'v-org-tree'\nimport 'v-org-tree/dist/v-org-tree.css'\n// 实际打包时应该不引入mock\n/* eslint-disable */\nif (process.env.NODE_ENV !== 'production') require('@/mock')\n\nVue.use(iView, {\n  i18n: (key, value) => i18n.t(key, value)\n})\nVue.use(TreeTable)\nVue.use(VOrgTree)\n/**\n * @description 注册admin内置插件\n */\ninstallPlugin(Vue)\n/**\n * @description 生产环境关掉提示\n */\nVue.config.productionTip = false\n/**\n * @description 全局注册应用配置\n */\nVue.prototype.$config = config\n/**\n * 注册指令\n */\nimportDirective(Vue)\nVue.directive('clickOutside', clickOutside)\n\n/* eslint-disable no-new */\nnew Vue({\n  el: '#app',\n  router,\n  i18n,\n  store,\n  render: h => h(App)\n})\n"
  },
  {
    "path": "src/mock/data/org-data.js",
    "content": "export default {\n  id: 0,\n  label: 'XXX科技有限公司',\n  children: [\n    {\n      id: 2,\n      label: '产品研发部',\n      children: [\n        {\n          id: 5,\n          label: '研发-前端'\n        }, {\n          id: 6,\n          label: '研发-后端'\n        }, {\n          id: 9,\n          label: 'UI设计'\n        }, {\n          id: 10,\n          label: '产品经理'\n        }\n      ]\n    },\n    {\n      id: 3,\n      label: '销售部',\n      children: [\n        {\n          id: 7,\n          label: '销售一部'\n        }, {\n          id: 8,\n          label: '销售二部'\n        }\n      ]\n    },\n    {\n      id: 4,\n      label: '财务部'\n    }, {\n      id: 11,\n      label: 'HR人事'\n    }\n  ]\n}\n"
  },
  {
    "path": "src/mock/data/tree-select.js",
    "content": "export const treeData = [\n  {\n    id: 1,\n    title: '1',\n    children: [\n      {\n        id: 11,\n        title: '1-1',\n        loading: false,\n        children: [\n          // {\n          //   id: 111,\n          //   title: '1-1-1'\n          // },\n          // {\n          //   id: 112,\n          //   title: '1-1-2'\n          // },\n          // {\n          //   id: 113,\n          //   title: '1-1-3'\n          // },\n          // {\n          //   id: 114,\n          //   title: '1-1-4'\n          // }\n        ]\n      },\n      {\n        id: 12,\n        title: '1-2',\n        children: [\n          {\n            id: 121,\n            title: '1-2-1'\n          }\n        ]\n      }\n    ]\n  }\n]\n\nexport const newTreeData = [\n  {\n    id: 'a',\n    title: 'a',\n    children: [\n      {\n        id: 'a1',\n        title: 'a-1',\n        children: [\n          {\n            id: 112,\n            title: '1-1-2'\n          },\n          {\n            id: 'a12',\n            title: 'a-1-2'\n          },\n          {\n            id: 'a13',\n            title: 'a-1-3'\n          },\n          {\n            id: 'a14',\n            title: 'a-1-4'\n          }\n        ]\n      },\n      {\n        id: 'a2',\n        title: 'a-2',\n        children: [\n          {\n            id: 'a21',\n            title: 'b-2-1'\n          }\n        ]\n      }\n    ]\n  }\n]\n"
  },
  {
    "path": "src/mock/data.js",
    "content": "import Mock from 'mockjs'\nimport { doCustomTimes } from '@/libs/util'\nimport orgData from './data/org-data'\nimport { treeData } from './data/tree-select'\nconst Random = Mock.Random\n\nexport const getTableData = req => {\n  let tableData = []\n  doCustomTimes(5, () => {\n    tableData.push(Mock.mock({\n      name: '@name',\n      email: '@email',\n      createTime: '@date'\n    }))\n  })\n  return tableData\n}\n\nexport const getDragList = req => {\n  let dragList = []\n  doCustomTimes(5, () => {\n    dragList.push(Mock.mock({\n      name: Random.csentence(10, 13),\n      id: Random.increment(10)\n    }))\n  })\n  return dragList\n}\n\nexport const uploadImage = req => {\n  return Promise.resolve()\n}\n\nexport const getOrgData = req => {\n  return orgData\n}\n\nexport const getTreeSelectData = req => {\n  return treeData\n}\n"
  },
  {
    "path": "src/mock/index.js",
    "content": "import Mock from 'mockjs'\nimport { login, logout, getUserInfo } from './login'\nimport { getTableData, getDragList, uploadImage, getOrgData, getTreeSelectData } from './data'\nimport { getMessageInit, getContentByMsgId, hasRead, removeReaded, restoreTrash, messageCount } from './user'\n\n// 配置Ajax请求延时，可用来测试网络延迟大时项目中一些效果\nMock.setup({\n  timeout: 1000\n})\n\n// 登录相关和获取用户信息\nMock.mock(/\\/login/, login)\nMock.mock(/\\/get_info/, getUserInfo)\nMock.mock(/\\/logout/, logout)\nMock.mock(/\\/get_table_data/, getTableData)\nMock.mock(/\\/get_drag_list/, getDragList)\nMock.mock(/\\/save_error_logger/, 'success')\nMock.mock(/\\/image\\/upload/, uploadImage)\nMock.mock(/\\/message\\/init/, getMessageInit)\nMock.mock(/\\/message\\/content/, getContentByMsgId)\nMock.mock(/\\/message\\/has_read/, hasRead)\nMock.mock(/\\/message\\/remove_readed/, removeReaded)\nMock.mock(/\\/message\\/restore/, restoreTrash)\nMock.mock(/\\/message\\/count/, messageCount)\nMock.mock(/\\/get_org_data/, getOrgData)\nMock.mock(/\\/get_tree_select_data/, getTreeSelectData)\n\nexport default Mock\n"
  },
  {
    "path": "src/mock/login.js",
    "content": "import { getParams } from '@/libs/util'\nconst USER_MAP = {\n  super_admin: {\n    name: 'super_admin',\n    user_id: '1',\n    access: ['super_admin', 'admin'],\n    token: 'super_admin',\n    avatar: 'https://file.iviewui.com/dist/a0e88e83800f138b94d2414621bd9704.png'\n  },\n  admin: {\n    name: 'admin',\n    user_id: '2',\n    access: ['admin'],\n    token: 'admin',\n    avatar: 'https://avatars0.githubusercontent.com/u/20942571?s=460&v=4'\n  }\n}\n\nexport const login = req => {\n  req = JSON.parse(req.body)\n  return { token: USER_MAP[req.userName].token }\n}\n\nexport const getUserInfo = req => {\n  const params = getParams(req.url)\n  return USER_MAP[params.token]\n}\n\nexport const logout = req => {\n  return null\n}\n"
  },
  {
    "path": "src/mock/user.js",
    "content": "import Mock from 'mockjs'\nimport { doCustomTimes } from '@/libs/util'\nconst Random = Mock.Random\n\nexport const getMessageInit = () => {\n  let unreadList = []\n  doCustomTimes(3, () => {\n    unreadList.push(Mock.mock({\n      title: Random.cword(10, 15),\n      create_time: '@date',\n      msg_id: Random.increment(100)\n    }))\n  })\n  let readedList = []\n  doCustomTimes(4, () => {\n    readedList.push(Mock.mock({\n      title: Random.cword(10, 15),\n      create_time: '@date',\n      msg_id: Random.increment(100)\n    }))\n  })\n  let trashList = []\n  doCustomTimes(2, () => {\n    trashList.push(Mock.mock({\n      title: Random.cword(10, 15),\n      create_time: '@date',\n      msg_id: Random.increment(100)\n    }))\n  })\n  return {\n    unread: unreadList,\n    readed: readedList,\n    trash: trashList\n  }\n}\n\nexport const getContentByMsgId = () => {\n  return `<divcourier new',=\"\" monospace;font-weight:=\"\" normal;font-size:=\"\" 12px;line-height:=\"\" 18px;white-space:=\"\" pre;\"=\"\"><div>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style=\"font-size: medium;\">这是消息内容，这个内容是使用<span style=\"color: rgb(255, 255, 255); background-color: rgb(28, 72, 127);\">富文本编辑器</span>编辑的，所以你可以看到一些<span style=\"text-decoration-line: underline; font-style: italic; color: rgb(194, 79, 74);\">格式</span></span></div><ol><li>你可以查看Mock返回的数据格式，和api请求的接口，来确定你的后端接口的开发</li><li>使用你的真实接口后，前端页面基本不需要修改即可满足基本需求</li><li>快来试试吧</li></ol><p>${Random.csentence(100, 200)}</p></divcourier>`\n}\n\nexport const hasRead = () => {\n  return true\n}\n\nexport const removeReaded = () => {\n  return true\n}\n\nexport const restoreTrash = () => {\n  return true\n}\n\nexport const messageCount = () => {\n  return 3\n}\n"
  },
  {
    "path": "src/plugin/error-store/index.js",
    "content": "import store from '@/store'\nexport default {\n  install (Vue, options) {\n    if (options.developmentOff && process.env.NODE_ENV === 'development') return\n    Vue.config.errorHandler = (error, vm, mes) => {\n      let info = {\n        type: 'script',\n        code: 0,\n        mes: error.message,\n        url: window.location.href\n      }\n      Vue.nextTick(() => {\n        store.dispatch('addErrorLog', info)\n      })\n    }\n  }\n}\n"
  },
  {
    "path": "src/plugin/index.js",
    "content": "import config from '@/config'\nconst { plugin } = config\n\nexport default (Vue) => {\n  for (let name in plugin) {\n    const value = plugin[name]\n    Vue.use(require(`./${name}`).default, typeof value === 'object' ? value : undefined)\n  }\n}\n"
  },
  {
    "path": "src/router/before-close.js",
    "content": "import { Modal } from 'iview'\n\nconst beforeClose = {\n  before_close_normal: (resolve) => {\n    Modal.confirm({\n      title: '确定要关闭这一页吗',\n      onOk: () => {\n        resolve(true)\n      },\n      onCancel: () => {\n        resolve(false)\n      }\n    })\n  }\n}\n\nexport default beforeClose\n"
  },
  {
    "path": "src/router/index.js",
    "content": "import Vue from 'vue'\nimport Router from 'vue-router'\nimport routes from './routers'\nimport store from '@/store'\nimport iView from 'iview'\nimport { setToken, getToken, canTurnTo, setTitle } from '@/libs/util'\nimport config from '@/config'\nconst { homeName } = config\n\nVue.use(Router)\nconst router = new Router({\n  routes,\n  mode: 'history'\n})\nconst LOGIN_PAGE_NAME = 'login'\n\nconst turnTo = (to, access, next) => {\n  if (canTurnTo(to.name, access, routes)) next() // 有权限，可访问\n  else next({ replace: true, name: 'error_401' }) // 无权限，重定向到401页面\n}\n\nrouter.beforeEach((to, from, next) => {\n  iView.LoadingBar.start()\n  const token = getToken()\n  if (!token && to.name !== LOGIN_PAGE_NAME) {\n    // 未登录且要跳转的页面不是登录页\n    next({\n      name: LOGIN_PAGE_NAME // 跳转到登录页\n    })\n  } else if (!token && to.name === LOGIN_PAGE_NAME) {\n    // 未登陆且要跳转的页面是登录页\n    next() // 跳转\n  } else if (token && to.name === LOGIN_PAGE_NAME) {\n    // 已登录且要跳转的页面是登录页\n    next({\n      name: homeName // 跳转到homeName页\n    })\n  } else {\n    if (store.state.user.hasGetInfo) {\n      turnTo(to, store.state.user.access, next)\n    } else {\n      store.dispatch('getUserInfo').then(user => {\n        // 拉取用户信息，通过用户权限和跳转的页面的name来判断是否有权限访问;access必须是一个数组，如：['super_admin'] ['super_admin', 'admin']\n        turnTo(to, user.access, next)\n      }).catch(() => {\n        setToken('')\n        next({\n          name: 'login'\n        })\n      })\n    }\n  }\n})\n\nrouter.afterEach(to => {\n  setTitle(to, router.app)\n  iView.LoadingBar.finish()\n  window.scrollTo(0, 0)\n})\n\nexport default router\n"
  },
  {
    "path": "src/router/routers.js",
    "content": "import Main from '@/components/main'\nimport parentView from '@/components/parent-view'\n\n/**\n * iview-admin中meta除了原生参数外可配置的参数:\n * meta: {\n *  title: { String|Number|Function }\n *         显示在侧边栏、面包屑和标签栏的文字\n *         使用'{{ 多语言字段 }}'形式结合多语言使用，例子看多语言的路由配置;\n *         可以传入一个回调函数，参数是当前路由对象，例子看动态路由和带参路由\n *  hideInBread: (false) 设为true后此级路由将不会出现在面包屑中，示例看QQ群路由配置\n *  hideInMenu: (false) 设为true后在左侧菜单不会显示该页面选项\n *  notCache: (false) 设为true后页面在切换标签后不会缓存，如果需要缓存，无需设置这个字段，而且需要设置页面组件name属性和路由配置的name一致\n *  access: (null) 可访问该页面的权限数组，当前路由设置的权限会影响子路由\n *  icon: (-) 该页面在左侧菜单、面包屑和标签导航处显示的图标，如果是自定义图标，需要在图标名称前加下划线'_'\n *  beforeCloseName: (-) 设置该字段，则在关闭当前tab页时会去'@/router/before-close.js'里寻找该字段名对应的方法，作为关闭前的钩子函数\n * }\n */\n\nexport default [\n  {\n    path: '/login',\n    name: 'login',\n    meta: {\n      title: 'Login - 登录',\n      hideInMenu: true\n    },\n    component: () => import('@/view/login/login.vue')\n  },\n  {\n    path: '/',\n    name: '_home',\n    redirect: '/home',\n    component: Main,\n    meta: {\n      hideInMenu: true,\n      notCache: true\n    },\n    children: [\n      {\n        path: '/home',\n        name: 'home',\n        meta: {\n          hideInMenu: true,\n          title: '首页',\n          notCache: true,\n          icon: 'md-home'\n        },\n        component: () => import('@/view/single-page/home')\n      }\n    ]\n  },\n  {\n    path: '',\n    name: 'doc',\n    meta: {\n      title: '文档',\n      href: 'https://lison16.github.io/iview-admin-doc/#/',\n      icon: 'ios-book'\n    }\n  },\n  {\n    path: '/join',\n    name: 'join',\n    component: Main,\n    meta: {\n      hideInBread: true\n    },\n    children: [\n      {\n        path: 'join_page',\n        name: 'join_page',\n        meta: {\n          icon: '_qq',\n          title: 'QQ群'\n        },\n        component: () => import('@/view/join-page.vue')\n      }\n    ]\n  },\n  {\n    path: '/message',\n    name: 'message',\n    component: Main,\n    meta: {\n      hideInBread: true,\n      hideInMenu: true\n    },\n    children: [\n      {\n        path: 'message_page',\n        name: 'message_page',\n        meta: {\n          icon: 'md-notifications',\n          title: '消息中心'\n        },\n        component: () => import('@/view/single-page/message/index.vue')\n      }\n    ]\n  },\n  {\n    path: '/components',\n    name: 'components',\n    meta: {\n      icon: 'logo-buffer',\n      title: '组件'\n    },\n    component: Main,\n    children: [\n      {\n        path: 'tree_select_page',\n        name: 'tree_select_page',\n        meta: {\n          icon: 'md-arrow-dropdown-circle',\n          title: '树状下拉选择器'\n        },\n        component: () => import('@/view/components/tree-select/index.vue')\n      },\n      {\n        path: 'count_to_page',\n        name: 'count_to_page',\n        meta: {\n          icon: 'md-trending-up',\n          title: '数字渐变'\n        },\n        component: () => import('@/view/components/count-to/count-to.vue')\n      },\n      {\n        path: 'drag_list_page',\n        name: 'drag_list_page',\n        meta: {\n          icon: 'ios-infinite',\n          title: '拖拽列表'\n        },\n        component: () => import('@/view/components/drag-list/drag-list.vue')\n      },\n      {\n        path: 'drag_drawer_page',\n        name: 'drag_drawer_page',\n        meta: {\n          icon: 'md-list',\n          title: '可拖拽抽屉'\n        },\n        component: () => import('@/view/components/drag-drawer')\n      },\n      {\n        path: 'org_tree_page',\n        name: 'org_tree_page',\n        meta: {\n          icon: 'ios-people',\n          title: '组织结构树'\n        },\n        component: () => import('@/view/components/org-tree')\n      },\n      {\n        path: 'tree_table_page',\n        name: 'tree_table_page',\n        meta: {\n          icon: 'md-git-branch',\n          title: '树状表格'\n        },\n        component: () => import('@/view/components/tree-table/index.vue')\n      },\n      {\n        path: 'cropper_page',\n        name: 'cropper_page',\n        meta: {\n          icon: 'md-crop',\n          title: '图片裁剪'\n        },\n        component: () => import('@/view/components/cropper/cropper.vue')\n      },\n      {\n        path: 'tables_page',\n        name: 'tables_page',\n        meta: {\n          icon: 'md-grid',\n          title: '多功能表格'\n        },\n        component: () => import('@/view/components/tables/tables.vue')\n      },\n      {\n        path: 'split_pane_page',\n        name: 'split_pane_page',\n        meta: {\n          icon: 'md-pause',\n          title: '分割窗口'\n        },\n        component: () => import('@/view/components/split-pane/split-pane.vue')\n      },\n      {\n        path: 'markdown_page',\n        name: 'markdown_page',\n        meta: {\n          icon: 'logo-markdown',\n          title: 'Markdown编辑器'\n        },\n        component: () => import('@/view/components/markdown/markdown.vue')\n      },\n      {\n        path: 'editor_page',\n        name: 'editor_page',\n        meta: {\n          icon: 'ios-create',\n          title: '富文本编辑器'\n        },\n        component: () => import('@/view/components/editor/editor.vue')\n      },\n      {\n        path: 'icons_page',\n        name: 'icons_page',\n        meta: {\n          icon: '_bear',\n          title: '自定义图标'\n        },\n        component: () => import('@/view/components/icons/icons.vue')\n      }\n    ]\n  },\n  {\n    path: '/update',\n    name: 'update',\n    meta: {\n      icon: 'md-cloud-upload',\n      title: '数据上传'\n    },\n    component: Main,\n    children: [\n      {\n        path: 'update_table_page',\n        name: 'update_table_page',\n        meta: {\n          icon: 'ios-document',\n          title: '上传Csv'\n        },\n        component: () => import('@/view/update/update-table.vue')\n      },\n      {\n        path: 'update_paste_page',\n        name: 'update_paste_page',\n        meta: {\n          icon: 'md-clipboard',\n          title: '粘贴表格数据'\n        },\n        component: () => import('@/view/update/update-paste.vue')\n      }\n    ]\n  },\n  {\n    path: '/excel',\n    name: 'excel',\n    meta: {\n      icon: 'ios-stats',\n      title: 'EXCEL导入导出'\n    },\n    component: Main,\n    children: [\n      {\n        path: 'upload-excel',\n        name: 'upload-excel',\n        meta: {\n          icon: 'md-add',\n          title: '导入EXCEL'\n        },\n        component: () => import('@/view/excel/upload-excel.vue')\n      },\n      {\n        path: 'export-excel',\n        name: 'export-excel',\n        meta: {\n          icon: 'md-download',\n          title: '导出EXCEL'\n        },\n        component: () => import('@/view/excel/export-excel.vue')\n      }\n    ]\n  },\n  {\n    path: '/tools_methods',\n    name: 'tools_methods',\n    meta: {\n      hideInBread: true\n    },\n    component: Main,\n    children: [\n      {\n        path: 'tools_methods_page',\n        name: 'tools_methods_page',\n        meta: {\n          icon: 'ios-hammer',\n          title: '工具方法',\n          beforeCloseName: 'before_close_normal'\n        },\n        component: () => import('@/view/tools-methods/tools-methods.vue')\n      }\n    ]\n  },\n  {\n    path: '/i18n',\n    name: 'i18n',\n    meta: {\n      hideInBread: true\n    },\n    component: Main,\n    children: [\n      {\n        path: 'i18n_page',\n        name: 'i18n_page',\n        meta: {\n          icon: 'md-planet',\n          title: 'i18n - {{ i18n_page }}'\n        },\n        component: () => import('@/view/i18n/i18n-page.vue')\n      }\n    ]\n  },\n  {\n    path: '/error_store',\n    name: 'error_store',\n    meta: {\n      hideInBread: true\n    },\n    component: Main,\n    children: [\n      {\n        path: 'error_store_page',\n        name: 'error_store_page',\n        meta: {\n          icon: 'ios-bug',\n          title: '错误收集'\n        },\n        component: () => import('@/view/error-store/error-store.vue')\n      }\n    ]\n  },\n  {\n    path: '/error_logger',\n    name: 'error_logger',\n    meta: {\n      hideInBread: true,\n      hideInMenu: true\n    },\n    component: Main,\n    children: [\n      {\n        path: 'error_logger_page',\n        name: 'error_logger_page',\n        meta: {\n          icon: 'ios-bug',\n          title: '错误收集'\n        },\n        component: () => import('@/view/single-page/error-logger.vue')\n      }\n    ]\n  },\n  {\n    path: '/directive',\n    name: 'directive',\n    meta: {\n      hideInBread: true\n    },\n    component: Main,\n    children: [\n      {\n        path: 'directive_page',\n        name: 'directive_page',\n        meta: {\n          icon: 'ios-navigate',\n          title: '指令'\n        },\n        component: () => import('@/view/directive/directive.vue')\n      }\n    ]\n  },\n  {\n    path: '/multilevel',\n    name: 'multilevel',\n    meta: {\n      icon: 'md-menu',\n      title: '多级菜单'\n    },\n    component: Main,\n    children: [\n      {\n        path: 'level_2_1',\n        name: 'level_2_1',\n        meta: {\n          icon: 'md-funnel',\n          title: '二级-1'\n        },\n        component: () => import('@/view/multilevel/level-2-1.vue')\n      },\n      {\n        path: 'level_2_2',\n        name: 'level_2_2',\n        meta: {\n          access: ['super_admin'],\n          icon: 'md-funnel',\n          showAlways: true,\n          title: '二级-2'\n        },\n        component: parentView,\n        children: [\n          {\n            path: 'level_2_2_1',\n            name: 'level_2_2_1',\n            meta: {\n              icon: 'md-funnel',\n              title: '三级'\n            },\n            component: () => import('@/view/multilevel/level-2-2/level-2-2-1.vue')\n          },\n          {\n            path: 'level_2_2_2',\n            name: 'level_2_2_2',\n            meta: {\n              icon: 'md-funnel',\n              title: '三级'\n            },\n            component: () => import('@/view/multilevel/level-2-2/level-2-2-2.vue')\n          }\n        ]\n      },\n      {\n        path: 'level_2_3',\n        name: 'level_2_3',\n        meta: {\n          icon: 'md-funnel',\n          title: '二级-3'\n        },\n        component: () => import('@/view/multilevel/level-2-3.vue')\n      }\n    ]\n  },\n  {\n    path: '/argu',\n    name: 'argu',\n    meta: {\n      hideInMenu: true\n    },\n    component: Main,\n    children: [\n      {\n        path: 'params/:id',\n        name: 'params',\n        meta: {\n          icon: 'md-flower',\n          title: route => `{{ params }}-${route.params.id}`,\n          notCache: true,\n          beforeCloseName: 'before_close_normal'\n        },\n        component: () => import('@/view/argu-page/params.vue')\n      },\n      {\n        path: 'query',\n        name: 'query',\n        meta: {\n          icon: 'md-flower',\n          title: route => `{{ query }}-${route.query.id}`,\n          notCache: true\n        },\n        component: () => import('@/view/argu-page/query.vue')\n      }\n    ]\n  },\n  {\n    path: '/401',\n    name: 'error_401',\n    meta: {\n      hideInMenu: true\n    },\n    component: () => import('@/view/error-page/401.vue')\n  },\n  {\n    path: '/500',\n    name: 'error_500',\n    meta: {\n      hideInMenu: true\n    },\n    component: () => import('@/view/error-page/500.vue')\n  },\n  {\n    path: '*',\n    name: 'error_404',\n    meta: {\n      hideInMenu: true\n    },\n    component: () => import('@/view/error-page/404.vue')\n  }\n]\n"
  },
  {
    "path": "src/store/index.js",
    "content": "import Vue from 'vue'\nimport Vuex from 'vuex'\n\nimport user from './module/user'\nimport app from './module/app'\n\nVue.use(Vuex)\n\nexport default new Vuex.Store({\n  state: {\n    //\n  },\n  mutations: {\n    //\n  },\n  actions: {\n    //\n  },\n  modules: {\n    user,\n    app\n  }\n})\n"
  },
  {
    "path": "src/store/module/app.js",
    "content": "import {\n  getBreadCrumbList,\n  setTagNavListInLocalstorage,\n  getMenuByRouter,\n  getTagNavListFromLocalstorage,\n  getHomeRoute,\n  getNextRoute,\n  routeHasExist,\n  routeEqual,\n  getRouteTitleHandled,\n  localSave,\n  localRead\n} from '@/libs/util'\nimport { saveErrorLogger } from '@/api/data'\nimport router from '@/router'\nimport routers from '@/router/routers'\nimport config from '@/config'\nconst { homeName } = config\n\nconst closePage = (state, route) => {\n  const nextRoute = getNextRoute(state.tagNavList, route)\n  state.tagNavList = state.tagNavList.filter(item => {\n    return !routeEqual(item, route)\n  })\n  router.push(nextRoute)\n}\n\nexport default {\n  state: {\n    breadCrumbList: [],\n    tagNavList: [],\n    homeRoute: {},\n    local: localRead('local'),\n    errorList: [],\n    hasReadErrorPage: false\n  },\n  getters: {\n    menuList: (state, getters, rootState) => getMenuByRouter(routers, rootState.user.access),\n    errorCount: state => state.errorList.length\n  },\n  mutations: {\n    setBreadCrumb (state, route) {\n      state.breadCrumbList = getBreadCrumbList(route, state.homeRoute)\n    },\n    setHomeRoute (state, routes) {\n      state.homeRoute = getHomeRoute(routes, homeName)\n    },\n    setTagNavList (state, list) {\n      let tagList = []\n      if (list) {\n        tagList = [...list]\n      } else tagList = getTagNavListFromLocalstorage() || []\n      if (tagList[0] && tagList[0].name !== homeName) tagList.shift()\n      let homeTagIndex = tagList.findIndex(item => item.name === homeName)\n      if (homeTagIndex > 0) {\n        let homeTag = tagList.splice(homeTagIndex, 1)[0]\n        tagList.unshift(homeTag)\n      }\n      state.tagNavList = tagList\n      setTagNavListInLocalstorage([...tagList])\n    },\n    closeTag (state, route) {\n      let tag = state.tagNavList.filter(item => routeEqual(item, route))\n      route = tag[0] ? tag[0] : null\n      if (!route) return\n      closePage(state, route)\n    },\n    addTag (state, { route, type = 'unshift' }) {\n      let router = getRouteTitleHandled(route)\n      if (!routeHasExist(state.tagNavList, router)) {\n        if (type === 'push') state.tagNavList.push(router)\n        else {\n          if (router.name === homeName) state.tagNavList.unshift(router)\n          else state.tagNavList.splice(1, 0, router)\n        }\n        setTagNavListInLocalstorage([...state.tagNavList])\n      }\n    },\n    setLocal (state, lang) {\n      localSave('local', lang)\n      state.local = lang\n    },\n    addError (state, error) {\n      state.errorList.push(error)\n    },\n    setHasReadErrorLoggerStatus (state, status = true) {\n      state.hasReadErrorPage = status\n    }\n  },\n  actions: {\n    addErrorLog ({ commit, rootState }, info) {\n      if (!window.location.href.includes('error_logger_page')) commit('setHasReadErrorLoggerStatus', false)\n      const { user: { token, userId, userName } } = rootState\n      let data = {\n        ...info,\n        time: Date.parse(new Date()),\n        token,\n        userId,\n        userName\n      }\n      saveErrorLogger(info).then(() => {\n        commit('addError', data)\n      })\n    }\n  }\n}\n"
  },
  {
    "path": "src/store/module/user.js",
    "content": "import {\n  login,\n  logout,\n  getUserInfo,\n  getMessage,\n  getContentByMsgId,\n  hasRead,\n  removeReaded,\n  restoreTrash,\n  getUnreadCount\n} from '@/api/user'\nimport { setToken, getToken } from '@/libs/util'\n\nexport default {\n  state: {\n    userName: '',\n    userId: '',\n    avatarImgPath: '',\n    token: getToken(),\n    access: '',\n    hasGetInfo: false,\n    unreadCount: 0,\n    messageUnreadList: [],\n    messageReadedList: [],\n    messageTrashList: [],\n    messageContentStore: {}\n  },\n  mutations: {\n    setAvatar (state, avatarPath) {\n      state.avatarImgPath = avatarPath\n    },\n    setUserId (state, id) {\n      state.userId = id\n    },\n    setUserName (state, name) {\n      state.userName = name\n    },\n    setAccess (state, access) {\n      state.access = access\n    },\n    setToken (state, token) {\n      state.token = token\n      setToken(token)\n    },\n    setHasGetInfo (state, status) {\n      state.hasGetInfo = status\n    },\n    setMessageCount (state, count) {\n      state.unreadCount = count\n    },\n    setMessageUnreadList (state, list) {\n      state.messageUnreadList = list\n    },\n    setMessageReadedList (state, list) {\n      state.messageReadedList = list\n    },\n    setMessageTrashList (state, list) {\n      state.messageTrashList = list\n    },\n    updateMessageContentStore (state, { msg_id, content }) {\n      state.messageContentStore[msg_id] = content\n    },\n    moveMsg (state, { from, to, msg_id }) {\n      const index = state[from].findIndex(_ => _.msg_id === msg_id)\n      const msgItem = state[from].splice(index, 1)[0]\n      msgItem.loading = false\n      state[to].unshift(msgItem)\n    }\n  },\n  getters: {\n    messageUnreadCount: state => state.messageUnreadList.length,\n    messageReadedCount: state => state.messageReadedList.length,\n    messageTrashCount: state => state.messageTrashList.length\n  },\n  actions: {\n    // 登录\n    handleLogin ({ commit }, { userName, password }) {\n      userName = userName.trim()\n      return new Promise((resolve, reject) => {\n        login({\n          userName,\n          password\n        }).then(res => {\n          const data = res.data\n          commit('setToken', data.token)\n          resolve()\n        }).catch(err => {\n          reject(err)\n        })\n      })\n    },\n    // 退出登录\n    handleLogOut ({ state, commit }) {\n      return new Promise((resolve, reject) => {\n        logout(state.token).then(() => {\n          commit('setToken', '')\n          commit('setAccess', [])\n          resolve()\n        }).catch(err => {\n          reject(err)\n        })\n        // 如果你的退出登录无需请求接口，则可以直接使用下面三行代码而无需使用logout调用接口\n        // commit('setToken', '')\n        // commit('setAccess', [])\n        // resolve()\n      })\n    },\n    // 获取用户相关信息\n    getUserInfo ({ state, commit }) {\n      return new Promise((resolve, reject) => {\n        try {\n          getUserInfo(state.token).then(res => {\n            const data = res.data\n            commit('setAvatar', data.avatar)\n            commit('setUserName', data.name)\n            commit('setUserId', data.user_id)\n            commit('setAccess', data.access)\n            commit('setHasGetInfo', true)\n            resolve(data)\n          }).catch(err => {\n            reject(err)\n          })\n        } catch (error) {\n          reject(error)\n        }\n      })\n    },\n    // 此方法用来获取未读消息条数，接口只返回数值，不返回消息列表\n    getUnreadMessageCount ({ state, commit }) {\n      getUnreadCount().then(res => {\n        const { data } = res\n        commit('setMessageCount', data)\n      })\n    },\n    // 获取消息列表，其中包含未读、已读、回收站三个列表\n    getMessageList ({ state, commit }) {\n      return new Promise((resolve, reject) => {\n        getMessage().then(res => {\n          const { unread, readed, trash } = res.data\n          commit('setMessageUnreadList', unread.sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))\n          commit('setMessageReadedList', readed.map(_ => {\n            _.loading = false\n            return _\n          }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))\n          commit('setMessageTrashList', trash.map(_ => {\n            _.loading = false\n            return _\n          }).sort((a, b) => new Date(b.create_time) - new Date(a.create_time)))\n          resolve()\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    },\n    // 根据当前点击的消息的id获取内容\n    getContentByMsgId ({ state, commit }, { msg_id }) {\n      return new Promise((resolve, reject) => {\n        let contentItem = state.messageContentStore[msg_id]\n        if (contentItem) {\n          resolve(contentItem)\n        } else {\n          getContentByMsgId(msg_id).then(res => {\n            const content = res.data\n            commit('updateMessageContentStore', { msg_id, content })\n            resolve(content)\n          })\n        }\n      })\n    },\n    // 把一个未读消息标记为已读\n    hasRead ({ state, commit }, { msg_id }) {\n      return new Promise((resolve, reject) => {\n        hasRead(msg_id).then(() => {\n          commit('moveMsg', {\n            from: 'messageUnreadList',\n            to: 'messageReadedList',\n            msg_id\n          })\n          commit('setMessageCount', state.unreadCount - 1)\n          resolve()\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    },\n    // 删除一个已读消息到回收站\n    removeReaded ({ commit }, { msg_id }) {\n      return new Promise((resolve, reject) => {\n        removeReaded(msg_id).then(() => {\n          commit('moveMsg', {\n            from: 'messageReadedList',\n            to: 'messageTrashList',\n            msg_id\n          })\n          resolve()\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    },\n    // 还原一个已删除消息到已读消息\n    restoreTrash ({ commit }, { msg_id }) {\n      return new Promise((resolve, reject) => {\n        restoreTrash(msg_id).then(() => {\n          commit('moveMsg', {\n            from: 'messageTrashList',\n            to: 'messageReadedList',\n            msg_id\n          })\n          resolve()\n        }).catch(error => {\n          reject(error)\n        })\n      })\n    }\n  }\n}\n"
  },
  {
    "path": "src/view/argu-page/params.vue",
    "content": "<template>\n  <div>\n    <Card>\n      <h2>ID: {{ $route.params.id }}</h2>\n      <Button @click=\"close\">调用closeTag方法关闭本页</Button>\n    </Card>\n  </div>\n</template>\n\n<script>\nimport { mapMutations } from 'vuex'\nexport default {\n  name: 'params',\n  methods: {\n    ...mapMutations([\n      'closeTag'\n    ]),\n    close () {\n      /**\n       * 如果是调用closeTag方法，普通的页面传入的对象参数只需要写name字段即可\n       * 如果是动态路由和带参路由，需要传入query或params字段，用来区别关闭的是参数为多少的页面\n       */\n      this.closeTag({\n        name: 'params',\n        params: {\n          id: this.$route.params.id\n        }\n      })\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/argu-page/query.vue",
    "content": "<template>\n  <div>\n    <Card>\n      <h2>ID: {{ $route.query.id }}</h2>\n      <Button @click=\"close\">调用closeTag方法关闭本页</Button>\n    </Card>\n  </div>\n</template>\n\n<script>\nimport { mapMutations } from 'vuex'\nexport default {\n  name: 'query',\n  methods: {\n    ...mapMutations([\n      'closeTag'\n    ]),\n    close () {\n      /**\n       * 如果是调用closeTag方法，普通的页面传入的对象参数只需要写name字段即可\n       * 如果是动态路由和带参路由，需要传入query或params字段，用来区别关闭的是参数为多少的页面\n       */\n      this.closeTag({\n        name: 'query',\n        query: {\n          id: this.$route.query.id\n        }\n      })\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/components/count-to/count-to.vue",
    "content": "<template>\n  <div>\n    <Row :gutter=\"14\">\n      <i-col span=\"3\">\n        <Card>\n          <p slot=\"title\">\n            <Icon type=\"waterdrop\"></Icon>\n            count-to组件基础用法\n          </p>\n          <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n            <div class=\"count-to-con\">\n              <count-to :end=\"2534\"/>\n            </div>\n          </Row>\n        </Card>\n      </i-col>\n        <i-col span=\"5\" class=\"padding-left-10\">\n          <Card>\n            <p slot=\"title\">\n              <Icon type=\"code\"></Icon>\n              可添加左右文字\n            </p>\n            <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n              <div class=\"count-to-con\">\n                <count-to :end=\"2534\">\n                  <span slot=\"left\">Total:&nbsp;</span>\n                  <span slot=\"right\">&nbsp;times</span>\n                </count-to>\n              </div>\n            </Row>\n          </Card>\n        </i-col>\n        <i-col span=\"8\" class=\"padding-left-10\">\n          <Card>\n            <p slot=\"title\">\n              <Icon type=\"paintbucket\"></Icon>\n              自定义样式\n            </p>\n            <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n              <div class=\"count-to-con\">\n                <count-to :end=\"2534\" count-class=\"count-text\" unit-class=\"unit-class\">\n                  <span class=\"slot-text\" slot=\"left\">Total:&nbsp;</span>\n                  <span class=\"slot-text\" slot=\"right\">&nbsp;times</span>\n                </count-to>\n              </div>\n            </Row>\n          </Card>\n        </i-col>\n        <i-col span=\"8\" class=\"padding-left-10\">\n          <Card>\n            <p slot=\"title\">\n              <Icon type=\"settings\"></Icon>\n              设置数据格式\n            </p>\n            <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n              <div class=\"count-to-con\">\n                <count-to :end=\"2534\" count-class=\"count-text\" unit-class=\"unit-class\" :decimals=\"2\">\n                  <span class=\"slot-text\" slot=\"left\">Total:&nbsp;</span>\n                  <span class=\"slot-text\" slot=\"right\">&nbsp;times</span>\n                </count-to>\n              </div>\n            </Row>\n          </Card>\n        </i-col>\n    </Row>\n    <Row :gutter=\"14\" style=\"margin-top: 14px;\">\n      <i-col span=\"8\">\n        <Card>\n          <p slot=\"title\">\n            <Icon type=\"ios-color-wand\"></Icon>\n            转换单位简化数据\n          </p>\n          <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n            <div class=\"count-to-con\">\n              <count-to :simplify=\"true\" :end=\"2534\" count-class=\"count-text\" unit-class=\"unit-class\">\n                <span class=\"slot-text\" slot=\"left\">Total:&nbsp;</span>\n                <span class=\"slot-text\" slot=\"right\">&nbsp;times</span>\n              </count-to>\n            </div>\n          </Row>\n        </Card>\n      </i-col>\n      <i-col span=\"8\" class=\"padding-left-10\">\n        <Card>\n          <p slot=\"title\">\n            <Icon type=\"ios-shuffle-strong\"></Icon>\n            自定义单位\n          </p>\n          <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n            <div class=\"count-to-con\">\n              <count-to :simplify=\"true\" :unit=\"unit\" :end=\"253\" count-class=\"count-text\" unit-class=\"unit-class\">\n                <span class=\"slot-text\" slot=\"left\">原始数据：253&nbsp;=>&nbsp;</span>\n              </count-to>\n              <count-to :simplify=\"true\" :unit=\"unit\" :end=\"2534\" count-class=\"count-text\" unit-class=\"unit-class\">\n                <span class=\"slot-text\" slot=\"left\">原始数据：2534&nbsp;=>&nbsp;</span>\n              </count-to>\n              <count-to :simplify=\"true\" :unit=\"unit\" :end=\"257678\" count-class=\"count-text\" unit-class=\"unit-class\">\n                <span class=\"slot-text\" slot=\"left\">原始数据：257678&nbsp;=>&nbsp;</span>\n              </count-to>\n            </div>\n          </Row>\n        </Card>\n      </i-col>\n      <i-col span=\"8\" class=\"padding-left-10\">\n        <Card>\n          <p slot=\"title\">\n            <Icon type=\"android-stopwatch\"></Icon>\n            可异步更新数据\n          </p>\n          <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n            <div class=\"count-to-con\">\n              <count-to :end=\"asynEndVal\" count-class=\"count-text\" unit-class=\"unit-class\">\n                <span class=\"slot-text\" slot=\"left\">Total:&nbsp;</span>\n                <span class=\"slot-text\" slot=\"right\">&nbsp;times</span>\n              </count-to>\n            </div>\n          </Row>\n        </Card>\n      </i-col>\n    </Row>\n    <Row :gutter=\"14\" style=\"margin-top: 14px;\">\n      <i-col>\n        <Card>\n          <p slot=\"title\">\n            <Icon type=\"ios-analytics\"></Icon>\n            综合实例\n          </p>\n          <Row type=\"flex\" justify=\"center\" align=\"middle\" class=\"countto-page-row\">\n            <div class=\"count-to-con\">\n              <count-to :delay=\"500\" :simplify=\"true\" :unit=\"unit2\" :end=\"integratedEndVal\" count-class=\"count-text\" unit-class=\"unit-class\">\n                <span class=\"slot-text\" slot=\"left\">原始数据:&nbsp;{{ integratedEndVal }}&nbsp;=>&nbsp;</span>\n                <span class=\"slot-text\" slot=\"right\">&nbsp;times</span>\n              </count-to>\n            </div>\n          </Row>\n        </Card>\n      </i-col>\n    </Row>\n  </div>\n</template>\n\n<script>\nimport CountTo from '_c/count-to'\nexport default {\n  name: 'count_to_page',\n  components: {\n    CountTo\n  },\n  data () {\n    return {\n      end: 0,\n      unit: [[3, '千多'], [4, '万多'], [5, '十万多']],\n      unit2: [[1, '十多'], [2, '百多'], [3, '千多'], [4, '万多'], [5, '十万多'], [6, '百万多'], [7, '千万多'], [8, '亿多']],\n      asynEndVal: 487,\n      integratedEndVal: 3\n    }\n  },\n  methods: {\n    init () {\n      setInterval(() => {\n        this.asynEndVal += parseInt(Math.random() * 20)\n        this.integratedEndVal += parseInt(Math.random() * 30)\n      }, 2000)\n    }\n  },\n  mounted () {\n    this.init()\n  }\n}\n</script>\n\n<style lang=\"less\">\n@baseColor: ~\"#dc9387\";\n.countto-page-row{\n  height: 200px;\n}\n.count-to-con{\n  display: block;\n  width: 100%;\n  text-align: center;\n}\n.count-text{\n  font-size: 50px;\n  color: @baseColor;\n}\n.slot-text{\n  font-size: 22px;\n}\n.unit-class{\n  font-size: 30px;\n  color: @baseColor;\n}\n</style>\n"
  },
  {
    "path": "src/view/components/cropper/cropper.vue",
    "content": "<template>\n  <div>\n    <Row>\n      <i-col span=\"12\">\n        <Card>\n          <div class=\"cropper-example cropper-first\">\n            <cropper\n              :src=\"exampleImageSrc\"\n              crop-button-text=\"确认提交\"\n              @on-crop=\"handleCroped\"\n            ></cropper>\n          </div>\n        </Card>\n      </i-col>\n    </Row>\n  </div>\n</template>\n\n<script>\nimport Cropper from '@/components/cropper'\nimport { uploadImg } from '@/api/data'\nexport default {\n  name: 'cropper_page',\n  components: {\n    Cropper\n  },\n  data () {\n    return {\n      exampleImageSrc: ''\n    }\n  },\n  methods: {\n    handleCroped (blob) {\n      const formData = new FormData()\n      formData.append('croppedImg', blob)\n      uploadImg(formData).then(() => {\n        this.$Message.success('Upload success~')\n      })\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.cropper-example{\n  height: 400px;\n}\n</style>\n"
  },
  {
    "path": "src/view/components/drag-drawer/index.vue",
    "content": "<template>\n  <Card>\n    <h3 style=\"padding: 10px 0;\">drag-drawer组件是对iview的drawer组件的封装，在支持drawer所有api的基础上，支持可拖动和footer底部插槽</h3>\n    <div style=\"padding: 10px 0\">\n      <b>\n        方向\n        <i-switch v-model=\"placement\">\n          <span slot=\"open\">左</span>\n          <span slot=\"close\">右</span>\n        </i-switch>\n      </b>\n      <b>\n        是否可拖动\n        <i-switch v-model=\"draggable\"></i-switch>\n      </b>\n      <Button @click=\"showContainerBDrawer = !showContainerBDrawer\" type=\"primary\" style=\"margin-left: 10px\">{{ showContainerBDrawer ? '关闭' : '打开' }}容器内抽屉</Button>\n      <Button @click=\"showWindowBDrawer = true\" type=\"primary\" style=\"margin-left: 10px\">打开全屏抽屉</Button>\n    </div>\n    <div class=\"drag-drawer-inner-box\">\n      <drag-drawer v-model=\"showContainerBDrawer\"\n        :width.sync=\"width2\"\n        min-width=\"30px\"\n        :inner=\"true\"\n        :transfer=\"false\"\n        :placement=\"placementComputed\"\n        :draggable=\"draggable\"\n        @on-resize=\"handleResize\"\n        :scrollable=\"true\">\n        <div slot=\"header\">\n          <Icon type=\"md-aperture\" :size=\"18\"></Icon>\n          <b>这是标题</b>\n        </div>\n        <p v-for=\"n in 200\" :key=\"n\">{{ n }}</p>\n        <div slot=\"footer\">\n          <p>123123</p>\n          <p>21312</p>\n        </div>\n      </drag-drawer>\n    </div>\n    <drag-drawer v-model=\"showWindowBDrawer\"\n      :width.sync=\"width1\"\n      :min-width=\"300\"\n      :placement=\"placementComputed\"\n      :draggable=\"draggable\"\n      :scrollable=\"true\">\n      <div slot=\"header\">\n        <Icon type=\"md-aperture\" :size=\"18\"></Icon>\n        <b>这是标题</b>\n      </div>\n      <Button @click=\"showBDrawer3 = true\">显示多层</Button>\n      <p v-for=\"n in 200\" :key=\"n\">{{ n }}</p>\n    </drag-drawer>\n  </Card>\n</template>\n\n<script>\nimport DragDrawer from '_c/drag-drawer'\nexport default {\n  name: 'drag_drawer_page',\n  components: {\n    DragDrawer\n  },\n  data () {\n    return {\n      showWindowBDrawer: false,\n      showContainerBDrawer: false,\n      showBDrawer3: false,\n      width1: 300,\n      width2: 200,\n      placement: false,\n      draggable: true\n    }\n  },\n  computed: {\n    placementComputed () {\n      return this.placement ? 'left' : 'right'\n    }\n  },\n  methods: {\n    handleResize (event) {\n      const { atMin } = event\n      /* eslint-disable */\n      console.log(atMin);\n    },\n  }\n}\n</script>\n\n<style lang=\"less\">\n.drag-drawer-inner-box{\n  position: relative;\n  width: 500px;\n  height: 400px;\n  background: pink;\n  border: 1px solid pink;\n}\n</style>\n"
  },
  {
    "path": "src/view/components/drag-list/drag-list.vue",
    "content": "<template>\n  <div>\n    <Card>\n      <div class=\"drag-box-card\">\n\n        <!-- 切记设置list1和list2属性时，一定要添加.sync修饰符 -->\n        <drag-list :list1.sync=\"list1\" :list2.sync=\"list2\" :dropConClass=\"dropConClass\" @on-change=\"handleChange\">\n          <h3 slot=\"left-title\">待办事项</h3>\n          <Card class=\"drag-item\" slot=\"left\" slot-scope=\"left\">{{ left.itemLeft.name }}</Card>\n          <h3 slot=\"right-title\">完成事项</h3>\n          <Card class=\"drag-item\" slot=\"right\" slot-scope=\"right\">{{ right.itemRight.name }}</Card>\n        </drag-list>\n\n      </div>\n      <div class=\"handle-log-box\">\n        <h3>操作记录</h3>\n        <div class=\"handle-inner-box\">\n          <p v-for=\"(item, index) in handleList\" :key=\"`handle_item_${index}`\">{{ item }}</p>\n        </div>\n      </div>\n      <div class=\"res-show-box\">\n        <pre>{{ list1 }}</pre>\n      </div>\n      <div class=\"res-show-box\">\n        <pre>{{ list2 }}</pre>\n      </div>\n    </Card>\n  </div>\n</template>\n<script>\nimport DragList from '_c/drag-list'\nimport { getDragList } from '@/api/data'\nexport default {\n  name: 'drag_list_page',\n  components: {\n    DragList\n  },\n  data () {\n    return {\n      list1: [],\n      list2: [],\n      dropConClass: {\n        left: ['drop-box', 'left-drop-box'],\n        right: ['drop-box', 'right-drop-box']\n      },\n      handleList: []\n    }\n  },\n  methods: {\n    handleChange ({ src, target, oldIndex, newIndex }) {\n      this.handleList.push(`${src} => ${target}, ${oldIndex} => ${newIndex}`)\n    }\n  },\n  mounted () {\n    getDragList().then(res => {\n      this.list1 = res.data\n      this.list2 = [res.data[0]]\n    })\n  }\n}\n</script>\n<style lang=\"less\">\n.drag-box-card{\n  display: inline-block;\n  width: 600px;\n  height: 560px;\n  .drag-item{\n    margin: 10px;\n  }\n  h3{\n    padding: 10px 15px;\n  }\n  .drop-box{\n    border: 1px solid #eeeeee;\n    height: 455px;\n    border-radius: 5px;\n  }\n  .left-drop-box{\n    margin-right: 10px;\n  }\n  .right-drop-box{\n    //\n  }\n}\n.handle-log-box{\n  display: inline-block;\n  margin-left: 20px;\n  border: 1px solid #eeeeee;\n  vertical-align: top;\n  width: 200px;\n  height: 500px;\n  h3{\n    padding: 10px 14px;\n  }\n  .handle-inner-box{\n    height: ~\"calc(100% - 44px)\";\n    overflow: auto;\n    p{\n      padding: 14px 0;\n      margin: 0 14px;\n      border-bottom: 1px dashed #eeeeee;\n    }\n  }\n}\n.res-show-box{\n  display: inline-block;\n  margin-left: 20px;\n  border: 1px solid #eeeeee;\n  vertical-align: top;\n  width: 350px;\n  height: 570px;\n}\n</style>\n"
  },
  {
    "path": "src/view/components/editor/editor.vue",
    "content": "<template>\n  <div>\n    <editor ref=\"editor\" :value=\"content\" @on-change=\"handleChange\"/>\n    <Button @click=\"changeContent\">修改编辑器内容</Button>\n  </div>\n</template>\n\n<script>\nimport Editor from '_c/editor'\nexport default {\n  name: 'editor_page',\n  components: {\n    Editor\n  },\n  data () {\n    return {\n      content: '12312323'\n    }\n  },\n  methods: {\n    handleChange (html, text) {\n      console.log(html, text)\n    },\n    changeContent () {\n      this.$refs.editor.setHtml('<p>powered by wangeditor</p>')\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/components/icons/icons.vue",
    "content": "<template>\n  <Row>\n    <i-col span=\"16\">\n      <Row v-for=\"i in (customIconList.length / 3)\" :key=\"`custom-icon-row-${i}`\">\n        <i-col span=\"8\" v-for=\"item in customIconList.slice((i - 1) * 3, i * 3)\" :key=\"`custom-icon-${item}`\">\n          <Card style=\"margin: 0 5px 5px; text-align: center;\">\n            <icons :size=\"30\" :type=\"item\"/>\n            <p class=\"icon-code\">&lt;Icons :size=\"30\" type=\"{{ item }}\"&gt;</p>\n            <p>&lt;CommonIcon :size=\"30\" type=\"_{{ item }}\"&gt;</p>\n          </Card>\n        </i-col>\n      </Row>\n      <Row>\n        <i-col>\n          <Card style=\"margin: 0 5px 5px; text-align: center;\">\n            <common-icon :size=\"30\" type=\"ionic\"/>\n            <p class=\"icon-code\">iView内置图标</p>\n            <p>&lt;CommonIcon :size=\"30\" type=\"ionic\"&gt;</p>\n          </Card>\n        </i-col>\n      </Row>\n    </i-col>\n    <i-col span=\"8\">\n      <Card>\n        <p class=\"intro-p\"><Icon style=\"margin-right: 10px;\" :size=\"10\" type=\"heart\"/>Icons组件支持自定义图标的显示，具体自定义图标字体文件的制作请参考文档。</p>\n        <p class=\"intro-p\"><Icon style=\"margin-right: 10px;\" :size=\"10\" type=\"heart\"/>CommonIcon组件同时支持iView内置图标类型和自定义图标类型，为了区别这两种类型，需要在自定义图标名称前加下划线\"_\"</p>\n      </Card>\n    </i-col>\n  </Row>\n</template>\n\n<script>\nimport Icons from '_c/icons'\nimport CommonIcon from '_c/common-icon'\nexport default {\n  name: 'icons_pages',\n  components: {\n    Icons,\n    CommonIcon\n  },\n  data () {\n    return {\n      customIconList: [\n        'woman',\n        'man',\n        'smile',\n        'meh',\n        'frown',\n        'bear'\n      ]\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.icon-code{\n  margin: 20px 0 10px;\n}\n.intro-p{\n  margin-bottom: 10px;\n}\n</style>\n"
  },
  {
    "path": "src/view/components/markdown/markdown.vue",
    "content": "<template>\n  <div>\n    <markdown-editor v-model=\"content\"/>\n  </div>\n</template>\n\n<script>\nimport MarkdownEditor from '_c/markdown'\nexport default {\n  name: 'markdown_page',\n  components: {\n    MarkdownEditor\n  },\n  data () {\n    return {\n      content: ''\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/components/org-tree/components/org-view.vue",
    "content": "<template>\n  <div\n    ref=\"dragWrapper\"\n    class=\"org-tree-drag-wrapper\"\n    @mousedown=\"mousedownView\"\n    @contextmenu=\"handleDocumentContextmenu\"\n  >\n    <div class=\"org-tree-wrapper\" :style=\"orgTreeStyle\">\n      <v-org-tree\n        v-if=\"data\"\n        :data=\"data\"\n        :node-render=\"nodeRender\"\n        :expand-all=\"true\"\n        @on-node-click=\"handleNodeClick\"\n        collapsable\n      ></v-org-tree>\n    </div>\n  </div>\n</template>\n\n<script>\nimport { on, off } from '@/libs/tools'\nconst menuList = [\n  {\n    key: 'edit',\n    label: '编辑部门'\n  },\n  {\n    key: 'detail',\n    label: '查看部门'\n  },\n  {\n    key: 'new',\n    label: '新增子部门'\n  },\n  {\n    key: 'delete',\n    label: '删除部门'\n  }\n]\nexport default {\n  name: 'OrgView',\n  props: {\n    zoomHandled: {\n      type: Number,\n      default: 1\n    },\n    data: Object\n  },\n  data () {\n    return {\n      currentContextMenuId: '',\n      orgTreeOffsetLeft: 0,\n      orgTreeOffsetTop: 0,\n      initPageX: 0,\n      initPageY: 0,\n      oldMarginLeft: 0,\n      oldMarginTop: 0,\n      canMove: false\n    }\n  },\n  computed: {\n    orgTreeStyle () {\n      return {\n        transform: `translate(-50%, -50%) scale(${this.zoomHandled}, ${\n          this.zoomHandled\n        })`,\n        marginLeft: `${this.orgTreeOffsetLeft}px`,\n        marginTop: `${this.orgTreeOffsetTop}px`\n      }\n    }\n  },\n  methods: {\n    handleNodeClick (e, data, expand) {\n      expand()\n    },\n    closeMenu () {\n      this.currentContextMenuId = ''\n    },\n    getBgColor (data) {\n      return this.currentContextMenuId === data.id\n        ? data.isRoot\n          ? '#0d7fe8'\n          : '#5d6c7b'\n        : ''\n    },\n    nodeRender (h, data) {\n      return (\n        <div\n          class={[\n            'custom-org-node',\n            data.children && data.children.length ? 'has-children-label' : ''\n          ]}\n          on-mousedown={event => event.stopPropagation()}\n          on-contextmenu={this.contextmenu.bind(this, data)}\n        >\n          {data.label}\n          <dropdown\n            trigger=\"custom\"\n            class=\"context-menu\"\n            visible={this.currentContextMenuId === data.id}\n            nativeOn-click={this.handleDropdownClick}\n            on-on-click={this.handleContextMenuClick.bind(this, data)}\n            style={{\n              transform: `scale(${1 / this.zoomHandled}, ${1 /\n                this.zoomHandled})`\n            }}\n            v-click-outside={this.closeMenu}\n          >\n            <dropdown-menu slot=\"list\">\n              {menuList.map(item => {\n                return (\n                  <dropdown-item name={item.key}>{item.label}</dropdown-item>\n                )\n              })}\n            </dropdown-menu>\n          </dropdown>\n        </div>\n      )\n    },\n    contextmenu (data, $event) {\n      let event = $event || window.event\n      event.preventDefault\n        ? event.preventDefault()\n        : (event.returnValue = false)\n      this.currentContextMenuId = data.id\n    },\n    setDepartmentData (data) {\n      data.isRoot = true\n      this.departmentData = data\n    },\n    mousedownView (event) {\n      this.canMove = true\n      this.initPageX = event.pageX\n      this.initPageY = event.pageY\n      this.oldMarginLeft = this.orgTreeOffsetLeft\n      this.oldMarginTop = this.orgTreeOffsetTop\n      on(document, 'mousemove', this.mousemoveView)\n      on(document, 'mouseup', this.mouseupView)\n    },\n    mousemoveView (event) {\n      if (!this.canMove) return\n      const { pageX, pageY } = event\n      this.orgTreeOffsetLeft = this.oldMarginLeft + pageX - this.initPageX\n      this.orgTreeOffsetTop = this.oldMarginTop + pageY - this.initPageY\n    },\n    mouseupView () {\n      this.canMove = false\n      off(document, 'mousemove', this.mousemoveView)\n      off(document, 'mouseup', this.mouseupView)\n    },\n    handleDropdownClick (event) {\n      event.stopPropagation()\n    },\n    handleDocumentContextmenu () {\n      this.canMove = false\n    },\n    handleContextMenuClick (data, key) {\n      this.$emit('on-menu-click', { data, key })\n    }\n  },\n  mounted () {\n    on(document, 'contextmenu', this.handleDocumentContextmenu)\n  },\n  beforeDestroy () {\n    off(document, 'contextmenu', this.handleDocumentContextmenu)\n  }\n}\n</script>\n\n<style>\n</style>\n"
  },
  {
    "path": "src/view/components/org-tree/components/zoom-controller.vue",
    "content": "<template>\n  <div class=\"zoom-wrapper\">\n    <button class=\"zoom-button\" @click=\"scale('down')\">\n      <Icon type=\"md-remove\" :size=\"14\" color=\"#fff\"/>\n    </button>\n    <span class=\"zoom-number\">{{ value }}%</span>\n    <button class=\"zoom-button\" @click=\"scale('up')\">\n      <Icon type=\"md-add\" :size=\"14\" color=\"#fff\"/>\n    </button>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'ZoomController',\n  props: {\n    value: {\n      type: Number,\n      default: 100\n    },\n    step: {\n      type: Number,\n      default: 20\n    },\n    min: {\n      type: Number,\n      default: 10\n    },\n    max: {\n      type: Number,\n      default: 200\n    }\n  },\n  methods: {\n    scale (type) {\n      const zoom = this.value + (type === 'down' ? -this.step : this.step)\n      if (\n        (zoom < this.min && type === 'down') ||\n        (zoom > this.max && type === 'up')\n      ) {\n        return\n      }\n      this.$emit('input', zoom)\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.trans(@duration) {\n  transition: ~\"all @{duration} ease-in\";\n}\n.zoom-wrapper {\n  .zoom-button {\n    width: 20px;\n    height: 20px;\n    line-height: 10px;\n    border-radius: 50%;\n    background: rgba(157, 162, 172, 1);\n    box-shadow: 0px 2px 8px 0px rgba(218, 220, 223, 0.7);\n    border: none;\n    cursor: pointer;\n    outline: none;\n    &:active {\n      box-shadow: 0px 0px 2px 2px rgba(218, 220, 223, 0.2) inset;\n    }\n    .trans(0.1s);\n    &:hover {\n      background: #1890ff;\n      .trans(0.1s);\n    }\n  }\n  .zoom-number {\n    color: #657180;\n    padding: 0 8px;\n    display: inline-block;\n    width: 46px;\n    text-align: center;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/view/components/org-tree/index.less",
    "content": "@wrapper: ~'department';\n.percent-100 {\n  width: 100%;\n  height: 100%;\n}\n.@{wrapper}-outer {\n  .percent-100;\n  overflow: hidden;\n  .tip-box{\n    position: absolute;\n    left: 20px;\n    top: 20px;\n    z-index: 12;\n  }\n  .zoom-box {\n    position: absolute;\n    right: 30px;\n    bottom: 30px;\n    z-index: 2;\n  }\n  .view-box {\n    position: absolute;\n    top: 0;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    z-index: 1;\n    cursor: move;\n    .org-tree-drag-wrapper {\n      width: 100%;\n      height: 100%;\n    }\n    .org-tree-wrapper {\n      display: inline-block;\n      position: absolute;\n      left: 50%;\n      top: 50%;\n      transition: transform 0.2s ease-out;\n      .org-tree-node-label {\n        box-shadow: 0px 2px 12px 0px rgba(143, 154, 165, 0.4);\n        border-radius: 4px;\n        .org-tree-node-label-inner {\n          padding: 0;\n          .custom-org-node {\n            padding: 14px 41px;\n            background: #738699;\n            user-select: none;\n            word-wrap: none;\n            white-space: nowrap;\n            border-radius: 4px;\n            color: #ffffff;\n            font-size: 14px;\n            font-weight: 500;\n            line-height: 20px;\n            transition: background 0.1s ease-in;\n            cursor: default;\n            &:hover {\n              background: #5d6c7b;\n              transition: background 0.1s ease-in;\n            }\n            &.has-children-label {\n              cursor: pointer;\n            }\n            .context-menu{\n              position: absolute;\n              right: -10px;\n              bottom: 20px;\n              z-index: 10;\n            }\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "src/view/components/org-tree/index.vue",
    "content": "<template>\n  <Card shadow style=\"height: 100%;width: 100%;overflow:hidden\">\n    <div class=\"department-outer\">\n      <div class=\"tip-box\">\n        <b style=\"margin-right: 20px;\">powered by <a target=\"blank\" href=\"https://github.com/lison16\">Lison</a></b>\n        <a target=\"blank\" href=\"https://github.com/lison16/v-org-tree\" style=\"margin-right: 10px;\">v-org-tree文档</a>\n      </div>\n      <div class=\"zoom-box\">\n        <zoom-controller v-model=\"zoom\" :min=\"20\" :max=\"200\"></zoom-controller>\n      </div>\n      <div class=\"view-box\">\n        <org-view\n          v-if=\"data\"\n          :data=\"data\"\n          :zoom-handled=\"zoomHandled\"\n          @on-menu-click=\"handleMenuClick\"\n        ></org-view>\n      </div>\n    </div>\n  </Card>\n</template>\n\n<script>\nimport OrgView from './components/org-view.vue'\nimport ZoomController from './components/zoom-controller.vue'\nimport { getOrgData } from '@/api/data'\nimport './index.less'\nconst menuDic = {\n  edit: '编辑部门',\n  detail: '查看部门',\n  new: '新增子部门',\n  delete: '删除部门'\n}\nexport default {\n  name: 'org_tree_page',\n  components: {\n    OrgView,\n    ZoomController\n  },\n  data () {\n    return {\n      data: null,\n      zoom: 100\n    }\n  },\n  computed: {\n    zoomHandled () {\n      return this.zoom / 100\n    }\n  },\n  methods: {\n    setDepartmentData (data) {\n      data.isRoot = true\n      return data\n    },\n    handleMenuClick ({ data, key }) {\n      this.$Message.success({\n        duration: 5,\n        content: `点击了《${data.label}》节点的'${menuDic[key]}'菜单`\n      })\n    },\n    getDepartmentData () {\n      getOrgData().then(res => {\n        const { data } = res\n        this.data = data\n      })\n    }\n  },\n  mounted () {\n    this.getDepartmentData()\n  }\n}\n</script>\n\n<style>\n</style>\n"
  },
  {
    "path": "src/view/components/split-pane/split-pane.vue",
    "content": "<template>\n  <div class=\"split-pane-page-wrapper\">\n    <split-pane v-model=\"offset\" @on-moving=\"handleMoving\">\n      <div slot=\"left\" class=\"pane left-pane\">\n        <split-pane v-model=\"offsetVertical\" mode=\"vertical\" @on-moving=\"handleMoving\">\n          <div slot=\"top\" class=\"pane top-pane\"></div>\n          <div slot=\"bottom\" class=\"pane bottom-pane\"></div>\n          <div slot=\"trigger\" class=\"custom-trigger\">\n            <icons class=\"trigger-icon\" :size=\"22\" type=\"resize-vertical\" color=\"#fff\"/>\n          </div>\n        </split-pane>\n      </div>\n      <div slot=\"right\" class=\"pane right-pane\"></div>\n    </split-pane>\n  </div>\n</template>\n\n<script>\nimport SplitPane from '_c/split-pane'\nimport Icons from '_c/icons'\nexport default {\n  name: 'split_pane_page',\n  components: {\n    SplitPane,\n    Icons\n  },\n  data () {\n    return {\n      offset: 0.6,\n      offsetVertical: '250px'\n    }\n  },\n  methods: {\n    handleMoving (e) {\n      console.log(e.atMin, e.atMax)\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.center-middle{\n  position: absolute;\n  left: 50%;\n  top: 50%;\n  transform: translate(-50%, -50%);\n}\n.split-pane-page-wrapper{\n  height: 600px;\n  .pane{\n    width: 100%;\n    height: 100%;\n    &.left-pane{\n      background: sandybrown;\n    }\n    &.right-pane{\n      background: palevioletred;\n    }\n    &.top-pane{\n      background: sandybrown;\n    }\n    &.bottom-pane{\n      background: palevioletred;\n    }\n  }\n  .custom-trigger{\n    width: 20px;\n    height: 20px;\n    border-radius: 50%;\n    background: #000000;\n    position: absolute;\n    .center-middle;\n    box-shadow: 0 0 6px 0 rgba(28, 36, 56, 0.4);\n    i.trigger-icon{\n      .center-middle;\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "src/view/components/tables/tables.vue",
    "content": "<template>\n  <div>\n    <Card>\n      <tables ref=\"tables\" editable searchable search-place=\"top\" v-model=\"tableData\" :columns=\"columns\" @on-delete=\"handleDelete\"/>\n      <Button style=\"margin: 10px 0;\" type=\"primary\" @click=\"exportExcel\">导出为Csv文件</Button>\n    </Card>\n  </div>\n</template>\n\n<script>\nimport Tables from '_c/tables'\nimport { getTableData } from '@/api/data'\nexport default {\n  name: 'tables_page',\n  components: {\n    Tables\n  },\n  data () {\n    return {\n      columns: [\n        { title: 'Name', key: 'name', sortable: true },\n        { title: 'Email', key: 'email', editable: true },\n        { title: 'Create-Time', key: 'createTime' },\n        {\n          title: 'Handle',\n          key: 'handle',\n          options: ['delete'],\n          button: [\n            (h, params, vm) => {\n              return h('Poptip', {\n                props: {\n                  confirm: true,\n                  title: '你确定要删除吗?'\n                },\n                on: {\n                  'on-ok': () => {\n                    vm.$emit('on-delete', params)\n                    vm.$emit('input', params.tableData.filter((item, index) => index !== params.row.initRowIndex))\n                  }\n                }\n              }, [\n                h('Button', '自定义删除')\n              ])\n            }\n          ]\n        }\n      ],\n      tableData: []\n    }\n  },\n  methods: {\n    handleDelete (params) {\n      console.log(params)\n    },\n    exportExcel () {\n      this.$refs.tables.exportCsv({\n        filename: `table-${(new Date()).valueOf()}.csv`\n      })\n    }\n  },\n  mounted () {\n    getTableData().then(res => {\n      this.tableData = res.data\n    })\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/components/tree-select/index.vue",
    "content": "<template>\n  <div>\n    <tree-select\n        v-model=\"treeSelected\"\n        style=\"width: 300px;\"\n        check-strictly\n        :expand-all=\"true\"\n        :load-data=\"loadData\"\n        @on-change=\"handleTreeSelectChange\"\n        @on-toggle-expand=\"handleTreeSelectExpand\"\n        @on-check-change=\"handleTreeSelectCheckChange\"\n        @on-select-change=\"handleTreeSelectClick\"\n        :data=\"treeData\"\n      ></tree-select>\n      <Button @click=\"changeTreeSelectData\">更新选中数据</Button>\n      <Button @click=\"changeTreeData\">更新树数据</Button>\n  </div>\n</template>\n\n<script>\nimport TreeSelect from '_c/tree-select'\nimport { newTreeData } from '@/mock/data/tree-select'\nimport { getTreeSelectData } from '@/api/data'\nexport default {\n  name: 'tree_select_page',\n  components: {\n    TreeSelect\n  },\n  data () {\n    return {\n      treeSelected: [112, 113],\n      treeData: []\n    }\n  },\n  mounted () {\n    getTreeSelectData().then(res => {\n      const { data } = res\n      this.treeData = data\n    })\n  },\n  methods: {\n    changeTreeSelectData () {\n      this.treeSelected = [111, 114]\n    },\n    changeTreeData () {\n      this.treeData = newTreeData\n      // this.treeSelected = [];\n    },\n    handleTreeSelectChange (list) {\n      // console.log('=-========', list);\n    },\n    handleTreeSelectExpand (item) {\n      // console.log('toggle expand', item);\n    },\n    handleTreeSelectCheckChange (selectedArray, item) {\n      // console.log(selectedArray, item);\n    },\n    handleTreeSelectClick (selectArray, item) {\n      // console.log(selectArray, item);\n    },\n    loadData (item, callback) {\n      setTimeout(() => {\n        let data = [\n          {\n            id: 111,\n            title: '1-1-1'\n          },\n          {\n            id: 112,\n            title: '1-1-2'\n          },\n          {\n            id: 113,\n            title: '1-1-3'\n          },\n          {\n            id: 114,\n            title: '1-1-4'\n          }\n        ]\n        callback(data)\n      }, 1000)\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/components/tree-table/index.vue",
    "content": "<template>\n  <div>\n    <Card shadow>\n      树状表格组件tree-table-vue，基于<a href=\"https://github.com/MisterTaki/vue-table-with-tree-grid\">vue-table-with-tree-grid</a>进行开发，修复了一些bug，添加了一些新属性\n      <p><b>支持使用slot-scope进行自定义列渲染内容</b></p>\n      <p>文档请看<a href=\"https://github.com/lison16/tree-table-vue\">https://github.com/lison16/tree-table-vue</a></p>\n      <tree-table expand-key=\"sex\" :expand-type=\"false\" :selectable=\"false\" :columns=\"columns\" :data=\"data\" >\n        <template slot=\"likes\" slot-scope=\"scope\">\n          <Button @click=\"handle(scope)\">123</Button>\n        </template>\n      </tree-table>\n    </Card>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'tree_table_page',\n  data () {\n    return {\n      columns: [\n        {\n          title: 'name',\n          key: 'name',\n          width: '400px'\n        },\n        {\n          title: 'sex',\n          key: 'sex',\n          minWidth: '50px'\n        },\n        {\n          title: 'score',\n          key: 'score'\n        },\n        {\n          title: 'likes',\n          key: 'likes',\n          minWidth: '200px',\n          type: 'template',\n          template: 'likes'\n        }\n      ],\n      data: [\n        {\n          name: 'Jack',\n          sex: 'male',\n          likes: ['football', 'basketball'],\n          score: 10,\n          children: [\n            {\n              name: 'Ashley',\n              sex: 'female',\n              likes: ['football', 'basketball'],\n              score: 20,\n              children: [\n                {\n                  name: 'Ashley',\n                  sex: 'female',\n                  likes: ['football', 'basketball'],\n                  score: 20\n                },\n                {\n                  name: 'Taki',\n                  sex: 'male',\n                  likes: ['football', 'basketball'],\n                  score: 10,\n                  children: [\n                    {\n                      name: 'Ashley',\n                      sex: 'female',\n                      likes: ['football', 'basketball'],\n                      score: 20\n                    },\n                    {\n                      name: 'Taki',\n                      sex: 'male',\n                      likes: ['football', 'basketball'],\n                      score: 10,\n                      children: [\n                        {\n                          name: 'Ashley',\n                          sex: 'female',\n                          likes: ['football', 'basketball'],\n                          score: 20\n                        },\n                        {\n                          name: 'Taki',\n                          sex: 'male',\n                          likes: ['football', 'basketball'],\n                          score: 10\n                        }\n                      ]\n                    }\n                  ]\n                }\n              ]\n            },\n            {\n              name: 'Taki',\n              sex: 'male',\n              likes: ['football', 'basketball'],\n              score: 10\n            }\n          ]\n        },\n        {\n          name: 'Tom',\n          sex: 'male',\n          likes: ['football', 'basketball'],\n          score: 20,\n          children: [\n            {\n              name: 'Ashley',\n              sex: 'female',\n              likes: ['football', 'basketball'],\n              score: 20,\n              children: [\n                {\n                  name: 'Ashley',\n                  sex: 'female',\n                  likes: ['football', 'basketball'],\n                  score: 20\n                },\n                {\n                  name: 'Taki',\n                  sex: 'male',\n                  likes: ['football', 'basketball'],\n                  score: 10\n                }\n              ]\n            },\n            {\n              name: 'Taki',\n              sex: 'male',\n              likes: ['football', 'basketball'],\n              score: 10,\n              children: [\n                {\n                  name: 'Ashley',\n                  sex: 'female',\n                  likes: ['football', 'basketball'],\n                  score: 20\n                },\n                {\n                  name: 'Taki',\n                  sex: 'male',\n                  likes: ['football', 'basketball'],\n                  score: 10\n                }\n              ]\n            }\n          ]\n        },\n        {\n          name: 'Tom',\n          sex: 'male',\n          likes: ['football', 'basketball'],\n          score: 20\n        },\n        {\n          name: 'Tom',\n          sex: 'male',\n          likes: ['football', 'basketball'],\n          score: 20,\n          children: [\n            {\n              name: 'Ashley',\n              sex: 'female',\n              likes: ['football', 'basketball'],\n              score: 20\n            },\n            {\n              name: 'Taki',\n              sex: 'male',\n              likes: ['football', 'basketball'],\n              score: 10\n            }\n          ]\n        }\n      ]\n    }\n  },\n  methods: {\n    handle (scope) {\n      console.log(scope)\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/directive/directive.vue",
    "content": "<template>\n  <div>\n    <Row>\n      <i-col>\n        <Card>\n          <Row>\n            <i-col span=\"8\">\n              <Button type=\"primary\" @click=\"showModal\">显示可拖动弹窗</Button>\n              <br/>\n              <Button v-draggable=\"buttonOptions\" class=\"draggable-btn\">这个按钮也是可以拖动的</Button>\n            </i-col>\n            <i-col span=\"16\">\n              <div class=\"intro-con\">\n                &lt;Modal v-draggable=\"options\" v-model=\"visible\"&gt;标题&lt;/Modal&gt;\n                <pre class=\"code-con\">\n    options = {\n      trigger: '.ivu-modal-body',\n      body: '.ivu-modal'\n    }\n                </pre>\n              </div>\n            </i-col>\n          </Row>\n        </Card>\n      </i-col>\n      <Modal v-draggable=\"options\" v-model=\"modalVisible\">\n        拖动这里即可拖动整个弹窗\n      </Modal>\n    </Row>\n    <Row style=\"margin-top: 10px;\">\n      <i-col>\n        <Card>\n          <Row>\n            <i-col span=\"8\">\n              <Input style=\"width: 60%\" v-model=\"inputValue\">\n                <Button slot=\"append\" v-clipboard=\"clipOptions\">copy</Button>\n              </Input>\n            </i-col>\n            <i-col span=\"16\">\n              <div class=\"intro-con\">\n                &lt;Input style=\"width: 60%\" v-model=\"inputValue\"&gt;\n                  <br/>\n                  &nbsp;&nbsp;&nbsp;&lt;Button slot=\"append\" v-clipboard=\"clipOptions\"&gt;copy&lt;/Button&gt;\n                  <br/>\n                &lt;/Input&gt;\n                <pre class=\"code-con\">\n    clipOptions: {\n      value: this.inputValue,\n      success: (e) => {\n        this.$Message.success('复制成功')\n      },\n      error: () => {\n        this.$Message.error('复制失败')\n      }\n    }\n                </pre>\n              </div>\n            </i-col>\n          </Row>\n        </Card>\n      </i-col>\n      <Modal v-draggable=\"options\" v-model=\"modalVisible\">\n        拖动这里即可拖动整个弹窗\n      </Modal>\n    </Row>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'directive_page',\n  data () {\n    return {\n      modalVisible: false,\n      options: {\n        trigger: '.ivu-modal-body',\n        body: '.ivu-modal',\n        recover: true\n      },\n      buttonOptions: {\n        trigger: '.draggable-btn',\n        body: '.draggable-btn'\n      },\n      statu: 1,\n      inputValue: '这是输入的内容'\n    }\n  },\n  computed: {\n    clipOptions () {\n      return {\n        value: this.inputValue,\n        success: (e) => {\n          this.$Message.success('复制成功')\n        },\n        error: () => {\n          this.$Message.error('复制失败')\n        }\n      }\n    }\n  },\n  methods: {\n    showModal () {\n      this.modalVisible = true\n    }\n  }\n}\n</script>\n\n<style>\n.intro-con{\n  min-height: 140px;\n}\n.draggable-btn{\n  margin-top: 20px;\n}\n.code-con{\n  width: 400px;\n  background: #F9F9F9;\n  padding-top: 10px;\n}\n</style>\n"
  },
  {
    "path": "src/view/error-page/401.vue",
    "content": "<template>\n  <error-content code=\"401\" desc=\"Oh~~您没有浏览这个页面的权限~\" :src=\"src\"/>\n</template>\n\n<script>\nimport error401 from '@/assets/images/error-page/error-401.svg'\nimport errorContent from './error-content.vue'\nexport default {\n  name: 'error_401',\n  components: {\n    errorContent\n  },\n  data () {\n    return {\n      src: error401\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/error-page/404.vue",
    "content": "<template>\n  <error-content code=\"404\" desc=\"Oh~~您的页面好像飞走了~\" :src=\"src\"/>\n</template>\n\n<script>\nimport error404 from '@/assets/images/error-page/error-404.svg'\nimport errorContent from './error-content.vue'\nexport default {\n  name: 'error_404',\n  components: {\n    errorContent\n  },\n  data () {\n    return {\n      src: error404\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/error-page/500.vue",
    "content": "<template>\n  <error-content code=\"500\" desc=\"Oh~~鬼知道服务器经历了什么~\" :src=\"src\"/>\n</template>\n\n<script>\nimport error404 from '@/assets/images/error-page/error-500.svg'\nimport errorContent from './error-content.vue'\nexport default {\n  name: 'error_500',\n  components: {\n    errorContent\n  },\n  data () {\n    return {\n      src: error404\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/error-page/back-btn-group.vue",
    "content": "<template>\n  <div>\n    <Button size=\"large\" type=\"text\" @click=\"backHome\">返回首页</Button>\n    <Button size=\"large\" type=\"text\" @click=\"backPrev\">返回上一页({{ second }}s)</Button>\n  </div>\n</template>\n\n<script>\nimport './error.less'\nexport default {\n  name: 'backBtnGroup',\n  data () {\n    return {\n      second: 5,\n      timer: null\n    }\n  },\n  methods: {\n    backHome () {\n      this.$router.replace({\n        name: this.$config.homeName\n      })\n    },\n    backPrev () {\n      this.$router.go(-1)\n    }\n  },\n  mounted () {\n    this.timer = setInterval(() => {\n      if (this.second === 0) this.backPrev()\n      else this.second--\n    }, 1000)\n  },\n  beforeDestroy () {\n    clearInterval(this.timer)\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/error-page/error-content.vue",
    "content": "<template>\n  <div class=\"error-page\">\n    <div class=\"content-con\">\n      <img :src=\"src\" :alt=\"code\">\n      <div class=\"text-con\">\n        <h4>{{ code }}</h4>\n        <h5>{{ desc }}</h5>\n      </div>\n      <back-btn-group class=\"back-btn-group\"></back-btn-group>\n    </div>\n  </div>\n</template>\n\n<script>\nimport './error.less'\nimport backBtnGroup from './back-btn-group.vue'\nexport default {\n  name: 'error_content',\n  components: {\n    backBtnGroup\n  },\n  props: {\n    code: String,\n    desc: String,\n    src: String\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/error-page/error.less",
    "content": ".error-page{\n  width: 100%;\n  height: 100%;\n  position: relative;\n  background: #f8f8f9;\n  .content-con{\n    width: 700px;\n    height: 600px;\n    position: absolute;\n    left: 50%;\n    top: 50%;\n    transform: translate(-50%, -60%);\n    img{\n      display: block;\n      width: 100%;\n      height: 100%;\n    }\n    .text-con{\n      position: absolute;\n      left: 0px;\n      top: 0px;\n      h4{\n        position: absolute;\n        left: 0px;\n        top: 0px;\n        font-size: 80px;\n        font-weight: 700;\n        color: #348EED;\n      }\n      h5{\n        position: absolute;\n        width: 700px;\n        left: 0px;\n        top: 100px;\n        font-size: 20px;\n        font-weight: 700;\n        color: #67647D;\n      }\n    }\n    .back-btn-group{\n      position: absolute;\n      right: 0px;\n      bottom: 20px;\n    }\n  }\n}\n"
  },
  {
    "path": "src/view/error-store/error-store.vue",
    "content": "<template>\n  <div>\n    <Card>\n      iview-admin会自动将你程序中的错误收集起来，你可以将错误日志发给后端保存起来。如果你不需要这个功能，将'./src/config/index.js'里的plugin的'error-store'属性删掉即可。\n      另外在开发环境下，你程序中的错误都会被收集起来，这样可能不利于你排查错误，你可以将'./src/config/index.js'的'error-store'的'developmentOff'设为true。\n      如果你只是想收集错误日志，不希望登录用户看到错误日志，你可以不提供查看日志的入口，只需将'./src/config/index.js'的'error-store'的'showInHeader'设为false。\n    </Card>\n    <Card style=\"margin-top: 20px;\">\n      <Row>\n        <i-col span=\"8\">\n          <Button @click=\"click\" style=\"display: block\">点击测试触发程序错误</Button>\n          <Button @click=\"ajaxClick\" style=\"margin-top:10px;\">点击测试触发ajax接口请求错误</Button>\n        </i-col>\n        <i-col span=\"16\">\n          ajax接口请求是请求easy-mock的一个不存在接口，所以服务端会报404错误，错误收集机制会收集这个错误，测试的时候有一定网络延迟，所以点击按钮之后稍等一会才会收集到错误。\n        </i-col>\n      </Row>\n    </Card>\n  </div>\n</template>\n\n<script>\nimport { errorReq } from '@/api/data'\nexport default {\n  name: 'error_store_page',\n  methods: {\n    click () {\n      console.log(admin)\n    },\n    ajaxClick () {\n      errorReq()\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/excel/common.less",
    "content": ".margin-top-8{\n    margin-top: 8px;\n}\n.margin-top-10{\n    margin-top: 10px;\n}\n.margin-top-20{\n    margin-top: 20px;\n}\n.margin-left-10{\n    margin-left: 10px;\n}\n.margin-bottom-10{\n    margin-bottom: 10px;\n}\n.margin-bottom-100{\n    margin-bottom: 100px;\n}\n.margin-right-10{\n    margin-right: 10px;\n}\n.padding-left-6{\n    padding-left: 6px;\n}\n.padding-left-8{\n    padding-left: 5px;\n}\n.padding-left-10{\n    padding-left: 10px;\n}\n.padding-left-20{\n    padding-left: 20px;\n}\n.height-100{\n    height: 100%;\n}\n.height-120px{\n    height: 100px;\n}\n.height-200px{\n    height: 200px;\n}\n.height-492px{\n    height: 492px;\n}\n.height-460px{\n    height: 460px;\n}\n.line-gray{\n    height: 0;\n    border-bottom: 2px solid #dcdcdc;\n}\n.notwrap{\n    word-break:keep-all; \n    white-space:nowrap;\n    overflow: hidden;\n    text-overflow: ellipsis;\n}\n.padding-left-5{\n    padding-left: 10px;\n}\n[v-cloak]{\n    display: none;\n}"
  },
  {
    "path": "src/view/excel/export-excel.vue",
    "content": "<style lang=\"less\">\n    @import \"./common.less\";\n</style>\n<template>\n  <div>\n    <Card title=\"导出EXCEL\">\n      <Row>\n        <Button icon=\"md-download\" :loading=\"exportLoading\" @click=\"exportExcel\">导出文件</Button>\n      </Row>\n    </Card>\n    <Row class=\"margin-top-10\">\n      <Table :columns=\"tableTitle\" :data=\"tableData\"></Table>\n    </Row>\n  </div>\n</template>\n<script>\nimport excel from '@/libs/excel'\nexport default {\n  name: 'export-excel',\n  data () {\n    return {\n      exportLoading: false,\n      tableTitle: [\n        {\n          title: '一级分类',\n          key: 'category1'\n        },\n        {\n          title: '二级分类',\n          key: 'category2'\n        },\n        {\n          title: '三级分类',\n          key: 'category3'\n        }\n      ],\n      tableData: [\n        {\n          category1: 1,\n          category2: 2,\n          category3: 3\n        },\n        {\n          category1: 4,\n          category2: 5,\n          category3: 6\n        },\n        {\n          category1: 7,\n          category2: 8,\n          category3: 9\n        }\n      ]\n    }\n  },\n  methods: {\n    exportExcel () {\n      if (this.tableData.length) {\n        this.exportLoading = true\n        const params = {\n          title: ['一级分类', '二级分类', '三级分类'],\n          key: ['category1', 'category2', 'category3'],\n          data: this.tableData,\n          autoWidth: true,\n          filename: '分类列表'\n        }\n        excel.export_array_to_excel(params)\n        this.exportLoading = false\n      } else {\n        this.$Message.info('表格数据不能为空！')\n      }\n    }\n  },\n  created () {\n\n  },\n  mounted () {\n\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/excel/upload-excel.vue",
    "content": "<style lang=\"less\">\n  @import \"./common.less\";\n</style>\n<template>\n  <div>\n    <Card title=\"导入EXCEL\">\n      <Row>\n        <Upload action=\"\" :before-upload=\"handleBeforeUpload\" accept=\".xls, .xlsx\">\n          <Button icon=\"ios-cloud-upload-outline\" :loading=\"uploadLoading\" @click=\"handleUploadFile\">上传文件</Button>\n        </Upload>\n      </Row>\n      <Row>\n        <div class=\"ivu-upload-list-file\" v-if=\"file !== null\">\n          <Icon type=\"ios-stats\"></Icon>\n            {{ file.name }}\n          <Icon v-show=\"showRemoveFile\" type=\"ios-close\" class=\"ivu-upload-list-remove\" @click.native=\"handleRemove()\"></Icon>\n        </div>\n      </Row>\n      <Row>\n        <transition name=\"fade\">\n          <Progress v-if=\"showProgress\" :percent=\"progressPercent\" :stroke-width=\"2\">\n            <div v-if=\"progressPercent == 100\">\n              <Icon type=\"ios-checkmark-circle\"></Icon>\n              <span>成功</span>\n            </div>\n          </Progress>\n        </transition>\n      </Row>\n    </Card>\n    <Row class=\"margin-top-10\">\n      <Table :columns=\"tableTitle\" :data=\"tableData\" :loading=\"tableLoading\"></Table>\n    </Row>\n  </div>\n</template>\n<script>\nimport excel from '@/libs/excel'\nexport default {\n  name: 'upload-excel',\n  data () {\n    return {\n      uploadLoading: false,\n      progressPercent: 0,\n      showProgress: false,\n      showRemoveFile: false,\n      file: null,\n      tableData: [],\n      tableTitle: [],\n      tableLoading: false\n    }\n  },\n  methods: {\n    initUpload () {\n      this.file = null\n      this.showProgress = false\n      this.loadingProgress = 0\n      this.tableData = []\n      this.tableTitle = []\n    },\n    handleUploadFile () {\n      this.initUpload()\n    },\n    handleRemove () {\n      this.initUpload()\n      this.$Message.info('上传的文件已删除！')\n    },\n    handleBeforeUpload (file) {\n      const fileExt = file.name.split('.').pop().toLocaleLowerCase()\n      if (fileExt === 'xlsx' || fileExt === 'xls') {\n        this.readFile(file)\n        this.file = file\n      } else {\n        this.$Notice.warning({\n          title: '文件类型错误',\n          desc: '文件：' + file.name + '不是EXCEL文件，请选择后缀为.xlsx或者.xls的EXCEL文件。'\n        })\n      }\n      return false\n    },\n    // 读取文件\n    readFile (file) {\n      const reader = new FileReader()\n      reader.readAsArrayBuffer(file)\n      reader.onloadstart = e => {\n        this.uploadLoading = true\n        this.tableLoading = true\n        this.showProgress = true\n      }\n      reader.onprogress = e => {\n        this.progressPercent = Math.round(e.loaded / e.total * 100)\n      }\n      reader.onerror = e => {\n        this.$Message.error('文件读取出错')\n      }\n      reader.onload = e => {\n        this.$Message.info('文件读取成功')\n        const data = e.target.result\n        const { header, results } = excel.read(data, 'array')\n        const tableTitle = header.map(item => { return { title: item, key: item } })\n        this.tableData = results\n        this.tableTitle = tableTitle\n        this.uploadLoading = false\n        this.tableLoading = false\n        this.showRemoveFile = true\n      }\n    }\n  },\n  created () {\n\n  },\n  mounted () {\n\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/i18n/i18n-page.vue",
    "content": "<template>\n  <div>\n    <Row :gutter=\"10\">\n      <i-col span=\"6\">\n        <Card>\n          <div class=\"i18n-card-box\">\n            <DatePicker type=\"date\" placeholder=\"Select date\"></DatePicker>\n            <TimePicker type=\"timerange\" placement=\"bottom-end\" placeholder=\"Select time\" style=\"display: block;margin-top: 10px;\"></TimePicker>\n            <Button type=\"primary\" @click=\"modalVisible = true\" style=\"margin-top: 10px;\">{{ $t('buttonText') }}</Button>\n            <Modal\n                v-model=\"modalVisible\"\n                :title=\"$t('modalTitle')\">\n                <p>{{ content }}</p>\n                <p>{{ content }}</p>\n                <p>{{ content }}</p>\n            </Modal>\n            <i class=\"tip\">{{ $t('i18n-tip') }}</i>\n          </div>\n        </Card>\n      </i-col>\n    </Row>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'i18n_page',\n  data () {\n    return {\n      modalVisible: false\n    }\n  },\n  computed: {\n    content () {\n      return this.$t('content')\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.i18n-card-box{\n  height: 200px;\n  .tip{\n    color: gray;\n    display: block;\n    margin-top: 20px;\n  }\n}\n</style>\n"
  },
  {
    "path": "src/view/join-page.vue",
    "content": "<template>\n  <div>\n    <Card shadow title=\"社区\">\n      <row class=\"join-page\" :gutter=\"32\">\n        <i-col span=\"10\">\n          <img class=\"qq-group-img\" src=\"../assets/images/icon-qr-qq-wechat.png\">\n          <row type=\"flex\" justify=\"center\">\n            <i-col span=\"12\">\n              <p>QQ 群号：621780943</p>\n            </i-col>\n            <i-col span=\"12\"></i-col>\n          </row>\n        </i-col>\n        <i-col span=\"14\">\n          <div class=\"join-page-other\">\n            <Button to=\"https://zhuanlan.zhihu.com/feview\" target=\"_blank\" size=\"large\">\n              <img src=\"../assets/images/icon-social-zhihu.svg\" class=\"join-page-other-icon\">\n              iView 知乎专栏\n            </Button>\n            <Button to=\"https://juejin.im/user/56fe494539b0570054f2e032\" target=\"_blank\" size=\"large\">\n              <img src=\"../assets/images/icon-social-juejin.svg\" class=\"join-page-other-icon\">\n              掘金\n            </Button>\n            <Button to=\"https://live.bilibili.com/1353202\" target=\"_blank\" size=\"large\">\n              <img src=\"../assets/images/icon-social-bilibili.svg\" class=\"join-page-other-icon\">\n              活动直播间\n            </Button>\n            <Button to=\"https://twitter.com/iViewUI\" target=\"_blank\" size=\"large\">\n              <img src=\"../assets/images/icon-social-twitter.svg\" class=\"join-page-other-icon\">\n              Twitter\n            </Button>\n          </div>\n        </i-col>\n      </row>\n    </Card>\n  </div>\n</template>\n\n<script>\nexport default {\n  name: 'join_page',\n  data () {\n    return {\n    }\n  }\n}\n</script>\n<style>\n  .join-page{\n    text-align: center;\n  }\n  .qq-group-img{\n    width: 100%;\n  }\n  .join-page-other-icon{\n    width: 20px;\n    vertical-align: middle;\n    margin-right: 6px;\n  }\n  .join-page-other{\n    text-align: left;\n  }\n  .join-page-other .ivu-btn{\n    margin-right: 6px;\n  }\n</style>\n"
  },
  {
    "path": "src/view/login/login.less",
    "content": ".login{\n    width: 100%;\n    height: 100%;\n    background-image: url('../../assets/images/login-bg.jpg');\n    background-size: cover;\n    background-position: center;\n    position: relative;\n    &-con{\n        position: absolute;\n        right: 160px;\n        top: 50%;\n        transform: translateY(-60%);\n        width: 300px;\n        &-header{\n            font-size: 16px;\n            font-weight: 300;\n            text-align: center;\n            padding: 30px 0;\n        }\n        .form-con{\n            padding: 10px 0 0;\n        }\n        .login-tip{\n            font-size: 10px;\n            text-align: center;\n            color: #c3c3c3;\n        }\n    }\n}\n"
  },
  {
    "path": "src/view/login/login.vue",
    "content": "<style lang=\"less\">\n  @import './login.less';\n</style>\n\n<template>\n  <div class=\"login\">\n    <div class=\"login-con\">\n      <Card icon=\"log-in\" title=\"欢迎登录\" :bordered=\"false\">\n        <div class=\"form-con\">\n          <login-form @on-success-valid=\"handleSubmit\"></login-form>\n          <p class=\"login-tip\">输入任意用户名和密码即可</p>\n        </div>\n      </Card>\n    </div>\n  </div>\n</template>\n\n<script>\nimport LoginForm from '_c/login-form'\nimport { mapActions } from 'vuex'\nexport default {\n  components: {\n    LoginForm\n  },\n  methods: {\n    ...mapActions([\n      'handleLogin',\n      'getUserInfo'\n    ]),\n    handleSubmit ({ userName, password }) {\n      this.handleLogin({ userName, password }).then(res => {\n        this.getUserInfo().then(res => {\n          this.$router.push({\n            name: this.$config.homeName\n          })\n        })\n      })\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/multilevel/level-2-1.vue",
    "content": "<template>\n  <div>多级菜单 -> 二级-1</div>\n</template>\n<script>\nexport default {\n  name: 'level_2_1'\n}\n</script>\n"
  },
  {
    "path": "src/view/multilevel/level-2-2/level-2-2-1.vue",
    "content": "<template>\n  <div>\n    <h3>多级菜单 -> 二级-2 -> 3级1</h3>\n    <Input v-model=\"val\" style=\"width: 200px\"></Input>\n  </div>\n</template>\n<script>\nexport default {\n  name: 'level_2_2_1',\n  data () {\n    return {\n      val: ''\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/multilevel/level-2-2/level-2-2-2.vue",
    "content": "<template>\n  <div>\n    <h3>多级菜单 -> 二级-2 -> 3级2</h3>\n    <Input v-model=\"val\" style=\"width: 200px\"></Input>\n  </div>\n</template>\n<script>\nexport default {\n  name: 'level_2_2_2',\n  data () {\n    return {\n      val: ''\n    }\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/multilevel/level-2-3.vue",
    "content": "<template>\n  <div>多级菜单 -> 二级-3</div>\n</template>\n<script>\nexport default {\n  name: 'level_2_3'\n}\n</script>\n"
  },
  {
    "path": "src/view/single-page/error-logger.vue",
    "content": "<template>\n  <div>\n    <Button @click=\"exportData\" type=\"primary\" style=\"margin: 0 10px 10px 0;\">导出日志记录</Button>\n    <b>注：这里只会显示成功保存到服务端的错误日志，而且页面错误日志不会在浏览器持久化存储，刷新页面即会丢失</b>\n    <Table ref=\"table\" :columns=\"columns\" :data=\"errorList\"></Table>\n  </div>\n</template>\n\n<script>\nimport dayjs from 'dayjs'\nimport { mapMutations } from 'vuex'\nexport default {\n  name: 'error_logger_page',\n  data () {\n    return {\n      columns: [\n        {\n          type: 'index',\n          title: '序号',\n          width: 100\n        },\n        {\n          key: 'type',\n          title: '类型',\n          width: 100,\n          render: (h, { row }) => {\n            return (\n              <div>\n                <icon size={16} type={row.type === 'ajax' ? 'md-link' : 'md-code-working'}></icon>\n              </div>\n            )\n          }\n        },\n        {\n          key: 'code',\n          title: '编码',\n          render: (h, { row }) => {\n            return (\n              <span>{ row.code === 0 ? '-' : row.code }</span>\n            )\n          }\n        },\n        {\n          key: 'mes',\n          title: '信息'\n        },\n        {\n          key: 'url',\n          title: 'URL'\n        },\n        {\n          key: 'time',\n          title: '时间',\n          render: (h, { row }) => {\n            return (\n              <span>{ dayjs(row.time).format('YYYY-MM-DD HH:mm:ss') }</span>\n            )\n          },\n          sortable: true,\n          sortType: 'desc'\n        }\n      ]\n    }\n  },\n  computed: {\n    errorList () {\n      return this.$store.state.app.errorList\n    }\n  },\n  methods: {\n    ...mapMutations([\n      'setHasReadErrorLoggerStatus'\n    ]),\n    exportData () {\n      this.$refs.table.exportCsv({\n        filename: '错误日志.csv'\n      })\n    }\n  },\n  activated () {\n    this.setHasReadErrorLoggerStatus()\n  },\n  mounted () {\n    this.setHasReadErrorLoggerStatus()\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/single-page/home/example.vue",
    "content": "<template>\n    <div ref=\"dom\"></div>\n</template>\n\n<script>\nimport echarts from 'echarts'\nimport { on, off } from '@/libs/tools'\nexport default {\n  name: 'serviceRequests',\n  data () {\n    return {\n      dom: null\n    }\n  },\n  methods: {\n    resize () {\n      this.dom.resize()\n    }\n  },\n  mounted () {\n    const option = {\n      tooltip: {\n        trigger: 'axis',\n        axisPointer: {\n          type: 'cross',\n          label: {\n            backgroundColor: '#6a7985'\n          }\n        }\n      },\n      grid: {\n        top: '3%',\n        left: '1.2%',\n        right: '1%',\n        bottom: '3%',\n        containLabel: true\n      },\n      xAxis: [\n        {\n          type: 'category',\n          boundaryGap: false,\n          data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']\n        }\n      ],\n      yAxis: [\n        {\n          type: 'value'\n        }\n      ],\n      series: [\n        {\n          name: '运营商/网络服务',\n          type: 'line',\n          stack: '总量',\n          areaStyle: { normal: {\n            color: '#2d8cf0'\n          } },\n          data: [120, 132, 101, 134, 90, 230, 210]\n        },\n        {\n          name: '银行/证券',\n          type: 'line',\n          stack: '总量',\n          areaStyle: { normal: {\n            color: '#10A6FF'\n          } },\n          data: [257, 358, 278, 234, 290, 330, 310]\n        },\n        {\n          name: '游戏/视频',\n          type: 'line',\n          stack: '总量',\n          areaStyle: { normal: {\n            color: '#0C17A6'\n          } },\n          data: [379, 268, 354, 269, 310, 478, 358]\n        },\n        {\n          name: '餐饮/外卖',\n          type: 'line',\n          stack: '总量',\n          areaStyle: { normal: {\n            color: '#4608A6'\n          } },\n          data: [320, 332, 301, 334, 390, 330, 320]\n        },\n        {\n          name: '快递/电商',\n          type: 'line',\n          stack: '总量',\n          label: {\n            normal: {\n              show: true,\n              position: 'top'\n            }\n          },\n          areaStyle: { normal: {\n            color: '#398DBF'\n          } },\n          data: [820, 645, 546, 745, 872, 624, 258]\n        }\n      ]\n    }\n    this.$nextTick(() => {\n      this.dom = echarts.init(this.$refs.dom)\n      this.dom.setOption(option)\n      on(window, 'resize', this.resize)\n    })\n  },\n  beforeDestroy () {\n    off(window, 'resize', this.resize)\n  }\n}\n</script>\n"
  },
  {
    "path": "src/view/single-page/home/home.vue",
    "content": "<template>\n  <div>\n    <Row :gutter=\"20\">\n      <i-col :xs=\"12\" :md=\"8\" :lg=\"4\" v-for=\"(infor, i) in inforCardData\" :key=\"`infor-${i}`\" style=\"height: 120px;padding-bottom: 10px;\">\n        <infor-card shadow :color=\"infor.color\" :icon=\"infor.icon\" :icon-size=\"36\">\n          <count-to :end=\"infor.count\" count-class=\"count-style\"/>\n          <p>{{ infor.title }}</p>\n        </infor-card>\n      </i-col>\n    </Row>\n    <Row :gutter=\"20\" style=\"margin-top: 10px;\">\n      <i-col :md=\"24\" :lg=\"8\" style=\"margin-bottom: 20px;\">\n        <Card shadow>\n          <chart-pie style=\"height: 300px;\" :value=\"pieData\" text=\"用户访问来源\"></chart-pie>\n        </Card>\n      </i-col>\n      <i-col :md=\"24\" :lg=\"16\" style=\"margin-bottom: 20px;\">\n        <Card shadow>\n          <chart-bar style=\"height: 300px;\" :value=\"barData\" text=\"每周用户活跃量\"/>\n        </Card>\n      </i-col>\n    </Row>\n    <Row>\n      <Card shadow>\n        <example style=\"height: 310px;\"/>\n      </Card>\n    </Row>\n  </div>\n</template>\n\n<script>\nimport InforCard from '_c/info-card'\nimport CountTo from '_c/count-to'\nimport { ChartPie, ChartBar } from '_c/charts'\nimport Example from './example.vue'\nexport default {\n  name: 'home',\n  components: {\n    InforCard,\n    CountTo,\n    ChartPie,\n    ChartBar,\n    Example\n  },\n  data () {\n    return {\n      inforCardData: [\n        { title: '新增用户', icon: 'md-person-add', count: 803, color: '#2d8cf0' },\n        { title: '累计点击', icon: 'md-locate', count: 232, color: '#19be6b' },\n        { title: '新增问答', icon: 'md-help-circle', count: 142, color: '#ff9900' },\n        { title: '分享统计', icon: 'md-share', count: 657, color: '#ed3f14' },\n        { title: '新增互动', icon: 'md-chatbubbles', count: 12, color: '#E46CBB' },\n        { title: '新增页面', icon: 'md-map', count: 14, color: '#9A66E4' }\n      ],\n      pieData: [\n        { value: 335, name: '直接访问' },\n        { value: 310, name: '邮件营销' },\n        { value: 234, name: '联盟广告' },\n        { value: 135, name: '视频广告' },\n        { value: 1548, name: '搜索引擎' }\n      ],\n      barData: {\n        Mon: 13253,\n        Tue: 34235,\n        Wed: 26321,\n        Thu: 12340,\n        Fri: 24643,\n        Sat: 1322,\n        Sun: 1324\n      }\n    }\n  },\n  mounted () {\n    //\n  }\n}\n</script>\n\n<style lang=\"less\">\n.count-style{\n  font-size: 50px;\n}\n</style>\n"
  },
  {
    "path": "src/view/single-page/home/index.js",
    "content": "import home from './home.vue'\nexport default home\n"
  },
  {
    "path": "src/view/single-page/message/index.vue",
    "content": "<template>\n  <Card shadow>\n    <div>\n      <div class=\"message-page-con message-category-con\">\n        <Menu width=\"auto\" active-name=\"unread\" @on-select=\"handleSelect\">\n          <MenuItem name=\"unread\">\n            <span class=\"category-title\">未读消息</span><Badge style=\"margin-left: 10px\" :count=\"messageUnreadCount\"></Badge>\n          </MenuItem>\n          <MenuItem name=\"readed\">\n            <span class=\"category-title\">已读消息</span><Badge style=\"margin-left: 10px\" class-name=\"gray-dadge\" :count=\"messageReadedCount\"></Badge>\n          </MenuItem>\n          <MenuItem name=\"trash\">\n            <span class=\"category-title\">回收站</span><Badge style=\"margin-left: 10px\" class-name=\"gray-dadge\" :count=\"messageTrashCount\"></Badge>\n          </MenuItem>\n        </Menu>\n      </div>\n      <div class=\"message-page-con message-list-con\">\n        <Spin fix v-if=\"listLoading\" size=\"large\"></Spin>\n        <Menu\n          width=\"auto\"\n          active-name=\"\"\n          :class=\"titleClass\"\n          @on-select=\"handleView\"\n        >\n          <MenuItem v-for=\"item in messageList\" :name=\"item.msg_id\" :key=\"`msg_${item.msg_id}`\">\n            <div>\n              <p class=\"msg-title\">{{ item.title }}</p>\n              <Badge status=\"default\" :text=\"item.create_time\" />\n              <Button\n                style=\"float: right;margin-right: 20px;\"\n                :style=\"{ display: item.loading ? 'inline-block !important' : '' }\"\n                :loading=\"item.loading\"\n                size=\"small\"\n                :icon=\"currentMessageType === 'readed' ? 'md-trash' : 'md-redo'\"\n                :title=\"currentMessageType === 'readed' ? '删除' : '还原'\"\n                type=\"text\"\n                v-show=\"currentMessageType !== 'unread'\"\n                @click.native.stop=\"removeMsg(item)\"></Button>\n            </div>\n          </MenuItem>\n        </Menu>\n      </div>\n      <div class=\"message-page-con message-view-con\">\n        <Spin fix v-if=\"contentLoading\" size=\"large\"></Spin>\n        <div class=\"message-view-header\">\n          <h2 class=\"message-view-title\">{{ showingMsgItem.title }}</h2>\n          <time class=\"message-view-time\">{{ showingMsgItem.create_time }}</time>\n        </div>\n        <div v-html=\"messageContent\"></div>\n      </div>\n    </div>\n  </Card>\n</template>\n\n<script>\nimport { mapState, mapGetters, mapMutations, mapActions } from 'vuex'\nconst listDic = {\n  unread: 'messageUnreadList',\n  readed: 'messageReadedList',\n  trash: 'messageTrashList'\n}\nexport default {\n  name: 'message_page',\n  data () {\n    return {\n      listLoading: true,\n      contentLoading: false,\n      currentMessageType: 'unread',\n      messageContent: '',\n      showingMsgItem: {}\n    }\n  },\n  computed: {\n    ...mapState({\n      messageUnreadList: state => state.user.messageUnreadList,\n      messageReadedList: state => state.user.messageReadedList,\n      messageTrashList: state => state.user.messageTrashList,\n      messageList () {\n        return this[listDic[this.currentMessageType]]\n      },\n      titleClass () {\n        return {\n          'not-unread-list': this.currentMessageType !== 'unread'\n        }\n      }\n    }),\n    ...mapGetters([\n      'messageUnreadCount',\n      'messageReadedCount',\n      'messageTrashCount'\n    ])\n  },\n  methods: {\n    ...mapMutations([\n      //\n    ]),\n    ...mapActions([\n      'getContentByMsgId',\n      'getMessageList',\n      'hasRead',\n      'removeReaded',\n      'restoreTrash'\n    ]),\n    stopLoading (name) {\n      this[name] = false\n    },\n    handleSelect (name) {\n      this.currentMessageType = name\n    },\n    handleView (msg_id) {\n      this.contentLoading = true\n      this.getContentByMsgId({ msg_id }).then(content => {\n        this.messageContent = content\n        const item = this.messageList.find(item => item.msg_id === msg_id)\n        if (item) this.showingMsgItem = item\n        if (this.currentMessageType === 'unread') this.hasRead({ msg_id })\n        this.stopLoading('contentLoading')\n      }).catch(() => {\n        this.stopLoading('contentLoading')\n      })\n    },\n    removeMsg (item) {\n      item.loading = true\n      const msg_id = item.msg_id\n      if (this.currentMessageType === 'readed') this.removeReaded({ msg_id })\n      else this.restoreTrash({ msg_id })\n    }\n  },\n  mounted () {\n    this.listLoading = true\n    // 请求获取消息列表\n    this.getMessageList().then(() => this.stopLoading('listLoading')).catch(() => this.stopLoading('listLoading'))\n  }\n}\n</script>\n\n<style lang=\"less\">\n.message-page{\n  &-con{\n    height: ~\"calc(100vh - 176px)\";\n    display: inline-block;\n    vertical-align: top;\n    position: relative;\n    &.message-category-con{\n      border-right: 1px solid #e6e6e6;\n      width: 200px;\n    }\n    &.message-list-con{\n      border-right: 1px solid #e6e6e6;\n      width: 230px;\n    }\n    &.message-view-con{\n      position: absolute;\n      left: 446px;\n      top: 16px;\n      right: 16px;\n      bottom: 16px;\n      overflow: auto;\n      padding: 12px 20px 0;\n      .message-view-header{\n        margin-bottom: 20px;\n        .message-view-title{\n          display: inline-block;\n        }\n        .message-view-time{\n          margin-left: 20px;\n        }\n      }\n    }\n    .category-title{\n      display: inline-block;\n      width: 65px;\n    }\n    .gray-dadge{\n      background: gainsboro;\n    }\n    .not-unread-list{\n      .msg-title{\n        color: rgb(170, 169, 169);\n      }\n      .ivu-menu-item{\n        .ivu-btn.ivu-btn-text.ivu-btn-small.ivu-btn-icon-only{\n          display: none;\n        }\n        &:hover{\n          .ivu-btn.ivu-btn-text.ivu-btn-small.ivu-btn-icon-only{\n            display: inline-block;\n          }\n        }\n      }\n    }\n  }\n}\n</style>\n"
  },
  {
    "path": "src/view/tools-methods/tools-methods.vue",
    "content": "<template>\n  <div>\n    <Card shadow>\n      <Row>\n        <i-col span=\"4\">\n          <Button @click=\"createTagParams\">添加一个标签</Button>\n        </i-col>\n        <i-col span=\"20\">\n          <p>动态路由，添加params</p>\n        </i-col>\n      </Row>\n    </Card>\n    <Card shadow style=\"margin-top: 10px;\">\n      <Row>\n        <i-col span=\"4\">\n          <Button @click=\"createTagQuery\">添加一个标签</Button>\n        </i-col>\n        <i-col span=\"20\">\n          <p>动态路由，添加query</p>\n        </i-col>\n      </Row>\n    </Card>\n    <Card shadow style=\"margin-top: 10px;\">\n      <Row>\n        <i-col span=\"4\">\n          <Button @click=\"handleCloseTag\">关闭工具方法页</Button>\n        </i-col>\n        <i-col span=\"20\">\n          <p>手动关闭页面</p>\n        </i-col>\n      </Row>\n    </Card>\n  </div>\n</template>\n\n<script>\nimport { mapMutations } from 'vuex'\nexport default {\n  name: 'tools_methods_page',\n  methods: {\n    ...mapMutations([\n      'closeTag'\n    ]),\n    createTagParams () {\n      const id = parseInt(Math.random() * 100000)\n      const route = {\n        name: 'params',\n        params: {\n          id\n        },\n        meta: {\n          title: `动态路由-${id}`\n        }\n      }\n      this.$router.push(route)\n    },\n    createTagQuery () {\n      const id = parseInt(Math.random() * 100000)\n      const route = {\n        name: 'query',\n        query: {\n          id\n        },\n        meta: {\n          title: `参数-${id}`\n        }\n      }\n      this.$router.push(route)\n    },\n    handleCloseTag () {\n      this.closeTag({\n        name: 'tools_methods_page'\n      })\n    }\n  }\n}\n</script>\n\n<style>\n\n</style>\n"
  },
  {
    "path": "src/view/update/update-paste.vue",
    "content": "<template>\n  <Row :gutter=\"10\">\n    <i-col span=\"12\">\n      <Card>\n        <div class=\"update-paste-con\">\n          <paste-editor v-model=\"pasteDataArr\" @on-success=\"handleSuccess\" @on-error=\"handleError\"/>\n        </div>\n        <div class=\"update-paste-btn-con\">\n          <span class=\"paste-tip\">使用Tab键换列，使用回车键换行</span>\n          <Button type=\"primary\" style=\"float: right;\" @click=\"handleShow\">显示表格数据</Button>\n        </div>\n      </Card>\n    </i-col>\n    <i-col span=\"12\">\n      <Card>\n        <Table :height=\"400\" :columns=\"columns\" :data=\"tableData\"/>\n      </Card>\n    </i-col>\n  </Row>\n</template>\n\n<script>\nimport PasteEditor from '_c/paste-editor'\nimport { getTableDataFromArray } from '@/libs/util'\nexport default {\n  name: 'update_paste_page',\n  components: {\n    PasteEditor\n  },\n  data () {\n    return {\n      pasteDataArr: [],\n      columns: [],\n      tableData: [],\n      validated: true,\n      errorIndex: 0\n    }\n  },\n  methods: {\n    handleSuccess () {\n      this.validated = true\n    },\n    handleError (index) {\n      this.validated = false\n      this.errorIndex = index\n    },\n    handleShow () {\n      if (!this.validated) {\n        this.$Notice.error({\n          title: '您的内容不规范',\n          desc: `您的第${this.errorIndex + 1}行数据不规范，请修改`\n        })\n      } else {\n        let { columns, tableData } = getTableDataFromArray(this.pasteDataArr)\n        this.columns = columns\n        this.tableData = tableData\n      }\n    }\n  }\n}\n</script>\n\n<style lang=\"less\">\n.update-paste{\n  &-con{\n    height: 350px;\n  }\n  &-btn-con{\n    box-sizing: content-box;\n    height: 30px;\n    padding: 15px 0 5px;\n  }\n}\n.paste-tip{\n  color: #19be6b;\n}\n</style>\n"
  },
  {
    "path": "src/view/update/update-table.vue",
    "content": "<template>\n  <Row :gutter=\"10\">\n    <i-col span=\"6\">\n      <Card>\n        <Upload action=\"\" :before-upload=\"beforeUpload\">\n          <Button icon=\"ios-cloud-upload-outline\">上传Csv文件</Button>\n          &nbsp;&nbsp;&nbsp;&nbsp;点击上传Csv文件\n        </Upload>\n        <p>util.js提供两个方法用来实现这个功能：</p>\n        <p class=\"update-table-intro\"><Icon style=\"margin-right: 10px;\" :size=\"10\" type=\"md-heart\"/><span class=\"code-high-line\">getArrayFromFile</span>：将Csv文件解析为二维数组</p>\n        <p class=\"update-table-intro\"><Icon style=\"margin-right: 10px;\" :size=\"10\" type=\"md-heart\"/><span class=\"code-high-line\">getTableDataFromArray</span>：将二维数组转为表格数据，具体请看文档</p>\n      </Card>\n    </i-col>\n    <i-col span=\"18\">\n      <Table :height=\"500\" :columns=\"columns\" :data=\"tableData\"/>\n    </i-col>\n  </Row>\n</template>\n\n<script>\nimport { getArrayFromFile, getTableDataFromArray } from '@/libs/util'\nexport default {\n  name: 'update_table_page',\n  data () {\n    return {\n      columns: [],\n      tableData: []\n    }\n  },\n  methods: {\n    beforeUpload (file) {\n      getArrayFromFile(file).then(data => {\n        let { columns, tableData } = getTableDataFromArray(data)\n        this.columns = columns\n        this.tableData = tableData\n      }).catch(() => {\n        this.$Notice.warning({\n          title: '只能上传Csv文件',\n          desc: '只能上传Csv文件，请重新上传'\n        })\n      })\n      return false\n    }\n  }\n}\n</script>\n\n<style>\n.update-table-intro{\n  margin-top: 10px;\n}\n.code-high-line{\n  color: #2d8cf0;\n}\n</style>\n"
  },
  {
    "path": "tests/e2e/.eslintrc",
    "content": "{\n  \"plugins\": [\n    \"cypress\"\n  ],\n  \"env\": {\n    \"mocha\": true,\n    \"cypress/globals\": true\n  },\n  \"rules\": {\n    \"strict\": \"off\"\n  }\n}\n"
  },
  {
    "path": "tests/e2e/plugins/index.js",
    "content": "// https://docs.cypress.io/guides/guides/plugins-guide.html\n\nmodule.exports = (on, config) => Object.assign({}, config, {\n  fixturesFolder: 'tests/e2e/fixtures',\n  integrationFolder: 'tests/e2e/specs',\n  screenshotsFolder: 'tests/e2e/screenshots',\n  videosFolder: 'tests/e2e/videos',\n  supportFile: 'tests/e2e/support/index.js'\n})\n"
  },
  {
    "path": "tests/e2e/specs/test.js",
    "content": "// https://docs.cypress.io/api/introduction/api.html\n\ndescribe('My First Test', () => {\n  it('Visits the app root url', () => {\n    cy.visit('/')\n    cy.contains('h1', 'Welcome to Your Vue.js App')\n  })\n})\n"
  },
  {
    "path": "tests/e2e/support/commands.js",
    "content": "// ***********************************************\n// This example commands.js shows you how to\n// create various custom commands and overwrite\n// existing commands.\n//\n// For more comprehensive examples of custom\n// commands please read more here:\n// https://on.cypress.io/custom-commands\n// ***********************************************\n//\n//\n// -- This is a parent command --\n// Cypress.Commands.add(\"login\", (email, password) => { ... })\n//\n//\n// -- This is a child command --\n// Cypress.Commands.add(\"drag\", { prevSubject: 'element'}, (subject, options) => { ... })\n//\n//\n// -- This is a dual command --\n// Cypress.Commands.add(\"dismiss\", { prevSubject: 'optional'}, (subject, options) => { ... })\n//\n//\n// -- This is will overwrite an existing command --\n// Cypress.Commands.overwrite(\"visit\", (originalFn, url, options) => { ... })\n"
  },
  {
    "path": "tests/e2e/support/index.js",
    "content": "// ***********************************************************\n// This example support/index.js is processed and\n// loaded automatically before your test files.\n//\n// This is a great place to put global configuration and\n// behavior that modifies Cypress.\n//\n// You can change the location of this file or turn off\n// automatically serving support files with the\n// 'supportFile' configuration option.\n//\n// You can read more here:\n// https://on.cypress.io/configuration\n// ***********************************************************\n\n// Import commands.js using ES2015 syntax:\nimport './commands'\n\n// Alternatively you can use CommonJS syntax:\n// require('./commands')\n"
  },
  {
    "path": "tests/unit/.eslintrc.js",
    "content": "module.exports = {\n  env: {\n    mocha: true\n  },\n  rules: {\n    'import/no-extraneous-dependencies': 'off'\n  }\n}\n"
  },
  {
    "path": "tests/unit/HelloWorld.spec.js",
    "content": "import { expect } from 'chai'\nimport { shallow } from '@vue/test-utils'\nimport HelloWorld from '@/components/HelloWorld.vue'\n\ndescribe('HelloWorld.vue', () => {\n  it('renders props.msg when passed', () => {\n    const msg = 'new message'\n    const wrapper = shallow(HelloWorld, {\n      propsData: { msg }\n    })\n    expect(wrapper.text()).to.include(msg)\n  })\n})\n"
  },
  {
    "path": "vue.config.js",
    "content": "const path = require('path')\n\nconst resolve = dir => {\n  return path.join(__dirname, dir)\n}\n\n// 项目部署基础\n// 默认情况下，我们假设你的应用将被部署在域的根目录下,\n// 例如：https://www.my-app.com/\n// 默认：'/'\n// 如果您的应用程序部署在子路径中，则需要在这指定子路径\n// 例如：https://www.foobar.com/my-app/\n// 需要将它改为'/my-app/'\n// iview-admin线上演示打包路径： https://file.iviewui.com/admin-dist/\nconst BASE_URL = process.env.NODE_ENV === 'production'\n  ? '/'\n  : '/'\n\nmodule.exports = {\n  // Project deployment base\n  // By default we assume your app will be deployed at the root of a domain,\n  // e.g. https://www.my-app.com/\n  // If your app is deployed at a sub-path, you will need to specify that\n  // sub-path here. For example, if your app is deployed at\n  // https://www.foobar.com/my-app/\n  // then change this to '/my-app/'\n  baseUrl: BASE_URL,\n  // tweak internal webpack configuration.\n  // see https://github.com/vuejs/vue-cli/blob/dev/docs/webpack.md\n  // 如果你不需要使用eslint，把lintOnSave设为false即可\n  lintOnSave: true,\n  chainWebpack: config => {\n    config.resolve.alias\n      .set('@', resolve('src')) // key,value自行定义，比如.set('@@', resolve('src/components'))\n      .set('_c', resolve('src/components'))\n  },\n  // 设为false打包时不生成.map文件\n  productionSourceMap: false\n  // 这里写你调用接口的基础路径，来解决跨域，如果设置了代理，那你本地开发环境的axios的baseUrl要写为 '' ，即空字符串\n  // devServer: {\n  //   proxy: 'localhost:3000'\n  // }\n}\n"
  }
]