first commit

This commit is contained in:
shawnzhang 2019-02-12 17:46:33 +08:00
commit 6a2b662b88
346 changed files with 34065 additions and 0 deletions

17
.babelrc Normal file
View file

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

14
.editorconfig Normal file
View file

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

6
.eslintignore Normal file
View file

@ -0,0 +1,6 @@
build/*.js
config/*.js
src/assets
src/views/home
src/views/Repair
src/views/Employee

195
.eslintrc.js Normal file
View file

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

21
.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
**/*.log
test/unit/coverage
test/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
package-lock.json

10
.postcssrc.js Normal file
View file

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

5
.travis.yml Normal file
View file

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

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present PanJiaChen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

219
README.md Normal file
View file

@ -0,0 +1,219 @@
<p align="center">
<img width="320" src="https://wpimg.wallstcn.com/ecc53a42-d79b-42e2-8852-5126b810a4c8.svg">
</p>
<p align="center">
<a href="https://github.com/vuejs/vue">
<img src="https://img.shields.io/badge/vue-2.5.17-brightgreen.svg" alt="vue">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.4.6-brightgreen.svg" alt="element-ui">
</a>
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/releases">
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
</a>
<a href="https://gitter.im/vue-element-admin/discuss">
<img src="https://badges.gitter.im/Join%20Chat.svg" alt="gitter">
</a>
<a href="https://panjiachen.github.io/vue-element-admin-site/donate">
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
</a>
</p>
English | [简体中文](./README.zh-CN.md)
## Introduction
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) is a front-end management background integration solution. It based on [vue](https://github.com/vuejs/vue) and use the UI Toolkit [element](https://github.com/ElemeFE/element).
It is a magical vue admin based on the newest development stack of vue, built-in i18n solution, typical templates for enterprise applications, lots of awesome features. It helps you build a large complex Single-Page Applications. I believe whatever your needs are, this project will help you.
- [Preview](http://panjiachen.github.io/vue-element-admin)
- [Documentation](https://panjiachen.github.io/vue-element-admin-site/)
- [Gitter](https://gitter.im/vue-element-admin/discuss)
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/donate/)
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
**This project is positioned as a background integration solution and is not suitable for secondary development as a basic template.**
- Base template recommends using: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- Desktop: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (Credits: [@Armour](https://github.com/Armour))
**This project does not support low version browsers (e.g. IE). Please add polyfill yourself if you need them.**
**Note: This project uses element-ui@2.3.0+ version, so the minimum compatible vue@2.5.0+**
**Start using `webpack4` from `v3.8.0`. If you still want to continue using `webpack3`, please use this branch [webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
## Preparation
You need to install [node](http://nodejs.org/) and [git](https://git-scm.com/) locally. The project is based on [ES2015+](http://es6.ruanyifeng.com/), [vue](https://cn.vuejs.org/index.html), [vuex](https://vuex.vuejs.org/zh-cn/), [vue-router](https://router.vuejs.org/zh-cn/), [axios](https://github.com/axios/axios) and [element-ui](https://github.com/ElemeFE/element), all request data is simulated using [Mock.js](https://github.com/nuysoft/Mock).
Understanding and learning this knowledge in advance will greatly help the use of this project.
---
<p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p>
## Sponsors
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
## Features
```
- Login / Logout
- Permission Authentication
- Page permission
- Directive permission
- Two-step login
- Multi-environment build
- dev sit stage prod
- Global Features
- I18n
- Multiple dynamic themes
- Dynamic sidebar (supports multi-level routing)
- Dynamic breadcrumb
- Tags-view(Tab page Support right-click operation)
- Svg Sprite
- Mock data
- Screenfull
- Responsive Sidebar
- Editor
- Rich Text Editor
- Markdown Editor
- JSON Editor
- Excel
- Export Excel
- Export zip
- Upload Excel
- Visualization Excel
- Table
- Dynamic Table
- Drag And Drop Table
- Tree Table
- Inline Edit Table
- Error Page
- 401
- 404
- Components
- Avatar Upload
- Back To Top
- Drag Dialog
- Drag Select
- Drag Kanban
- Drag List
- SplitPane
- Dropzone
- Sticky
- CountTo
- Advanced Example
- Error Log
- Dashboard
- Guide Page
- ECharts
- Clipboard
- Markdown to html
```
## Getting started
```bash
# clone the project
git clone https://github.com/PanJiaChen/vue-element-admin.git
# install dependency
npm install
# develop
npm run dev
```
This will automatically open http://localhost:9527.
## Build
```bash
# build for test environment
npm run build:sit
# build for production environment
npm run build:prod
```
## Advanced
```bash
# --report to build with bundle size analytics
npm run build:prod --report
# --generate a bundle size analytics. default: bundle-report.html
npm run build:prod --generate_report
# --preview to start a server in local to preview
npm run build:prod --preview
# lint code
npm run lint
# auto fix
npm run lint -- --fix
```
Refer to [Documentation](https://panjiachen.github.io/vue-element-admin-site/guide/essentials/deploy.html) for more information
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## Online Demo
[Preview](http://panjiachen.github.io/vue-element-admin)
## Donate
If you find this project useful, you can buy author a glass of juice :tropical_drink:
![donate](https://wpimg.wallstcn.com/bd273f0d-83a0-4ef2-92e1-9ac8ed3746b9.png)
[Paypal Me](https://www.paypal.me/panfree23)
[Buy me a coffee](https://www.buymeacoffee.com/Pan)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
Copyright (c) 2017-present PanJiaChen

231
README.zh-CN.md Normal file
View file

@ -0,0 +1,231 @@
<p align="center">
<img width="320" src="https://wpimg.wallstcn.com/ecc53a42-d79b-42e2-8852-5126b810a4c8.svg">
</p>
<p align="center">
<a href="https://github.com/vuejs/vue">
<img src="https://img.shields.io/badge/vue-2.5.10-brightgreen.svg" alt="vue">
</a>
<a href="https://github.com/ElemeFE/element">
<img src="https://img.shields.io/badge/element--ui-2.3.2-brightgreen.svg" alt="element-ui">
</a>
<a href="https://travis-ci.org/PanJiaChen/vue-element-admin" rel="nofollow">
<img src="https://travis-ci.org/PanJiaChen/vue-element-admin.svg?branch=master" alt="Build Status">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/mashape/apistatus.svg" alt="license">
</a>
<a href="https://github.com/PanJiaChen/vue-element-admin/releases">
<img src="https://img.shields.io/github/release/PanJiaChen/vue-element-admin.svg" alt="GitHub release">
</a>
<a href="https://gitter.im/vue-element-admin/discuss">
<img src="https://badges.gitter.im/Join%20Chat.svg" alt="gitter">
</a>
<a href="https://panjiachen.gitee.io/vue-element-admin-site/zh/donate">
<img src="https://img.shields.io/badge/%24-donate-ff69b4.svg" alt="donate">
</a>
</p>
简体中文 | [English](./README.md)
## 简介
[vue-element-admin](http://panjiachen.github.io/vue-element-admin) 是一个后台集成解决方案,它基于 [vue](https://github.com/vuejs/vue) 和 [element](https://github.com/ElemeFE/element)。它使用了最新的前端技术栈,内置了 i18 国际化解决方案,动态路由,权限验证,提炼了典型的业务模型,提供了丰富的功能组件,它可以帮助你快速搭建企业级中后台产品原型。相信不管你的需求是什么,本项目都能帮助到你。
- [在线访问](http://panjiachen.github.io/vue-element-admin)
- [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
- [Gitter 讨论组](https://gitter.im/vue-element-admin/discuss)
- [Wiki](https://github.com/PanJiaChen/vue-element-admin/wiki)
- [Donate](https://panjiachen.github.io/vue-element-admin-site/zh/donate/)
- [Gitee](https://panjiachen.gitee.io/vue-element-admin/) 国内用户可访问该地址在线预览
- [国内访问文档](https://panjiachen.gitee.io/vue-element-admin-site/zh/) 方便没翻墙的用户查看文档
**本项目的定位是后台集成方案,不适合当基础模板来开发。**
- 模板建议使用: [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)
- 桌面端: [electron-vue-admin](https://github.com/PanJiaChen/electron-vue-admin)
- Typescript版: [vue-typescript-admin-template](https://github.com/Armour/vue-typescript-admin-template) (鸣谢: [@Armour](https://github.com/Armour))
群主 **[圈子](https://jianshiapp.com/circles/1209)** 楼主会经常分享一些技术相关的东西,或者加入[qq 群](https://github.com/PanJiaChen/vue-element-admin/issues/602)
**注意:该项目使用 element-ui@2.3.0+ 版本,所以最低兼容 vue@2.5.0+**
**从`v3.8.0`开始使用`webpack4`。所以若还想使用`webpack3`开发,请使用该分支[webpack3](https://github.com/PanJiaChen/vue-element-admin/tree/webpack3)**
**该项目不支持低版本浏览器(如 ie),有需求请自行添加 polyfill [详情](https://github.com/PanJiaChen/vue-element-admin/wiki#babel-polyfill)**
## 前序准备
你需要在本地安装 [node](http://nodejs.org/) 和 [git](https://git-scm.com/)。本项目技术栈基于 [ES2015+](http://es6.ruanyifeng.com/)、[vue](https://cn.vuejs.org/index.html)、[vuex](https://vuex.vuejs.org/zh-cn/)、[vue-router](https://router.vuejs.org/zh-cn/) 、[axios](https://github.com/axios/axios) 和 [element-ui](https://github.com/ElemeFE/element),所有的请求数据都使用[Mock.js](https://github.com/nuysoft/Mock)模拟,提前了解和学习这些知识会对使用本项目有很大的帮助。
同时配套一个系列的教程文章,如何从零构建后一个完整的后台项目,建议大家先看完这些文章再来实践本项目
- [手摸手,带你用 vue 撸后台 系列一(基础篇)](https://juejin.im/post/59097cd7a22b9d0065fb61d2)
- [手摸手,带你用 vue 撸后台 系列二(登录权限篇)](https://juejin.im/post/591aa14f570c35006961acac)
- [手摸手,带你用 vue 撸后台 系列三 (实战篇)](https://juejin.im/post/593121aa0ce4630057f70d35)
- [手摸手,带你用 vue 撸后台 系列四(vueAdmin 一个极简的后台基础模板)](https://juejin.im/post/595b4d776fb9a06bbe7dba56)
- [手摸手,带你封装一个 vue component](https://segmentfault.com/a/1190000009090836)
- [手摸手,带你优雅的使用 icon](https://juejin.im/post/59bb864b5188257e7a427c09)
- [手摸手,带你用合理的姿势使用 webpack4](https://juejin.im/post/5b56909a518825195f499806)
- [手摸手,带你用合理的姿势使用 webpack4](https://juejin.im/post/5b5d6d6f6fb9a04fea58aabc)
**如有问题请先看上述使用文档和文章,若不能满足,欢迎 issue 和 pr**
<p align="center">
<img width="900" src="https://wpimg.wallstcn.com/a5894c1b-f6af-456e-82df-1151da0839bf.png">
</p>
## Sponsors
Become a sponsor and get your logo on our README on GitHub with a link to your site. [[Become a sponsor]](https://www.patreon.com/panjiachen)
<a href="https://flatlogic.com/admin-dashboards?from=vue-element-admin"><img width="150px" src="https://wpimg.wallstcn.com/9c0b719b-5551-4c1e-b776-63994632d94a.png" /></a><p>Admin Dashboard Templates made with Vue, React and Angular.</p>
## 功能
```
- 登录 / 注销
- 权限验证
- 页面权限
- 指令权限
- 二步登录
- 多环境发布
- dev sit stage prod
- 全局功能
- 国际化多语言
- 多种动态换肤
- 动态侧边栏(支持多级路由嵌套)
- 动态面包屑
- 快捷导航(标签页)
- Svg Sprite 图标
- 本地mock数据
- Screenfull全屏
- 自适应收缩侧边栏
- 编辑器
- 富文本
- Markdown
- JSON 等多格式
- Excel
- 导出excel
- 导出zip
- 导入excel
- 前端可视化excel
- 表格
- 动态表格
- 拖拽表格
- 树形表格
- 内联编辑
- 错误页面
- 401
- 404
- 組件
- 头像上传
- 返回顶部
- 拖拽Dialog
- 拖拽Select
- 拖拽看板
- 列表拖拽
- SplitPane
- Dropzone
- Sticky
- CountTo
- 综合实例
- 错误日志
- Dashboard
- 引导页
- ECharts 图表
- Clipboard(剪贴复制)
- Markdown2html
```
## 开发
```bash
# 克隆项目
git clone https://github.com/PanJiaChen/vue-element-admin.git
# 安装依赖
npm install
# 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org
# 启动服务
npm run dev
```
浏览器访问 http://localhost:9527
## 发布
```bash
# 构建测试环境
npm run build:sit
# 构建生产环境
npm run build:prod
```
## 其它
```bash
# --report to build with bundle size analytics
npm run build:prod
# --generate a bundle size analytics. default: bundle-report.html
npm run build:prod --generate_report
# --preview to start a server in local to preview
npm run build:prod --preview
# lint code
npm run lint
# auto fix
npm run lint -- --fix
```
更多信息请参考 [使用文档](https://panjiachen.github.io/vue-element-admin-site/zh/)
## Changelog
Detailed changes for each release are documented in the [release notes](https://github.com/PanJiaChen/vue-element-admin/releases).
## Online Demo
[在线 Demo](http://panjiachen.github.io/vue-element-admin)
## Donate
如果你觉得这个项目帮助到了你,你可以帮作者买一杯果汁表示鼓励 :tropical_drink:
![donate](https://panjiachen.github.io/donate/donation.png)
[Paypal Me](https://www.paypal.me/panfree23)
## Browsers support
Modern browsers and Internet Explorer 10+.
| [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/edge/edge_48x48.png" alt="IE / Edge" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>IE / Edge | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/firefox/firefox_48x48.png" alt="Firefox" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Firefox | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/chrome/chrome_48x48.png" alt="Chrome" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Chrome | [<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/src/safari/safari_48x48.png" alt="Safari" width="24px" height="24px" />](http://godban.github.io/browsers-support-badges/)</br>Safari |
| --------- | --------- | --------- | --------- |
| IE10, IE11, Edge| last 2 versions| last 2 versions| last 2 versions
## License
[MIT](https://github.com/PanJiaChen/vue-element-admin/blob/master/LICENSE)
Copyright (c) 2017-present PanJiaChen

67
build/build.js Normal file
View file

@ -0,0 +1,67 @@
'use strict'
require('./check-versions')()
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
var connect = require('connect')
var serveStatic = require('serve-static')
const spinner = ora(
'building for ' + process.env.env_config + ' environment...'
)
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(
stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false
}) + '\n\n'
)
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(
chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
" Opening index.html over file:// won't work.\n"
)
)
if (process.env.npm_config_preview) {
const port = 9526
const host = 'http://localhost:' + port
const basePath = config.build.assetsPublicPath
const app = connect()
app.use(
basePath,
serveStatic('./dist', {
index: ['index.html', '/']
})
)
app.listen(port, function() {
console.log(
chalk.green(`> Listening at http://localhost:${port}${basePath}`)
)
})
}
})
})

64
build/check-versions.js Normal file
View file

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

BIN
build/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

108
build/utils.js Normal file
View file

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

5
build/vue-loader.conf.js Normal file
View file

@ -0,0 +1,5 @@
'use strict'
module.exports = {
//You can set the vue-loader configuration by yourself.
}

107
build/webpack.base.conf.js Normal file
View file

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

98
build/webpack.dev.conf.js Normal file
View file

@ -0,0 +1,98 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
mode: 'development',
module: {
rules: utils.styleLoaders({
sourceMap: config.dev.cssSourceMap,
usePostCSS: true
})
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: true,
hot: true,
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
templateParameters: {
BASE_URL: config.dev.assetsPublicPath + config.dev.assetsSubDirectory,
},
}),
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(
new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [
`Your application is running here: http://${
devWebpackConfig.devServer.host
}:${port}`
]
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
})
)
resolve(devWebpackConfig)
}
})
})

188
build/webpack.prod.conf.js Normal file
View file

@ -0,0 +1,188 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
function resolve(dir) {
return path.join(__dirname, '..', dir)
}
const env = require('../config/' + process.env.env_config + '.env')
// For NamedChunksPlugin
const seen = new Set()
const nameLength = 4
const webpackConfig = merge(baseWebpackConfig, {
mode: 'production',
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash:8].js'),
chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
// extract css into its own file
new MiniCssExtractPlugin({
filename: utils.assetsPath('css/[name].[contenthash:8].css'),
chunkFilename: utils.assetsPath('css/[name].[contenthash:8].css')
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
favicon: resolve('favicon.ico'),
title: 'vue-element-admin',
templateParameters: {
BASE_URL: config.build.assetsPublicPath + config.build.assetsSubDirectory,
},
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
}
// default sort mode uses toposort which cannot handle cyclic deps
// in certain cases, and in webpack 4, chunk order in HTML doesn't
// matter anyway
}),
new ScriptExtHtmlWebpackPlugin({
//`runtime` must same as runtimeChunk name. default is `runtime`
inline: /runtime\..*\.js$/
}),
// keep chunk.id stable when chunk has no name
new webpack.NamedChunksPlugin(chunk => {
if (chunk.name) {
return chunk.name
}
const modules = Array.from(chunk.modulesIterable)
if (modules.length > 1) {
const hash = require('hash-sum')
const joinedHash = hash(modules.map(m => m.id).join('_'))
let len = nameLength
while (seen.has(joinedHash.substr(0, len))) len++
seen.add(joinedHash.substr(0, len))
return `chunk-${joinedHash.substr(0, len)}`
} else {
return modules[0].id
}
}),
// keep module.id stable when vender modules does not change
new webpack.HashedModuleIdsPlugin(),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
],
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\\/]node_modules[\\/]/,
priority: 10,
chunks: 'initial' // 只打包初始时依赖的第三方
},
elementUI: {
name: 'chunk-elementUI', // 单独将 elementUI 拆包
priority: 20, // 权重要大于 libs 和 app 不然会被打包进 libs 或者 app
test: /[\\/]node_modules[\\/]element-ui[\\/]/
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // 可自定义拓展你的规则
minChunks: 3, // 最小公用次数
priority: 5,
reuseExistingChunk: true
}
}
},
runtimeChunk: 'single',
minimizer: [
new UglifyJsPlugin({
uglifyOptions: {
mangle: {
safari10: true
}
},
sourceMap: config.build.productionSourceMap,
cache: true,
parallel: true
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSAssetsPlugin()
]
}
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' + config.build.productionGzipExtensions.join('|') + ')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.generateAnalyzerReport || config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer')
.BundleAnalyzerPlugin
if (config.build.bundleAnalyzerReport) {
webpackConfig.plugins.push(
new BundleAnalyzerPlugin({
analyzerPort: 8080,
generateStatsFile: false
})
)
}
if (config.build.generateAnalyzerReport) {
webpackConfig.plugins.push(
new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false
})
)
}
}
module.exports = webpackConfig

6
config/dev.env.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
NODE_ENV: '"development"',
ENV_CONFIG: '"dev"',
// BASE_API: '"http://192.168.1.45:8080"'
BASE_API: '"http://153.3.252.58:9090"'
}

88
config/index.js Normal file
View file

@ -0,0 +1,88 @@
'use strict'
// Template version: 1.2.6
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
// can be overwritten by process.env.HOST
// if you want dev by ip, please set host: '0.0.0.0'
host: '192.168.1.38',
port: 9527, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: true,
errorOverlay: true,
notifyOnErrors: false,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
// Use Eslint Loader?
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-source-map',
// CSS Sourcemaps off by default because relative paths are "buggy"
// with this option, according to the CSS-Loader README
// (https://github.com/webpack/css-loader#sourcemaps)
// In our experience, they generally work as expected,
// just be aware of this issue when enabling this option.
cssSourceMap: false
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
/**
* You can set by youself according to actual condition
* You will need to set this if you plan to deploy your site under a sub path,
* for example GitHub pages. If you plan to deploy your site to https://foo.github.io/bar/,
* then assetsPublicPath should be set to "/bar/".
* In most cases please use '/' !!!
*/
assetsPublicPath: '/dist/',
/**
* Source Maps
*/
productionSourceMap: false,
// https://webpack.js.org/configuration/devtool/#production
devtool: 'source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build:prod --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report || false,
// `npm run build:prod --generate_report`
generateAnalyzerReport: process.env.npm_config_generate_report || false
}
}

6
config/prod.env.js Normal file
View file

@ -0,0 +1,6 @@
module.exports = {
NODE_ENV: '"production"',
ENV_CONFIG: '"prod"',
// BASE_API: '"http://www.beyond123.top"'
BASE_API: '"http://www.nwowapp.com:9090"'
}

5
config/sit.env.js Normal file
View file

@ -0,0 +1,5 @@
module.exports = {
NODE_ENV: '"production"',
ENV_CONFIG: '"sit"',
BASE_API: '"http://192.168.1.26:9090"'
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

16
index.html Normal file
View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>NWOW</title>
</head>
<body>
<script src=<%= BASE_URL %>/tinymce4.7.5/tinymce.min.js></script>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?key=AIzaSyCGoNyENiHEPc3QStdJyeeadB6XjLgYJCA"></script>
<script src=<%= BASE_URL %>/tinymce4.7.5/markerwithlabel.js></script>
<div id="app"></div>
</body>
</html>

133
package.json Normal file
View file

@ -0,0 +1,133 @@
{
"name": "vue-element-admin",
"version": "3.9.3",
"description": "A magical vue admin. Typical templates for enterprise applications. Newest development stack of vue. Lots of awesome features",
"author": "Pan <panfree23@gmail.com>",
"license": "MIT",
"scripts": {
"dev": "cross-env BABEL_ENV=development webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"build:prod": "cross-env NODE_ENV=production env_config=prod node build/build.js",
"build:sit": "cross-env NODE_ENV=production env_config=sit node build/build.js",
"lint": "eslint --ext .js,.vue src",
"test": "npm run lint",
"precommit": "lint-staged",
"svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
},
"lint-staged": {
"src/**/*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"keywords": [
"vue",
"element-ui",
"admin",
"management-system",
"admin-template"
],
"repository": {
"type": "git",
"url": "git+https://github.com/PanJiaChen/vue-element-admin.git"
},
"bugs": {
"url": "https://github.com/PanJiaChen/vue-element-admin/issues"
},
"dependencies": {
"axios": "0.18.0",
"clipboard": "1.7.1",
"codemirror": "5.39.2",
"connect": "3.6.6",
"driver.js": "0.5.2",
"dropzone": "5.2.0",
"echarts": "4.1.0",
"element-ui": "2.4.11",
"file-saver": "1.3.8",
"font-awesome": "4.7.0",
"js-cookie": "2.2.0",
"jsonlint": "1.6.3",
"jszip": "3.1.5",
"mockjs": "1.0.1-beta3",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"screenfull": "3.3.3",
"showdown": "1.8.6",
"simplemde": "1.11.2",
"sockjs-client": "^1.3.0",
"sortablejs": "1.7.0",
"vue": "2.5.17",
"vue-count-to": "1.0.13",
"vue-google-maps": "^0.1.21",
"vue-i18n": "7.3.2",
"vue-router": "3.0.1",
"vue-socket.io": "^3.0.4",
"vue-splitpane": "1.0.2",
"vuedraggable": "^2.16.0",
"vuex": "3.0.1",
"xlsx": "^0.11.16"
},
"devDependencies": {
"autoprefixer": "8.5.0",
"babel-core": "6.26.3",
"babel-eslint": "8.2.6",
"babel-helper-vue-jsx-merge-props": "2.0.3",
"babel-loader": "7.1.5",
"babel-plugin-dynamic-import-node": "2.0.0",
"babel-plugin-syntax-jsx": "6.18.0",
"babel-plugin-transform-runtime": "6.23.0",
"babel-plugin-transform-vue-jsx": "3.7.0",
"babel-preset-env": "1.7.0",
"babel-preset-stage-2": "6.24.1",
"chalk": "2.4.1",
"copy-webpack-plugin": "4.5.2",
"cross-env": "5.2.0",
"css-loader": "1.0.0",
"eslint": "4.19.1",
"eslint-friendly-formatter": "4.0.1",
"eslint-loader": "2.0.0",
"eslint-plugin-vue": "4.7.1",
"file-loader": "1.1.11",
"friendly-errors-webpack-plugin": "1.7.0",
"hash-sum": "1.0.2",
"html-webpack-plugin": "4.0.0-alpha",
"husky": "0.14.3",
"lint-staged": "7.2.2",
"mini-css-extract-plugin": "0.4.1",
"node-notifier": "5.2.1",
"optimize-css-assets-webpack-plugin": "5.0.0",
"ora": "3.0.0",
"path-to-regexp": "2.4.0",
"portfinder": "1.0.13",
"postcss-import": "11.1.0",
"postcss-loader": "2.1.6",
"postcss-url": "7.3.2",
"rimraf": "2.6.2",
"sass-loader": "^7.0.3",
"script-ext-html-webpack-plugin": "2.0.1",
"script-loader": "0.7.2",
"semver": "5.5.0",
"serve-static": "1.13.2",
"shelljs": "0.8.2",
"svg-sprite-loader": "3.8.0",
"svgo": "1.0.5",
"uglifyjs-webpack-plugin": "1.2.7",
"url-loader": "1.0.1",
"vue-loader": "15.3.0",
"vue-style-loader": "4.1.2",
"vue-template-compiler": "2.5.17",
"webpack": "4.16.5",
"webpack-bundle-analyzer": "2.13.1",
"webpack-cli": "3.1.0",
"webpack-dev-server": "3.1.5",
"webpack-merge": "4.1.4"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

11
src/App.vue Normal file
View file

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

96
src/api/area.js Normal file
View file

@ -0,0 +1,96 @@
import request from '@/utils/request'
// 区域列表
export function newdetailList(query) {
return request({
url: '/pss/region/newdetailList',
method: 'post',
params: query
})
}
// 增加区域
export function createarea(query) {
var params = new URLSearchParams()
params.append('name', query.name) // 你要传给后台的参数值 key/value
params.append('levle', query.levle) // 你要传给后台的参数值 key/value
params.append('parentid', query.parentid) // 你要传给后台的参数值 key/value
params.append('firstlevelid', query.firstlevelid) // 你要传给后台的参数值 key/value
params.append('createid', query.createid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/create',
method: 'post',
data: params
})
}
// 查询所有一级区域
export function listbyparentid(query) {
return request({
url: '/pss/region/listbyparentid',
method: 'post',
params: query
})
}
// 经理选择人
export function regionmanagercandidate(regionid) {
var params = new URLSearchParams()
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/regionmanagercandidate',
method: 'post',
data: params
})
}
// 增加二级区域
export function createarea2(name, levle, parentid, createid, regionmanagerid) {
var params = new URLSearchParams()
params.append('name', name) // 你要传给后台的参数值 key/value
params.append('levle', levle) // 你要传给后台的参数值 key/value
params.append('parentid', parentid) // 你要传给后台的参数值 key/value
params.append('createid', createid) // 你要传给后台的参数值 key/value
params.append('regionmanagerid', regionmanagerid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/create',
method: 'post',
data: params
})
}
// 删除区域
export function deleteregion(regionid) {
var params = new URLSearchParams()
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/deleteregion',
method: 'post',
data: params
})
}
// 根据级别查询上一级区域
export function getregionbylevel(level) {
var params = new URLSearchParams()
params.append('level', level) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/getregionbylevel',
method: 'post',
data: params
})
}
// 修改区域
export function updateregion(regionid, name, regionmanagerid, parentid) {
var params = new URLSearchParams()
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
params.append('name', name) // 你要传给后台的参数值 key/value
params.append('regionmanagerid', regionmanagerid) // 你要传给后台的参数值 key/value
params.append('parentid', parentid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/updateregion',
method: 'post',
data: params
})
}

41
src/api/article.js Normal file
View file

@ -0,0 +1,41 @@
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/article/list',
method: 'get',
params: query
})
}
export function fetchArticle(id) {
return request({
url: '/article/detail',
method: 'get',
params: { id }
})
}
export function fetchPv(pv) {
return request({
url: '/article/pv',
method: 'get',
params: { pv }
})
}
export function createArticle(data) {
return request({
url: '/article/create',
method: 'post',
data
})
}
export function updateArticle(data) {
return request({
url: '/article/update',
method: 'post',
data
})
}

89
src/api/bigdata.js Normal file
View file

@ -0,0 +1,89 @@
import request from '@/utils/request'
// 维修问题
export function sericecomponentcount(query) {
return request({
url: '/pss/servicecount/sericecomponentcount',
method: 'post',
params: query
})
}
// 维修完成率
export function serviceitemscount2(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('date', query.date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/servicecount/serviceitemscount2',
method: 'post',
data: params
})
}
// 维修延误统计
export function delaycount(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('date', query.date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/servicecount/delaycount',
method: 'post',
data: params
})
}
// 维修延误统计更多数据
export function delaycount2(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('date', query.date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/servicecount/delaycount1',
method: 'post',
data: params
})
}
// 维修延误统计更多数据
export function delaycount2more(date, repositoryid) {
var params = new URLSearchParams()
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
params.append('date', date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/servicecount/delaycount1',
method: 'post',
data: params
})
}
// 维修问题百分比
export function sericecomponentcount1(query) {
return request({
url: '/pss/servicecount/sericecomponentcount1',
method: 'post',
params: query
})
}
// 维修完成统计率
export function serviceitemscount3(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('date', query.date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/servicecount/serviceitemscount3',
method: 'post',
data: params
})
}
// 门店id
export function serviceitemscountmen(repositoryid, date) {
var params = new URLSearchParams()
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
params.append('date', date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/servicecount/serviceitemscount3',
method: 'post',
data: params
})
}

60
src/api/client.js Normal file
View file

@ -0,0 +1,60 @@
import request from '@/utils/request'
// 客户列表
export function userlist(query) {
var params = new URLSearchParams()
params.append('phone', query.phone) // 你要传给后台的参数值 key/value
params.append('userName', query.userName) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/user/usersearch',
method: 'post',
data: params
})
}
// 客户详情
export function userdetailbyid(query) {
var params = new URLSearchParams()
params.append('userid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/user/userdetailbyid',
method: 'post',
data: params
})
}
// 加入黑名单
export function addblacklist(employeeid, userid, description) {
var params = new URLSearchParams()
params.append('employeeid', employeeid) // 你要传给后台的参数值 key/value
params.append('userid', userid) // 你要传给后台的参数值 key/value
params.append('description', description) // 你要传给后台的参数值 key/value
return request({
url: '/pss/blacklist/add',
method: 'post',
data: params
})
}
// 推广统计列表
export function getuserextension(query) {
return request({
url: '/pss/repository/getuserextension',
method: 'post',
params: query
})
}
// 推广统计列表搜索
export function seachuserextension(repositoryname, date) {
var params = new URLSearchParams()
params.append('repositoryname', repositoryname) // 你要传给后台的参数值 key/value
params.append('date', date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getuserextension',
method: 'post',
data: params
})
}

99
src/api/complaint.js Normal file
View file

@ -0,0 +1,99 @@
import request from '@/utils/request'
// 投诉列表
export function listcomplaints(username, phone, stat, pagenum, pagesize) {
var params = new URLSearchParams()
params.append('username', username) // 你要传给后台的参数值 key/value
params.append('phone', phone) // 你要传给后台的参数值 key/value
params.append('stat', stat) // 你要传给后台的参数值 key/value
params.append('pagenum', pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/complaint/listcomplaints',
method: 'post',
data: params
})
}
// 根据门点id获取员工
export function getremplist(repositoryid) {
var params = new URLSearchParams()
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
params.append('stat', 1) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 根据区域id获取员工
export function getremplist2(regionid) {
var params = new URLSearchParams()
params.append('regionid2', regionid) // 你要传给后台的参数值 key/value
params.append('stat', 1) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 投诉订单分派给员工
export function complainttoemp(complaintid, handlerid) {
var params = new URLSearchParams()
params.append('complaintid', complaintid) // 你要传给后台的参数值 key/value
params.append('handlerid', handlerid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/complaint/complainttoemp',
method: 'post',
data: params
})
}
// 用户搜索
export function usersearch() {
var params = new URLSearchParams()
params.append('pagenum', 1) // 你要传给后台的参数值 key/value
params.append('pagesize', 1000) // 你要传给后台的参数值 key/value
return request({
url: '/pss/user/usersearch',
method: 'post',
data: params
})
}
// 创建投诉
export function createcomplaint(query) {
var params = new URLSearchParams()
params.append('userid', query.userid) // 你要传给后台的参数值 key/value
params.append('subject', query.subject) // 你要传给后台的参数值 key/value
params.append('content', query.content) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/complaint/createcomplaint',
method: 'post',
data: params
})
}
// 投诉详情
export function complaintdetailemp(query) {
var params = new URLSearchParams()
params.append('complaintid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/complaint/complaintdetailemp',
method: 'post',
data: params
})
}
// 投诉详情
export function deletecomplaint(query) {
var params = new URLSearchParams()
params.append('complaintid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/complaint/deletecomplaint',
method: 'post',
data: params
})
}

46
src/api/discount.js Normal file
View file

@ -0,0 +1,46 @@
import request from '@/utils/request'
// 折扣列表
export function listdiscount(query) {
return request({
url: '/pss/discount/listdiscount',
method: 'post',
params: query
})
}
// 添加折扣
export function adddiscount(query) {
var params = new URLSearchParams()
params.append('discountname', query.discountName) // 你要传给后台的参数值 key/value
params.append('discountrate', query.discountRate) // 你要传给后台的参数值 key/value
params.append('createid', query.createid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/discount/adddiscount',
method: 'post',
data: params
})
}
// 修改折扣
export function updatediscount(query) {
var params = new URLSearchParams()
params.append('discountid', query.id) // 你要传给后台的参数值 key/value
params.append('discountrate', query.discountRate) // 你要传给后台的参数值 key/value
return request({
url: '/pss/discount/updatediscount',
method: 'post',
data: params
})
}
// 删除折扣
export function deletediscount(query) {
var params = new URLSearchParams()
params.append('discountid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/discount/deletediscount',
method: 'post',
data: params
})
}

244
src/api/employee.js Normal file
View file

@ -0,0 +1,244 @@
import request from '@/utils/request'
// 员工列表
export function employeelist(query) {
var params = new URLSearchParams()
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('personname', query.personname) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('regionid2', query.regionid2) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 员工列表
export function searchemployeelist(query) {
var params = new URLSearchParams()
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('personname', query.personname) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('regionid2', query.regionid) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 所有门店列表
export function allstore(repositoryid, regionid) {
var params = new URLSearchParams()
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getrepositorybyreid',
method: 'post',
data: params
})
}
// 所有区域列表
export function allarea(repositoryid, regionid) {
var params = new URLSearchParams()
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/region/getregionlistbyreid',
method: 'post',
data: params
})
}
// 员工列表
export function editemployee(query) {
var params = new URLSearchParams()
params.append('userid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/user/getemployeeinformation',
method: 'post',
data: params
})
}
// 员工列表
export function editarea(query) {
return request({
url: '/pss/region/detaillist',
method: 'post',
params: query
})
}
// 根据区域获取门店列表
export function getrepositorylist(regionid) {
var params = new URLSearchParams()
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getrepositorylist',
method: 'post',
data: params
})
}
// 获取角色列表
export function getrolelist(query) {
return request({
url: '/pss/role/getrolelist',
method: 'post',
params: query
})
}
// 修改角色
export function updateemprepository(query) {
var params = new URLSearchParams()
params.append('employeeid', query.id) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('roleid', query.roleId) // 你要传给后台的参数值 key/value
params.append('newrepositoryid', query.repositoryId) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionId) // 你要传给后台的参数值 key/value
params.append('password', query.password) // 你要传给后台的参数值 key/value
params.append('personname', query.personName) // 你要传给后台的参数值 key/value
params.append('birthday', query.birthday) // 你要传给后台的参数值 key/value
params.append('email', query.email) // 你要传给后台的参数值 key/value
params.append('phone', query.phoneNumber) // 你要传给后台的参数值 key/value
params.append('gender', query.gender) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/updateemprepository',
method: 'post',
data: params
})
}
// 根据id获取门店列表
export function getrepositorybyid(repositoryid) {
var params = new URLSearchParams()
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getrepositorybyid',
method: 'post',
data: params
})
}
// 根据id获取门店列表
export function addemp(query) {
var params = new URLSearchParams()
params.append('username', query.username) // 你要传给后台的参数值 key/value
params.append('passwd', query.passwd) // 你要传给后台的参数值 key/value
params.append('email', query.email) // 你要传给后台的参数值 key/value
params.append('phone', query.phone) // 你要传给后台的参数值 key/value
params.append('gender', query.gender) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('roleid', query.roleid) // 你要传给后台的参数值 key/value
params.append('birthday', query.birthday) // 你要传给后台的参数值 key/value
params.append('personname', query.personname) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/user/register',
method: 'post',
data: params
})
}
// 获取员工绩效列表搜索
export function kpigradelist(query) {
var params = new URLSearchParams()
params.append('employeename', query.employeename) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
params.append('begintime', query.begintime) // 你要传给后台的参数值 key/value
params.append('endtime', query.endtime) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/kpi/kpigradelist',
method: 'post',
data: params
})
}
// 获取员工绩效列表
export function kpigradelist2(query) {
var params = new URLSearchParams()
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/kpi/kpigradelist',
method: 'post',
data: params
})
}
// 根据员工id获取员工分数
export function getcountkpibymonth(query) {
var params = new URLSearchParams()
params.append('employeeid', query) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/kpi/getcountkpibymonth',
method: 'post',
data: params
})
}
// 根据员工id获取绩效得分趋势
export function empkpibymonth(query) {
var params = new URLSearchParams()
params.append('employeeid', query) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/kpi/empkpibymonth',
method: 'post',
data: params
})
}
// 获取绩效规则
export function kpiruleslist(query) {
return request({
url: '/pss/admin/kpi/kpiruleslist',
method: 'post',
params: query
})
}
// 修改绩效规则
export function updaterules(kpirulesid, mark) {
var params = new URLSearchParams()
params.append('kpirulesid', kpirulesid) // 你要传给后台的参数值 key/value
params.append('mark', mark) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/kpi/updaterules',
method: 'post',
data: params
})
}
// 删除绩效规则
export function deleterules(kpirulesid) {
var params = new URLSearchParams()
params.append('kpirulesid', kpirulesid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/kpi/deleterules',
method: 'post',
data: params
})
}
// 员工轨迹中详细数据
export function gettasklistbyday(userid, date) {
var params = new URLSearchParams()
params.append('userid', userid) // 你要传给后台的参数值 key/value
params.append('date', date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/position/gettasklistbyday',
method: 'post',
data: params
})
}

196
src/api/goods.js Normal file
View file

@ -0,0 +1,196 @@
import request from '@/utils/request'
// 商品分类
export function categories(query) {
var params = new URLSearchParams()
params.append('keyword', query.keyword2) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum)
params.append('pagesize', query.pagesize)
return request({
url: '/pss/category/listproductcategory',
method: 'post',
data: params
})
}
// 获取商品分类
export function categories2() {
return request({
url: '/pss/category/listproductcategory',
method: 'post'
})
}
// 商品分类搜索
export function searchcategories(query) {
var params = new URLSearchParams()
params.append('keyword', query.keyword2) // 你要传给后台的参数值 key/value
return request({
url: '/pss/category/listproductcategory',
method: 'post',
data: params
})
}
// 商品分类添加
export function addcategories(res) {
var params = new URLSearchParams()
params.append('ccode', res.categoryCode) // 你要传给后台的参数值 key/value
params.append('cname', res.categoryName)
params.append('createrid', res.createrId)
return request({
url: '/pss/category/newproductcategory',
method: 'post',
data: params
})
}
// 商品分类删除
export function deletecategories(res) {
var params = new URLSearchParams()
params.append('id', res.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/category/deleteproductcategory',
method: 'post',
data: params
})
}
// 商品分类修改
export function editcategories(res) {
var params = new URLSearchParams()
params.append('cid', res.id) // 你要传给后台的参数值 key/value
params.append('ccode', res.categoryCode) // 你要传给后台的参数值 key/value
params.append('cname', res.categoryName) // 你要传给后台的参数值 key/value
return request({
url: '/pss/category/editproductcategory',
method: 'post',
data: params
})
}
// 商品列表
// 商品列表
export function fetchList2(query) {
var params = new URLSearchParams()
params.append('code', query.code) // 你要传给后台的参数值 key/value
params.append('isactive', query.isActive)
params.append('maintitle', query.mainTitle)
params.append('pagenum', query.pagenum)
params.append('pagesize', query.pagesize)
params.append('type', query.type)
return request({
url: '/pss/product/adminsearch',
method: 'post',
data: params
})
}
// 商品列表搜索
export function searchList(code, isactive, maintitle) {
var params = new URLSearchParams()
params.append('code', code) // 你要传给后台的参数值 key/value
params.append('isactive', isactive)
params.append('maintitle', maintitle)
return request({
url: '/pss/product/adminsearch',
method: 'post',
data: params
})
}
// 商品列表上下架修改
export function productisactivechange(productid, isactive) {
var params = new URLSearchParams()
params.append('productid', productid) // 你要传给后台的参数值 key/value
params.append('isactive', isactive)
return request({
url: '/pss/product/productisactivechange',
method: 'post',
data: params
})
}
// 商品列表修改
export function editproduct(res) {
var params = new URLSearchParams()
params.append('brand', res.brand) // 你要传给后台的参数值 key/value
params.append('type', res.type)
params.append('color', res.color)
params.append('costPrice', res.costPrice)
params.append('mainTitle', res.mainTitle)
params.append('salePrice', res.salePrice)
params.append('productCategoryId', res.productCategoryId)
params.append('code', res.code)
params.append('createrId', res.createrId)
params.append('quantity', res.quantity)
params.append('subTitle', res.subTitle)
params.append('productDetail', res.productDetail)
params.append('picids', res.picids)
params.append('isActive', res.isActive)
params.append('kpiGrade', res.kpiGrade)
params.append('barcode', res.barcode)
params.append('detailpicid', res.detailpicid)
params.append('id', res.id)
return request({
url: '/pss/product/editproduct',
method: 'post',
data: params
})
}
// 商品列表添加
export function addproduct(res) {
var params = new URLSearchParams()
params.append('brand', res.brand) // 你要传给后台的参数值 key/value
params.append('type', res.type)
params.append('color', res.color)
params.append('costPrice', res.costPrice)
params.append('mainTitle', res.mainTitle)
params.append('salePrice', res.salePrice)
params.append('productCategoryId', res.productCategoryId)
params.append('code', res.code)
params.append('createrId', res.createrId)
params.append('quantity', res.quantity)
params.append('picids', res.picids)
params.append('isActive', res.isActive)
params.append('kpiGrade', res.kpiGrade)
params.append('barcode', res.barcode)
params.append('detailpicid', res.detailpicid)
return request({
url: '/pss/product/createnewproduct',
method: 'post',
data: params
})
}
// 获取商品分类
export function getproducttype() {
return request({
url: '/pss/product/getproducttype',
method: 'post'
})
}
// 商品列表详情
export function detail(productid) {
var params = new URLSearchParams()
params.append('productid', productid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/product/detailinfo',
method: 'post',
data: params
})
}
// 商品列表详情页图表
export function tu(productid) {
var params = new URLSearchParams()
params.append('productid', productid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/product/newrepairsbytype',
method: 'post',
data: params
})
}

39
src/api/login.js Normal file
View file

@ -0,0 +1,39 @@
import request from '@/utils/request'
export function loginByUsername(username, password) {
var params = new URLSearchParams()
params.append('username', username) // 你要传给后台的参数值 key/value
params.append('passwd', password)
return request({
url: '/pss/admin/user/login',
method: 'post',
data: params
})
}
export function logout() {
return request({
url: '/login/logout',
method: 'post'
})
}
export function getUserInfo(token) {
var params = new URLSearchParams()
params.append('token', token) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/user/getInforByToken',
method: 'post',
data: params
})
}
export function getstatByToken(token) {
var params = new URLSearchParams()
params.append('token', token) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/user/getstatByToken',
method: 'post',
data: params
})
}

57
src/api/lostUnits.js Normal file
View file

@ -0,0 +1,57 @@
import request from '@/utils/request'
// 黑名单列表
export function blacklist(query) {
return request({
url: '/pss/blacklist/list',
method: 'post',
params: query
})
}
// 黑名单搜索
export function searcblacklist(query) {
var params = new URLSearchParams()
params.append('personname', query.personname) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('productsn', query.productsn) // 你要传给后台的参数值 key/value
return request({
url: '/pss/blacklist/list',
method: 'post',
data: params
})
}
// 修改黑名单
export function judgeblacklist(query) {
var params = new URLSearchParams()
params.append('blacklistid', query.id) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
return request({
url: '/pss/blacklist/judgeblacklist',
method: 'post',
data: params
})
}
// 车辆丢失列表
export function loselist(query) {
return request({
url: '/pss/blacklist/loselist',
method: 'post',
params: query
})
}
// 车辆丢失搜索
export function searchloselist(query) {
var params = new URLSearchParams()
params.append('personname', query.personname) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('productsn', query.productsn) // 你要传给后台的参数值 key/value
return request({
url: '/pss/blacklist/loselist',
method: 'post',
data: params
})
}

37
src/api/map.js Normal file
View file

@ -0,0 +1,37 @@
import request from '@/utils/request'
// 经纬点
export function getrepositorybyreid() {
var params = new URLSearchParams()
params.append('regionid', 0) // 你要传给后台的参数值 key/value
params.append('repositoryid', 0) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getrepositorybyreid',
method: 'post',
data: params
})
}
// 员工轨迹
export function getpositionbydate(userid, date) {
var params = new URLSearchParams()
params.append('userid', userid) // 你要传给后台的参数值 key/value
params.append('date', date) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/position/getpositionbydate',
method: 'post',
data: params
})
}
// 维修轨迹
export function getposition(userid, serviceorderid) {
var params = new URLSearchParams()
params.append('userid', userid) // 你要传给后台的参数值 key/value
params.append('serviceorderid', serviceorderid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/position/getposition',
method: 'post',
data: params
})
}

153
src/api/order.js Normal file
View file

@ -0,0 +1,153 @@
import request from '@/utils/request'
// 订单列表
export function orderlist(query) {
return request({
url: '/pss/order/adminorderlist',
method: 'post',
params: query
})
}
// 订单列表搜索
export function searchorders(query) {
var params = new URLSearchParams()
params.append('keyword', query.keyword) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
return request({
url: '/pss/order/adminorderlist',
method: 'post',
data: params
})
}
// 订单详情
export function orderdetail(query) {
var params = new URLSearchParams()
params.append('orderid', query) // 你要传给后台的参数值 key/value
return request({
url: '/pss/order/orderdetail',
method: 'post',
data: params
})
}
// 分期订单搜索
export function installsearch(query) {
var params = new URLSearchParams()
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('applicantname', query.applicantname) // 你要传给后台的参数值 key/value
params.append('beginTime', query.beginTime) // 你要传给后台的参数值 key/value
params.append('endTime', query.endTime) // 你要传给后台的参数值 key/value
params.append('code', query.code) // 你要传给后台的参数值 key/value
return request({
url: '/pss/installmentorder/geteachinstallmentlist',
method: 'post',
data: params
})
}
// 分期订单列表
export function installlist(query) {
return request({
url: '/pss/installmentorder/geteachinstallmentlist',
method: 'post',
params: query
})
}
// 分期订单详情
export function installdetail(query) {
var params = new URLSearchParams()
params.append('orderid', query) // 你要传给后台的参数值 key/value
return request({
url: '/pss/order/orderdetail',
method: 'post',
data: params
})
}
// 所有员工列表
export function stafflist() {
return request({
url: '/pss/repository/getremplist',
method: 'post'
})
}
// 分期订单分派
export function dispatchorder(eachinstallmentid, handlerid) {
var params = new URLSearchParams()
params.append('eachinstallmentid', eachinstallmentid) // 你要传给后台的参数值 key/value
params.append('handlerid', handlerid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/installmenttoemp',
method: 'post',
data: params
})
}
// 所有门店信息
export function getrepositorylists() {
return request({
url: '/pss/repository/getrepositorylist',
method: 'post'
})
}
// 上门收款列表
export function getgather(query) {
return request({
url: '/pss/gathering/getgatheringbyrepositoryid',
method: 'post',
params: query
})
}
// 上门收款订单搜索
export function receiptseach(query) {
var params = new URLSearchParams()
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('ordercode', query.ordercode) // 你要传给后台的参数值 key/value
return request({
url: '/pss/gathering/getgatheringbyrepositoryid',
method: 'post',
data: params
})
}
// 分期申请列表
export function getjudgeinfolist(query) {
return request({
url: '/pss/installmentorder/getjudgeinfolist',
method: 'post',
params: query
})
}
// 分期申请订单搜索
export function applicationseach(query) {
var params = new URLSearchParams()
params.append('applicantname', query.applicantname) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
return request({
url: '/pss/installmentorder/getjudgeinfolist',
method: 'post',
data: params
})
}
// 分期申请订单审核
export function installmentjudge(stat, employeeid, installmentjudgeid) {
var params = new URLSearchParams()
params.append('stat', stat) // 你要传给后台的参数值 key/value
params.append('employeeid', employeeid) // 你要传给后台的参数值 key/value
params.append('installmentjudgeid', installmentjudgeid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/installmentorder/installmentjudge',
method: 'post',
data: params
})
}

8
src/api/qiniu.js Normal file
View file

@ -0,0 +1,8 @@
import request from '@/utils/request'
export function getToken() {
return request({
url: '/qiniu/upload/token', // 假地址 自行替换
method: 'get'
})
}

9
src/api/remoteSearch.js Normal file
View file

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function userSearch(name) {
return request({
url: '/search/user',
method: 'get',
params: { name }
})
}

411
src/api/repair.js Normal file
View file

@ -0,0 +1,411 @@
import request from '@/utils/request'
// 维修列表
export function repairList(query) {
var params = new URLSearchParams()
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('username', query.username) // 你要传给后台的参数值 key/value
params.append('begintime', query.begintime) // 你要传给后台的参数值 key/value
params.append('endtime', query.endtime) // 你要传给后台的参数值 key/valuedelaycount
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('expectrepositoryid', query.expectrepositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/getservicelistbycondition',
method: 'post',
data: params
})
}
// 维修详情
export function repairDetail(serviceid) {
var params = new URLSearchParams()
params.append('serviceid', serviceid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/getservicedetail',
method: 'post',
data: params
})
}
// 取消已分配的订单重新分配给员工
export function serviceretoemp(serviceid) {
var params = new URLSearchParams()
params.append('serviceid', serviceid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/serviceretoemp',
method: 'post',
data: params
})
}
// 取消已分配的订单重新分配给员工
export function endservicebackground(serviceid) {
var params = new URLSearchParams()
params.append('serviceid', serviceid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/endservicebackground',
method: 'post',
data: params
})
}
// 维修单重新分配门店
export function retoreposervice(serviceid, repositoryid) {
var params = new URLSearchParams()
params.append('serviceid', serviceid) // 你要传给后台的参数值 key/value
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/retoreposervice',
method: 'post',
data: params
})
}
// 维修追踪
export function getserviceschedule(serviceid) {
var params = new URLSearchParams()
params.append('serviceid', serviceid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/serviceposition/getserviceschedule',
method: 'post',
data: params
})
}
// 分派维修订单
export function repairtoemp(serviceorderid, handlerid) {
var params = new URLSearchParams()
params.append('serviceorderid', serviceorderid) // 你要传给后台的参数值 key/value
params.append('handlerid', handlerid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/serviceordertoemp',
method: 'post',
data: params
})
}
// 车辆型号
export function producttype(query) {
return request({
url: 'pss/product/getproducttype',
method: 'post',
params: query
})
}
// 门店列表
export function repositorylist(query) {
return request({
url: 'pss/repository/getrepositorylist',
method: 'post',
params: query
})
}
// 新增维修订单
export function newservice(query) {
var params = new URLSearchParams()
params.append('components', query.components) // 你要传给后台的参数值 key/value
params.append('detail', query.detail) // 你要传给后台的参数值 key/value
params.append('producttype', query.producttype) // 你要传给后台的参数值 key/value
params.append('username', query.username) // 你要传给后台的参数值 key/value
params.append('address', query.address) // 你要传给后台的参数值 key/value
params.append('person', query.person) // 你要传给后台的参数值 key/value
params.append('phone', query.phone) // 你要传给后台的参数值 key/value
params.append('servicexpecttime', query.servicexpecttime) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('cityid', query.cityid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/newservice',
method: 'post',
data: params
})
}
// 维修项目列表
export function repairprojectList(query) {
var params = new URLSearchParams()
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
params.append('producttype', query.productType) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/listserviceitem',
method: 'post',
data: params
})
}
// 维修项目修改
export function editrepairproject(price, itemid) {
var params = new URLSearchParams()
params.append('price', price) // 你要传给后台的参数值 key/value
params.append('itemid', itemid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/updateserviceitem',
method: 'post',
data: params
})
}
// 维修项目删除
export function deleterepairproject(itemid) {
var params = new URLSearchParams()
params.append('itemid', itemid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/deleteserviceitem',
method: 'post',
data: params
})
}
// 添加维修项目
export function addrepairproject(query) {
var params = new URLSearchParams()
params.append('producttype', query.producttype) // 你要传给后台的参数值 key/value
params.append('name', query.name) // 你要传给后台的参数值 key/value
params.append('price', query.price) // 你要传给后台的参数值 key/value
params.append('desc', query.desc) // 你要传给后台的参数值 key/value
params.append('mark', query.mark) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/addserviceitem',
method: 'post',
data: params
})
}
// 维修项目搜索
export function searchrepairproject(query) {
var params = new URLSearchParams()
params.append('producttype', query.productType) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/listserviceitem',
method: 'post',
data: params
})
}
// 根据门点id获取员工
export function getremplist(repositoryid) {
var params = new URLSearchParams()
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
params.append('stat', 1) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 项目所有员工
export function stafflist(repositoryid, regionid) {
var params = new URLSearchParams()
params.append('repositoryid', repositoryid) // 你要传给后台的参数值 key/value
params.append('regionid', regionid) // 你要传给后台的参数值 key/value
params.append('pagesize', 1000) // 你要传给后台的参数值 key/value
params.append('pagenum', 1) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 线下任务列表
export function gettaskofflinelist(query) {
var params = new URLSearchParams()
params.append('result', query.result) // 你要传给后台的参数值 key/value
params.append('code', query.code) // 你要传给后台的参数值 key/value
params.append('employeeid', query.employeeid) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/taskoffline/gettaskofflinelist',
method: 'post',
data: params
})
}
// 线下任务删除
export function deletetaskoffline(query) {
var params = new URLSearchParams()
params.append('taskid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/taskoffline/deletetaskoffline',
method: 'post',
data: params
})
}
// 添加线下任务
export function addtaskoffline(query) {
var params = new URLSearchParams()
params.append('taskname', query.taskname) // 你要传给后台的参数值 key/value
params.append('taskaddress', query.taskaddress) // 你要传给后台的参数值 key/value
params.append('taskcontent', query.taskcontent) // 你要传给后台的参数值 key/value
params.append('createid', query.createid) // 你要传给后台的参数值 key/value
params.append('employeeid', query.employeeid) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/taskoffline/addtaskoffline',
method: 'post',
data: params
})
}
// 线下任务搜索
export function searchtaskofflinelist(query) {
var params = new URLSearchParams()
params.append('employeeid', query.employeeid) // 你要传给后台的参数值 key/value
params.append('result', query.result) // 你要传给后台的参数值 key/value
params.append('code', query.code) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/taskoffline/gettaskofflinelist',
method: 'post',
data: params
})
}
// 延误统计列表
export function delaycount(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('code', query.code) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/serviceorderdelay/delaycount',
method: 'post',
data: params
})
}
// 延误统计搜索
export function searchdelaycount(query) {
var params = new URLSearchParams()
params.append('begintime', query.begintime) // 你要传给后台的参数值 key/value
params.append('endtime', query.endtime) // 你要传给后台的参数值 key/value
params.append('code', query.code) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/serviceorderdelay/delaycount',
method: 'post',
data: params
})
}
// 问题部位列表
export function searchproblemobject(query) {
return request({
url: '/pss/admin/fault/search',
method: 'post',
params: query
})
}
// 添加问题部位
export function addfaultcomponent(query) {
var params = new URLSearchParams()
params.append('faultname', query.faultname) // 你要传给后台的参数值 key/value
params.append('level', query.level) // 你要传给后台的参数值 key/value
params.append('parentid', query.parentid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/fault/addfaultcomponent',
method: 'post',
data: params
})
}
// 自检管理列表
export function selfChecklist(query) {
return request({
url: '/pss/admin/selfchecking/search',
method: 'post',
params: query
})
}
// 添加自检部位
export function addselfchecking(query) {
var params = new URLSearchParams()
params.append('name', query.name) // 你要传给后台的参数值 key/value
params.append('level', query.level) // 你要传给后台的参数值 key/value
params.append('parentid', query.parentid) // 你要传给后台的参数值 key/value
params.append('content', query.content) // 你要传给后台的参数值 key/value
params.append('employeeguide', query.employeeguide) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/selfchecking/addselfchecking',
method: 'post',
data: params
})
}
// 删除自检部位
export function deleteselfchecking(query) {
var params = new URLSearchParams()
params.append('id', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/selfchecking/deleteselfchecking',
method: 'post',
data: params
})
}
// 删除未分配的维修单
export function deleteservice(query) {
var params = new URLSearchParams()
params.append('serviceid', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/deleteservice',
method: 'post',
data: params
})
}
// 问题部位删除
export function deletefaultcomponent(query) {
var params = new URLSearchParams()
params.append('id', query.id) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/fault/deletefaultcomponent',
method: 'post',
data: params
})
}
// 维修各个状态的统计
export function countservice(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/countservice',
method: 'post',
data: params
})
}
// 维修各个状态的统计
export function countservice2(query) {
var params = new URLSearchParams()
params.append('regionid', query.regionid) // 你要传给后台的参数值 key/value
params.append('repositoryid', query.repositoryid) // 你要传给后台的参数值 key/value
params.append('begintime', query.begintime) // 你要传给后台的参数值 key/value
params.append('endtime', query.endtime) // 你要传给后台的参数值 key/value
return request({
url: '/pss/admin/service/countservice',
method: 'post',
data: params
})
}

57
src/api/roles.js Normal file
View file

@ -0,0 +1,57 @@
import request from '@/utils/request'
// 角色列表
export function repairList(query) {
var params = new URLSearchParams()
params.append('deptid', query.deptid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/role/getrolelist',
method: 'post',
data: params
})
}
// 权限列表
export function getauthoritydetaillist(query) {
return request({
url: '/pss/role/getauthoritydetaillist',
method: 'post',
params: query
})
}
// 添加角色
export function addrole(rolename, authority, description) {
var params = new URLSearchParams()
params.append('rolename', rolename) // 你要传给后台的参数值 key/value
params.append('authority', authority) // 你要传给后台的参数值 key/value
params.append('description', description) // 你要传给后台的参数值 key/value
return request({
url: '/pss/role/addrole',
method: 'post',
data: params
})
}
// 修改角色
export function updaterole(roleid, authority) {
var params = new URLSearchParams()
params.append('roleid', roleid) // 你要传给后台的参数值 key/value
params.append('authority', authority) // 你要传给后台的参数值 key/value
return request({
url: '/pss/role/updaterole',
method: 'post',
data: params
})
}
// 删除角色
export function deleterole(roleid) {
var params = new URLSearchParams()
params.append('roleid', roleid) // 你要传给后台的参数值 key/value
return request({
url: '/pss/role/deleterole',
method: 'post',
data: params
})
}

65
src/api/store.js Normal file
View file

@ -0,0 +1,65 @@
import request from '@/utils/request'
// 门店列表
export function getrepositorylist(query) {
var params = new URLSearchParams()
params.append('phone', query.phone) // 你要传给后台的参数值 key/value
params.append('repositoryname', query.repositoryname) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('pagenum', query.pagenum) // 你要传给后台的参数值 key/value
params.append('pagesize', query.pagesize) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getrepositorylist',
method: 'post',
data: params
})
}
// 新建门店
export function addrepository(query) {
var params = new URLSearchParams()
params.append('longitude', query.longitude) // 你要传给后台的参数值 key/value
params.append('latitude', query.latitude) // 你要传给后台的参数值 key/value
params.append('address', query.address) // 你要传给后台的参数值 key/value
params.append('phone', query.phone) // 你要传给后台的参数值 key/value
params.append('regionId', query.regionId) // 你要传给后台的参数值 key/value
params.append('name', query.name) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/addrepository',
method: 'post',
data: params
})
}
// 获取门店经理
export function getremplist(query) {
var params = new URLSearchParams()
params.append('repositoryid', query.id) // 你要传给后台的参数值 key/value
params.append('pagenum', 1) // 你要传给后台的参数值 key/value
params.append('pagesize', 10000) // 你要传给后台的参数值 key/value
params.append('stat', 1) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/getremplist',
method: 'post',
data: params
})
}
// 修改门店
export function updaterepository(query) {
var params = new URLSearchParams()
params.append('repositoryid', query.id) // 你要传给后台的参数值 key/value
params.append('stat', query.stat) // 你要传给后台的参数值 key/value
params.append('phone', query.phone) // 你要传给后台的参数值 key/value
params.append('managerid', query.managerid) // 你要传给后台的参数值 key/value
params.append('regionid', query.regionId) // 你要传给后台的参数值 key/value
params.append('repositoryname', query.name) // 你要传给后台的参数值 key/value
params.append('address', query.address) // 你要传给后台的参数值 key/value
params.append('longitude', query.longitude) // 你要传给后台的参数值 key/value
params.append('latitude', query.latitude) // 你要传给后台的参数值 key/value
return request({
url: '/pss/repository/updaterepository',
method: 'post',
data: params
})
}

9
src/api/transaction.js Normal file
View file

@ -0,0 +1,9 @@
import request from '@/utils/request'
export function fetchList(query) {
return request({
url: '/transaction/list',
method: 'get',
params: query
})
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

BIN
src/assets/orders/test.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -0,0 +1,116 @@
<template>
<transition :name="transitionName">
<div v-show="visible" :style="customStyle" class="back-to-ceiling" @click="backToTop">
<svg width="16" height="16" viewBox="0 0 17 17" xmlns="http://www.w3.org/2000/svg" class="Icon Icon--backToTopArrow" aria-hidden="true" style="height: 16px; width: 16px;">
<title>回到顶部</title>
<g>
<path d="M12.036 15.59c0 .55-.453.995-.997.995H5.032c-.55 0-.997-.445-.997-.996V8.584H1.03c-1.1 0-1.36-.633-.578-1.416L7.33.29c.39-.39 1.026-.385 1.412 0l6.878 6.88c.782.78.523 1.415-.58 1.415h-3.004v7.004z" fill-rule="evenodd"/>
</g>
</svg>
</div>
</transition>
</template>
<script>
export default {
name: 'BackToTop',
props: {
visibilityHeight: {
type: Number,
default: 400
},
backPosition: {
type: Number,
default: 0
},
customStyle: {
type: Object,
default: function() {
return {
right: '50px',
bottom: '50px',
width: '40px',
height: '40px',
'border-radius': '4px',
'line-height': '45px',
background: '#e7eaf1'
}
}
},
transitionName: {
type: String,
default: 'fade'
}
},
data() {
return {
visible: false,
interval: null,
isMoving: false
}
},
mounted() {
window.addEventListener('scroll', this.handleScroll)
},
beforeDestroy() {
window.removeEventListener('scroll', this.handleScroll)
if (this.interval) {
clearInterval(this.interval)
}
},
methods: {
handleScroll() {
this.visible = window.pageYOffset > this.visibilityHeight
},
backToTop() {
if (this.isMoving) return
const start = window.pageYOffset
let i = 0
this.isMoving = true
this.interval = setInterval(() => {
const next = Math.floor(this.easeInOutQuad(10 * i, start, -start, 500))
if (next <= this.backPosition) {
window.scrollTo(0, this.backPosition)
clearInterval(this.interval)
this.isMoving = false
} else {
window.scrollTo(0, next)
}
i++
}, 16.7)
},
easeInOutQuad(t, b, c, d) {
if ((t /= d / 2) < 1) return c / 2 * t * t + b
return -c / 2 * (--t * (t - 2) - 1) + b
}
}
}
</script>
<style scoped>
.back-to-ceiling {
position: fixed;
display: inline-block;
text-align: center;
cursor: pointer;
}
.back-to-ceiling:hover {
background: #d5dbe7;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity .5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0
}
.back-to-ceiling .Icon {
fill: #9aaabf;
background: none;
}
</style>

View file

@ -0,0 +1,73 @@
<template>
<el-breadcrumb class="app-breadcrumb" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" v-if="item.meta.title" :key="item.path">
<span v-if="item.redirect==='noredirect'||index==levelList.length-1" class="no-redirect">{{ generateTitle(item.meta.title) }}</span>
<a v-else @click.prevent="handleLink(item)">{{ generateTitle(item.meta.title) }}</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</template>
<script>
import { generateTitle } from '@/utils/i18n'
import pathToRegexp from 'path-to-regexp'
export default {
data() {
return {
levelList: null
}
},
watch: {
$route() {
this.getBreadcrumb()
}
},
created() {
this.getBreadcrumb()
},
methods: {
generateTitle,
getBreadcrumb() {
let matched = this.$route.matched.filter(item => {
if (item.name) {
return true
}
})
const first = matched[0]
if (first && first.name.trim().toLocaleLowerCase() !== 'Dashboard'.toLocaleLowerCase()) {
matched = [{ path: '/dashboard', meta: { title: 'dashboard' }}].concat(matched)
}
this.levelList = matched
},
pathCompile(path) {
// To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
const { params } = this.$route
var toPath = pathToRegexp.compile(path)
return toPath(params)
},
handleLink(item) {
const { redirect, path } = item
if (redirect) {
this.$router.push(redirect)
return
}
this.$router.push(this.pathCompile(path))
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.app-breadcrumb.el-breadcrumb {
display: inline-block;
font-size: 14px;
line-height: 50px;
margin-left: 10px;
.no-redirect {
color: #97a8be;
cursor: text;
}
}
</style>

View file

@ -0,0 +1,156 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
const xAxisData = []
const data = []
const data2 = []
for (let i = 0; i < 50; i++) {
xAxisData.push(i)
data.push((Math.sin(i / 5) * (i / 5 - 10) + i / 6) * 5)
data2.push((Math.sin(i / 5) * (i / 5 + 10) + i / 6) * 3)
}
this.chart.setOption(
{
backgroundColor: '#08263a',
grid: {
left: '5%',
right: '5%'
},
xAxis: [{
show: false,
data: xAxisData
}, {
show: false,
data: xAxisData
}],
visualMap: {
show: false,
min: 0,
max: 50,
dimension: 0,
inRange: {
color: ['#4a657a', '#308e92', '#b1cfa5', '#f5d69f', '#f5898b', '#ef5055']
}
},
yAxis: {
axisLine: {
show: false
},
axisLabel: {
textStyle: {
color: '#4a657a'
}
},
splitLine: {
show: true,
lineStyle: {
color: '#08263f'
}
},
axisTick: {
show: false
}
},
series: [{
name: 'back',
type: 'bar',
data: data2,
z: 1,
itemStyle: {
normal: {
opacity: 0.4,
barBorderRadius: 5,
shadowBlur: 3,
shadowColor: '#111'
}
}
}, {
name: 'Simulate Shadow',
type: 'line',
data,
z: 2,
showSymbol: false,
animationDelay: 0,
animationEasing: 'linear',
animationDuration: 1200,
lineStyle: {
normal: {
color: 'transparent'
}
},
areaStyle: {
normal: {
color: '#08263a',
shadowBlur: 50,
shadowColor: '#000'
}
}
}, {
name: 'front',
type: 'bar',
data,
xAxisIndex: 1,
z: 3,
itemStyle: {
normal: {
barBorderRadius: 5
}
}
}],
animationEasing: 'elasticOut',
animationEasingUpdate: 'elasticOut',
animationDelay(idx) {
return idx * 20
},
animationDelayUpdate(idx) {
return idx * 20
}
})
}
}
}
</script>

View file

@ -0,0 +1,227 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
this.chart.setOption({
backgroundColor: '#394056',
title: {
top: 20,
text: 'Requests',
textStyle: {
fontWeight: 'normal',
fontSize: 16,
color: '#F1F1F3'
},
left: '1%'
},
tooltip: {
trigger: 'axis',
axisPointer: {
lineStyle: {
color: '#57617B'
}
}
},
legend: {
top: 20,
icon: 'rect',
itemWidth: 14,
itemHeight: 5,
itemGap: 13,
data: ['CMCC', 'CTCC', 'CUCC'],
right: '4%',
textStyle: {
fontSize: 12,
color: '#F1F1F3'
}
},
grid: {
top: 100,
left: '2%',
right: '2%',
bottom: '2%',
containLabel: true
},
xAxis: [{
type: 'category',
boundaryGap: false,
axisLine: {
lineStyle: {
color: '#57617B'
}
},
data: ['13:00', '13:05', '13:10', '13:15', '13:20', '13:25', '13:30', '13:35', '13:40', '13:45', '13:50', '13:55']
}],
yAxis: [{
type: 'value',
name: '(%)',
axisTick: {
show: false
},
axisLine: {
lineStyle: {
color: '#57617B'
}
},
axisLabel: {
margin: 10,
textStyle: {
fontSize: 14
}
},
splitLine: {
lineStyle: {
color: '#57617B'
}
}
}],
series: [{
name: 'CMCC',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(137, 189, 27, 0.3)'
}, {
offset: 0.8,
color: 'rgba(137, 189, 27, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(137,189,27)',
borderColor: 'rgba(137,189,2,0.27)',
borderWidth: 12
}
},
data: [220, 182, 191, 134, 150, 120, 110, 125, 145, 122, 165, 122]
}, {
name: 'CTCC',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(0, 136, 212, 0.3)'
}, {
offset: 0.8,
color: 'rgba(0, 136, 212, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(0,136,212)',
borderColor: 'rgba(0,136,212,0.2)',
borderWidth: 12
}
},
data: [120, 110, 125, 145, 122, 165, 122, 220, 182, 191, 134, 150]
}, {
name: 'CUCC',
type: 'line',
smooth: true,
symbol: 'circle',
symbolSize: 5,
showSymbol: false,
lineStyle: {
normal: {
width: 1
}
},
areaStyle: {
normal: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
offset: 0,
color: 'rgba(219, 50, 51, 0.3)'
}, {
offset: 0.8,
color: 'rgba(219, 50, 51, 0)'
}], false),
shadowColor: 'rgba(0, 0, 0, 0.1)',
shadowBlur: 10
}
},
itemStyle: {
normal: {
color: 'rgb(219,50,51)',
borderColor: 'rgba(219,50,51,0.2)',
borderWidth: 12
}
},
data: [220, 182, 125, 145, 122, 191, 134, 150, 120, 110, 165, 122]
}]
})
}
}
}
</script>

View file

@ -0,0 +1,271 @@
<template>
<div :class="className" :id="id" :style="{height:height,width:width}"/>
</template>
<script>
import echarts from 'echarts'
import resize from './mixins/resize'
export default {
mixins: [resize],
props: {
className: {
type: String,
default: 'chart'
},
id: {
type: String,
default: 'chart'
},
width: {
type: String,
default: '200px'
},
height: {
type: String,
default: '200px'
}
},
data() {
return {
chart: null
}
},
mounted() {
this.initChart()
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.getElementById(this.id))
const xData = (function() {
const data = []
for (let i = 1; i < 13; i++) {
data.push(i + 'month')
}
return data
}())
this.chart.setOption({
backgroundColor: '#344b58',
title: {
text: 'statistics',
x: '20',
top: '20',
textStyle: {
color: '#fff',
fontSize: '22'
},
subtextStyle: {
color: '#90979c',
fontSize: '16'
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
textStyle: {
color: '#fff'
}
}
},
grid: {
left: '5%',
right: '5%',
borderWidth: 0,
top: 150,
bottom: 95,
textStyle: {
color: '#fff'
}
},
legend: {
x: '5%',
top: '10%',
textStyle: {
color: '#90979c'
},
data: ['female', 'male', 'average']
},
calculable: true,
xAxis: [{
type: 'category',
axisLine: {
lineStyle: {
color: '#90979c'
}
},
splitLine: {
show: false
},
axisTick: {
show: false
},
splitArea: {
show: false
},
axisLabel: {
interval: 0
},
data: xData
}],
yAxis: [{
type: 'value',
splitLine: {
show: false
},
axisLine: {
lineStyle: {
color: '#90979c'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 0
},
splitArea: {
show: false
}
}],
dataZoom: [{
show: true,
height: 30,
xAxisIndex: [
0
],
bottom: 30,
start: 10,
end: 80,
handleIcon: 'path://M306.1,413c0,2.2-1.8,4-4,4h-59.8c-2.2,0-4-1.8-4-4V200.8c0-2.2,1.8-4,4-4h59.8c2.2,0,4,1.8,4,4V413z',
handleSize: '110%',
handleStyle: {
color: '#d3dee5'
},
textStyle: {
color: '#fff' },
borderColor: '#90979c'
}, {
type: 'inside',
show: true,
height: 15,
start: 1,
end: 35
}],
series: [{
name: 'female',
type: 'bar',
stack: 'total',
barMaxWidth: 35,
barGap: '10%',
itemStyle: {
normal: {
color: 'rgba(255,144,128,1)',
label: {
show: true,
textStyle: {
color: '#fff'
},
position: 'insideTop',
formatter(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: [
709,
1917,
2455,
2610,
1719,
1433,
1544,
3285,
5208,
3372,
2484,
4078
]
},
{
name: 'male',
type: 'bar',
stack: 'total',
itemStyle: {
normal: {
color: 'rgba(0,191,183,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: [
327,
1776,
507,
1200,
800,
482,
204,
1390,
1001,
951,
381,
220
]
}, {
name: 'average',
type: 'line',
stack: 'total',
symbolSize: 10,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(252,230,48,1)',
barBorderRadius: 0,
label: {
show: true,
position: 'top',
formatter(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: [
1036,
3693,
2962,
3810,
2519,
1915,
1748,
4675,
6209,
4323,
2865,
4298
]
}
]
})
}
}
}
</script>

View file

@ -0,0 +1,32 @@
import { debounce } from '@/utils'
export default {
data() {
return {
sidebarElm: null
}
},
mounted() {
this.__resizeHandler = debounce(() => {
if (this.chart) {
this.chart.resize()
}
}, 100)
window.addEventListener('resize', this.__resizeHandler)
this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]
this.sidebarElm && this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler)
},
beforeDestroy() {
window.removeEventListener('resize', this.__resizeHandler)
this.sidebarElm && this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler)
},
methods: {
sidebarResizeHandler(e) {
if (e.propertyName === 'width') {
this.__resizeHandler()
}
}
}
}

View file

@ -0,0 +1,158 @@
<template>
<div class="dndList">
<div :style="{width:width1}" class="dndList-list">
<h3>{{ list1Title }}</h3>
<draggable :list="list1" :options="{group:'article'}" class="dragArea">
<div v-for="element in list1" :key="element.id" class="list-complete-item">
<div class="list-complete-item-handle">[{{ element.author }}] {{ element.title }}</div>
<div style="position:absolute;right:0px;">
<span style="float: right ;margin-top: -20px;margin-right:5px;" @click="deleteEle(element)">
<i style="color:#ff4949" class="el-icon-delete"/>
</span>
</div>
</div>
</draggable>
</div>
<div :style="{width:width2}" class="dndList-list">
<h3>{{ list2Title }}</h3>
<draggable :list="filterList2" :options="{group:'article'}" class="dragArea">
<div v-for="element in filterList2" :key="element.id" class="list-complete-item">
<div class="list-complete-item-handle2" @click="pushEle(element)"> [{{ element.author }}] {{ element.title }}</div>
</div>
</draggable>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'DndList',
components: { draggable },
props: {
list1: {
type: Array,
default() {
return []
}
},
list2: {
type: Array,
default() {
return []
}
},
list1Title: {
type: String,
default: 'list1'
},
list2Title: {
type: String,
default: 'list2'
},
width1: {
type: String,
default: '48%'
},
width2: {
type: String,
default: '48%'
}
},
computed: {
filterList2() {
return this.list2.filter(v => {
if (this.isNotInList1(v)) {
return v
}
return false
})
}
},
methods: {
isNotInList1(v) {
return this.list1.every(k => v.id !== k.id)
},
isNotInList2(v) {
return this.list2.every(k => v.id !== k.id)
},
deleteEle(ele) {
for (const item of this.list1) {
if (item.id === ele.id) {
const index = this.list1.indexOf(item)
this.list1.splice(index, 1)
break
}
}
if (this.isNotInList2(ele)) {
this.list2.unshift(ele)
}
},
pushEle(ele) {
this.list1.push(ele)
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.dndList {
background: #fff;
padding-bottom: 40px;
&:after {
content: "";
display: table;
clear: both;
}
.dndList-list {
float: left;
padding-bottom: 30px;
&:first-of-type {
margin-right: 2%;
}
.dragArea {
margin-top: 15px;
min-height: 50px;
padding-bottom: 30px;
}
}
}
.list-complete-item {
cursor: pointer;
position: relative;
font-size: 14px;
padding: 5px 12px;
margin-top: 4px;
border: 1px solid #bfcbd9;
transition: all 1s;
}
.list-complete-item-handle {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 50px;
}
.list-complete-item-handle2 {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 20px;
}
.list-complete-item.sortable-chosen {
background: #4AB7BD;
}
.list-complete-item.sortable-ghost {
background: #30B08F;
}
.list-complete-enter,
.list-complete-leave-active {
opacity: 0;
}
</style>

View file

@ -0,0 +1,61 @@
<template>
<el-select ref="dragSelect" v-model="selectVal" v-bind="$attrs" class="drag-select" multiple v-on="$listeners">
<slot/>
</el-select>
</template>
<script>
import Sortable from 'sortablejs'
export default {
name: 'DragSelect',
props: {
value: {
type: Array,
required: true
}
},
computed: {
selectVal: {
get() {
return [...this.value]
},
set(val) {
this.$emit('input', [...val])
}
}
},
mounted() {
this.setSort()
},
methods: {
setSort() {
const el = this.$refs.dragSelect.$el.querySelectorAll('.el-select__tags > span')[0]
this.sortable = Sortable.create(el, {
ghostClass: 'sortable-ghost', // Class name for the drop placeholder,
setData: function(dataTransfer) {
dataTransfer.setData('Text', '')
// to avoid Firefox bug
// Detail see : https://github.com/RubaXa/Sortable/issues/1012
},
onEnd: evt => {
const targetRow = this.value.splice(evt.oldIndex, 1)[0]
this.value.splice(evt.newIndex, 0, targetRow)
}
})
}
}
}
</script>
<style scoped>
.drag-select >>> .sortable-ghost{
opacity: .8;
color: #fff!important;
background: #42b983!important;
}
.drag-select >>> .el-tag{
cursor: pointer;
}
</style>

View file

@ -0,0 +1,297 @@
<template>
<div :ref="id" :action="url" :id="id" class="dropzone">
<input type="file" name="file">
</div>
</template>
<script>
import Dropzone from 'dropzone'
import 'dropzone/dist/dropzone.css'
// import { getToken } from 'api/qiniu';
Dropzone.autoDiscover = false
export default {
props: {
id: {
type: String,
required: true
},
url: {
type: String,
required: true
},
clickable: {
type: Boolean,
default: true
},
defaultMsg: {
type: String,
default: '上传图片'
},
acceptedFiles: {
type: String,
default: ''
},
thumbnailHeight: {
type: Number,
default: 200
},
thumbnailWidth: {
type: Number,
default: 200
},
showRemoveLink: {
type: Boolean,
default: true
},
maxFilesize: {
type: Number,
default: 2
},
maxFiles: {
type: Number,
default: 3
},
autoProcessQueue: {
type: Boolean,
default: true
},
useCustomDropzoneOptions: {
type: Boolean,
default: false
},
defaultImg: {
default: '',
type: [String, Array]
},
couldPaste: {
type: Boolean,
default: false
}
},
data() {
return {
dropzone: '',
initOnce: true
}
},
watch: {
defaultImg(val) {
if (val.length === 0) {
this.initOnce = false
return
}
if (!this.initOnce) return
this.initImages(val)
this.initOnce = false
}
},
mounted() {
const element = document.getElementById(this.id)
const vm = this
this.dropzone = new Dropzone(element, {
clickable: this.clickable,
thumbnailWidth: this.thumbnailWidth,
thumbnailHeight: this.thumbnailHeight,
maxFiles: this.maxFiles,
maxFilesize: this.maxFilesize,
dictRemoveFile: 'Remove',
addRemoveLinks: this.showRemoveLink,
acceptedFiles: this.acceptedFiles,
autoProcessQueue: this.autoProcessQueue,
dictDefaultMessage: '<i style="margin-top: 3em;display: inline-block" class="material-icons">' + this.defaultMsg + '</i><br>Drop files here to upload',
dictMaxFilesExceeded: '只能一个图',
previewTemplate: '<div class="dz-preview dz-file-preview"> <div class="dz-image" style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" ><img style="width:' + this.thumbnailWidth + 'px;height:' + this.thumbnailHeight + 'px" data-dz-thumbnail /></div> <div class="dz-details"><div class="dz-size"><span data-dz-size></span></div> <div class="dz-progress"><span class="dz-upload" data-dz-uploadprogress></span></div> <div class="dz-error-message"><span data-dz-errormessage></span></div> <div class="dz-success-mark"> <i class="material-icons">done</i> </div> <div class="dz-error-mark"><i class="material-icons">error</i></div></div>',
init() {
const val = vm.defaultImg
if (!val) return
if (Array.isArray(val)) {
if (val.length === 0) return
val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v }
this.options.addedfile.call(this, mockFile)
this.options.thumbnail.call(this, mockFile, v)
mockFile.previewElement.classList.add('dz-success')
mockFile.previewElement.classList.add('dz-complete')
vm.initOnce = false
return true
})
} else {
const mockFile = { name: 'name', size: 12345, url: val }
this.options.addedfile.call(this, mockFile)
this.options.thumbnail.call(this, mockFile, val)
mockFile.previewElement.classList.add('dz-success')
mockFile.previewElement.classList.add('dz-complete')
vm.initOnce = false
}
},
accept: (file, done) => {
/* 七牛*/
// const token = this.$store.getters.token;
// getToken(token).then(response => {
// file.token = response.data.qiniu_token;
// file.key = response.data.qiniu_key;
// file.url = response.data.qiniu_url;
// done();
// })
done()
},
sending: (file, xhr, formData) => {
// formData.append('token', file.token);
// formData.append('key', file.key);
vm.initOnce = false
}
})
if (this.couldPaste) {
document.addEventListener('paste', this.pasteImg)
}
this.dropzone.on('success', file => {
vm.$emit('dropzone-success', file, vm.dropzone.element)
})
this.dropzone.on('addedfile', file => {
vm.$emit('dropzone-fileAdded', file)
})
this.dropzone.on('removedfile', file => {
vm.$emit('dropzone-removedFile', file)
})
this.dropzone.on('error', (file, error, xhr) => {
vm.$emit('dropzone-error', file, error, xhr)
})
this.dropzone.on('successmultiple', (file, error, xhr) => {
vm.$emit('dropzone-successmultiple', file, error, xhr)
})
},
destroyed() {
document.removeEventListener('paste', this.pasteImg)
this.dropzone.destroy()
},
methods: {
removeAllFiles() {
this.dropzone.removeAllFiles(true)
},
processQueue() {
this.dropzone.processQueue()
},
pasteImg(event) {
const items = (event.clipboardData || event.originalEvent.clipboardData).items
if (items[0].kind === 'file') {
this.dropzone.addFile(items[0].getAsFile())
}
},
initImages(val) {
if (!val) return
if (Array.isArray(val)) {
val.map((v, i) => {
const mockFile = { name: 'name' + i, size: 12345, url: v }
this.dropzone.options.addedfile.call(this.dropzone, mockFile)
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, v)
mockFile.previewElement.classList.add('dz-success')
mockFile.previewElement.classList.add('dz-complete')
return true
})
} else {
const mockFile = { name: 'name', size: 12345, url: val }
this.dropzone.options.addedfile.call(this.dropzone, mockFile)
this.dropzone.options.thumbnail.call(this.dropzone, mockFile, val)
mockFile.previewElement.classList.add('dz-success')
mockFile.previewElement.classList.add('dz-complete')
}
}
}
}
</script>
<style scoped>
.dropzone {
border: 2px solid #E5E5E5;
font-family: 'Roboto', sans-serif;
color: #777;
transition: background-color .2s linear;
padding: 5px;
}
.dropzone:hover {
background-color: #F6F6F6;
}
i {
color: #CCC;
}
.dropzone .dz-image img {
width: 100%;
height: 100%;
}
.dropzone input[name='file'] {
display: none;
}
.dropzone .dz-preview .dz-image {
border-radius: 0px;
}
.dropzone .dz-preview:hover .dz-image img {
transform: none;
-webkit-filter: none;
width: 100%;
height: 100%;
}
.dropzone .dz-preview .dz-details {
bottom: 0px;
top: 0px;
color: white;
background-color: rgba(33, 150, 243, 0.8);
transition: opacity .2s linear;
text-align: left;
}
.dropzone .dz-preview .dz-details .dz-filename span, .dropzone .dz-preview .dz-details .dz-size span {
background-color: transparent;
}
.dropzone .dz-preview .dz-details .dz-filename:not(:hover) span {
border: none;
}
.dropzone .dz-preview .dz-details .dz-filename:hover span {
background-color: transparent;
border: none;
}
.dropzone .dz-preview .dz-remove {
position: absolute;
z-index: 30;
color: white;
margin-left: 15px;
padding: 10px;
top: inherit;
bottom: 15px;
border: 2px white solid;
text-decoration: none;
text-transform: uppercase;
font-size: 0.8rem;
font-weight: 800;
letter-spacing: 1.1px;
opacity: 0;
}
.dropzone .dz-preview:hover .dz-remove {
opacity: 1;
}
.dropzone .dz-preview .dz-success-mark, .dropzone .dz-preview .dz-error-mark {
margin-left: -40px;
margin-top: -50px;
}
.dropzone .dz-preview .dz-success-mark i, .dropzone .dz-preview .dz-error-mark i {
color: white;
font-size: 5rem;
}
</style>

View file

@ -0,0 +1,86 @@
<template>
<div v-if="errorLogs.length>0">
<el-badge :is-dot="true" style="line-height: 30px;" @click.native="dialogTableVisible=true">
<el-button size="small" type="danger" class="bug-btn">
<svg
t="1492682037685"
class="bug-svg"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="1863"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="128"
height="128">
<path
d="M969.142857 548.571429q0 14.848-10.861714 25.709714t-25.709714 10.861714l-128 0q0 97.718857-38.290286 165.705143l118.857143 119.442286q10.861714 10.861714 10.861714 25.709714t-10.861714 25.709714q-10.276571 10.861714-25.709714 10.861714t-25.709714-10.861714l-113.152-112.566857q-2.852571 2.852571-8.557714 7.424t-23.990857 16.274286-37.156571 20.845714-46.848 16.566857-55.442286 7.424l0-512-73.142857 0 0 512q-29.147429 0-58.002286-7.716571t-49.700571-18.870857-37.705143-22.272-24.868571-18.578286l-8.557714-8.009143-104.557714 118.272q-11.446857 11.995429-27.428571 11.995429-13.714286 0-24.576-9.142857-10.861714-10.276571-11.702857-25.417143t8.850286-26.587429l115.419429-129.718857q-33.133714-65.133714-33.133714-156.562286l-128 0q-14.848 0-25.709714-10.861714t-10.861714-25.709714 10.861714-25.709714 25.709714-10.861714l128 0 0-168.009143-98.852571-98.852571q-10.861714-10.861714-10.861714-25.709714t10.861714-25.709714 25.709714-10.861714 25.709714 10.861714l98.852571 98.852571 482.304 0 98.852571-98.852571q10.861714-10.861714 25.709714-10.861714t25.709714 10.861714 10.861714 25.709714-10.861714 25.709714l-98.852571 98.852571 0 168.009143 128 0q14.848 0 25.709714 10.861714t10.861714 25.709714zM694.857143 219.428571l-365.714286 0q0-75.995429 53.430857-129.426286t129.426286-53.430857 129.426286 53.430857 53.430857 129.426286z"
p-id="1864"/>
</svg>
</el-button>
</el-badge>
<el-dialog :visible.sync="dialogTableVisible" title="Error Log" width="80%">
<el-table :data="errorLogs" border>
<el-table-column label="Message">
<template slot-scope="scope">
<div>
<span class="message-title">Msg:</span>
<el-tag type="danger">{{ scope.row.err.message }}</el-tag>
</div>
<br>
<div>
<span class="message-title" style="padding-right: 10px;">Info: </span>
<el-tag type="warning">{{ scope.row.vm.$vnode.tag }} error in {{ scope.row.info }}</el-tag>
</div>
<br>
<div>
<span class="message-title" style="padding-right: 16px;">Url: </span>
<el-tag type="success">{{ scope.row.url }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="Stack">
<template slot-scope="scope">
{{ scope.row.err.stack }}
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'ErrorLog',
data() {
return {
dialogTableVisible: false
}
},
computed: {
errorLogs() {
return this.$store.getters.errorLogs
}
}
}
</script>
<style scoped>
.bug-btn.el-button--small {
padding: 9px 10px;
}
.bug-svg {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.message-title {
font-size: 16px;
color: #333;
font-weight: bold;
padding-right: 8px;
}
</style>

View file

@ -0,0 +1,51 @@
<template>
<a href="https://github.com/PanJiaChen/vue-element-admin" target="_blank" class="github-corner" aria-label="View source on Github">
<svg
width="80"
height="80"
viewBox="0 0 250 250"
style="fill:#40c9c6; color:#fff;"
aria-hidden="true">
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"/>
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor"
style="transform-origin: 130px 106px;"
class="octo-arm"/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"/>
</svg>
</a>
</template>
<style scoped>
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0)
}
20%,
60% {
transform: rotate(-25deg)
}
40%,
80% {
transform: rotate(10deg)
}
}
@media (max-width:500px) {
.github-corner:hover .octo-arm {
animation: none
}
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out
}
}
</style>

View file

@ -0,0 +1,59 @@
<template>
<div>
<svg
:class="{'is-active':isActive}"
t="1492500959545"
class="hamburger"
style=""
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="1691"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="64"
height="64"
@click="toggleClick">
<path
d="M966.8023 568.849776 57.196677 568.849776c-31.397081 0-56.850799-25.452695-56.850799-56.850799l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 543.397081 998.200404 568.849776 966.8023 568.849776z"
p-id="1692"/>
<path
d="M966.8023 881.527125 57.196677 881.527125c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.849776 56.850799-56.849776l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.849776l0 0C1023.653099 856.07443 998.200404 881.527125 966.8023 881.527125z"
p-id="1693"/>
<path
d="M966.8023 256.17345 57.196677 256.17345c-31.397081 0-56.850799-25.452695-56.850799-56.849776l0 0c0-31.397081 25.452695-56.850799 56.850799-56.850799l909.605623 0c31.397081 0 56.849776 25.452695 56.849776 56.850799l0 0C1023.653099 230.720755 998.200404 256.17345 966.8023 256.17345z"
p-id="1694"/>
</svg>
</div>
</template>
<script>
export default {
name: 'Hamburger',
props: {
isActive: {
type: Boolean,
default: false
},
toggleClick: {
type: Function,
default: null
}
}
}
</script>
<style scoped>
.hamburger {
display: inline-block;
cursor: pointer;
width: 20px;
height: 20px;
transform: rotate(90deg);
transition: .38s;
transform-origin: 50% 50%;
}
.hamburger.is-active {
transform: rotate(0deg);
}
</style>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,19 @@
/**
* database64文件格式转换为2进制
*
* @param {[String]} data dataURL 的格式为 data:image/png;base64,****,逗号之前都是一些说明性的文字我们只需要逗号之后的就行了
* @param {[String]} mime [description]
* @return {[blob]} [description]
*/
export default function(data, mime) {
data = data.split(',')[1]
data = window.atob(data)
var ia = new Uint8Array(data.length)
for (var i = 0; i < data.length; i++) {
ia[i] = data.charCodeAt(i)
}
// canvas.toDataURL 返回的默认格式就是 image/png
return new Blob([ia], {
type: mime
})
}

View file

@ -0,0 +1,39 @@
/**
* 点击波纹效果
*
* @param {[event]} e [description]
* @param {[Object]} arg_opts [description]
* @return {[bollean]} [description]
*/
export default function(e, arg_opts) {
var opts = Object.assign({
ele: e.target, // 波纹作用元素
type: 'hit', // hit点击位置扩散center中心点扩展
bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
}, arg_opts)
var target = opts.ele
if (target) {
var rect = target.getBoundingClientRect()
var ripple = target.querySelector('.e-ripple')
if (!ripple) {
ripple = document.createElement('span')
ripple.className = 'e-ripple'
ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
target.appendChild(ripple)
} else {
ripple.className = 'e-ripple'
}
switch (opts.type) {
case 'center':
ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
break
default:
ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'
ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
}
ripple.style.backgroundColor = opts.bgc
ripple.className = 'e-ripple z-active'
return false
}
}

View file

@ -0,0 +1,232 @@
export default {
zh: {
hint: '点击,或拖动图片至此处',
loading: '正在上传……',
noSupported: '浏览器不支持该功能请使用IE10以上或其他现在浏览器',
success: '上传成功',
fail: '图片上传失败',
preview: '头像预览',
btn: {
off: '取消',
close: '关闭',
back: '上一步',
save: '保存'
},
error: {
onlyImg: '仅限图片格式',
outOfSize: '单文件大小不能超过 ',
lowestPx: '图片最低像素为(宽*高):'
}
},
'zh-tw': {
hint: '點擊,或拖動圖片至此處',
loading: '正在上傳……',
noSupported: '瀏覽器不支持該功能請使用IE10以上或其他現代瀏覽器',
success: '上傳成功',
fail: '圖片上傳失敗',
preview: '頭像預覽',
btn: {
off: '取消',
close: '關閉',
back: '上一步',
save: '保存'
},
error: {
onlyImg: '僅限圖片格式',
outOfSize: '單文件大小不能超過 ',
lowestPx: '圖片最低像素為(寬*高):'
}
},
en: {
hint: 'Click or drag the file here to upload',
loading: 'Uploading…',
noSupported: 'Browser is not supported, please use IE10+ or other browsers',
success: 'Upload success',
fail: 'Upload failed',
preview: 'Preview',
btn: {
off: 'Cancel',
close: 'Close',
back: 'Back',
save: 'Save'
},
error: {
onlyImg: 'Image only',
outOfSize: 'Image exceeds size limit: ',
lowestPx: 'Image\'s size is too low. Expected at least: '
}
},
ro: {
hint: 'Atinge sau trage fișierul aici',
loading: 'Se încarcă',
noSupported: 'Browser-ul tău nu suportă acest feature. Te rugăm încearcă cu alt browser.',
success: 'S-a încărcat cu succes',
fail: 'A apărut o problemă la încărcare',
preview: 'Previzualizează',
btn: {
off: 'Anulează',
close: 'Închide',
back: 'Înapoi',
save: 'Salvează'
},
error: {
onlyImg: 'Doar imagini',
outOfSize: 'Imaginea depășește limita de: ',
loewstPx: 'Imaginea este prea mică; Minim: '
}
},
ru: {
hint: 'Нажмите, или перетащите файл в это окно',
loading: 'Загружаю……',
noSupported: 'Ваш браузер не поддерживается, пожалуйста, используйте IE10 + или другие браузеры',
success: 'Загрузка выполнена успешно',
fail: 'Ошибка загрузки',
preview: 'Предпросмотр',
btn: {
off: 'Отменить',
close: 'Закрыть',
back: 'Назад',
save: 'Сохранить'
},
error: {
onlyImg: 'Только изображения',
outOfSize: 'Изображение превышает предельный размер: ',
lowestPx: 'Минимальный размер изображения: '
}
},
'pt-br': {
hint: 'Clique ou arraste o arquivo aqui para carregar',
loading: 'Carregando…',
noSupported: 'Browser não suportado, use o IE10+ ou outro browser',
success: 'Sucesso ao carregar imagem',
fail: 'Falha ao carregar imagem',
preview: 'Pré-visualizar',
btn: {
off: 'Cancelar',
close: 'Fechar',
back: 'Voltar',
save: 'Salvar'
},
error: {
onlyImg: 'Apenas imagens',
outOfSize: 'A imagem excede o limite de tamanho: ',
lowestPx: 'O tamanho da imagem é muito pequeno. Tamanho mínimo: '
}
},
fr: {
hint: 'Cliquez ou glissez le fichier ici.',
loading: 'Téléchargement…',
noSupported: 'Votre navigateur n\'est pas supporté. Utilisez IE10 + ou un autre navigateur s\'il vous plaît.',
success: 'Téléchargement réussit',
fail: 'Téléchargement echoué',
preview: 'Aperçu',
btn: {
off: 'Annuler',
close: 'Fermer',
back: 'Retour',
save: 'Enregistrer'
},
error: {
onlyImg: 'Image uniquement',
outOfSize: 'L\'image sélectionnée dépasse la taille maximum: ',
lowestPx: 'L\'image sélectionnée est trop petite. Dimensions attendues: '
}
},
nl: {
hint: 'Klik hier of sleep een afbeelding in dit vlak',
loading: 'Uploaden…',
noSupported: 'Je browser wordt helaas niet ondersteund. Gebruik IE10+ of een andere browser.',
success: 'Upload succesvol',
fail: 'Upload mislukt',
preview: 'Voorbeeld',
btn: {
off: 'Annuleren',
close: 'Sluiten',
back: 'Terug',
save: 'Opslaan'
},
error: {
onlyImg: 'Alleen afbeeldingen',
outOfSize: 'De afbeelding is groter dan: ',
lowestPx: 'De afbeelding is te klein! Minimale afmetingen: '
}
},
tr: {
hint: 'Tıkla veya yüklemek istediğini buraya sürükle',
loading: 'Yükleniyor…',
noSupported: 'Tarayıcı desteklenmiyor, lütfen IE10+ veya farklı tarayıcı kullanın',
success: 'Yükleme başarılı',
fail: 'Yüklemede hata oluştu',
preview: 'Önizle',
btn: {
off: 'İptal',
close: 'Kapat',
back: 'Geri',
save: 'Kaydet'
},
error: {
onlyImg: 'Sadece resim',
outOfSize: 'Resim yükleme limitini aşıyor: ',
lowestPx: 'Resmin boyutu çok küçük. En az olması gereken: '
}
},
'es-MX': {
hint: 'Selecciona o arrastra una imagen',
loading: 'Subiendo...',
noSupported: 'Tu navegador no es soportado, porfavor usa IE10+ u otros navegadores mas recientes',
success: 'Subido exitosamente',
fail: 'Sucedió un error',
preview: 'Vista previa',
btn: {
off: 'Cancelar',
close: 'Cerrar',
back: 'Atras',
save: 'Guardar'
},
error: {
onlyImg: 'Unicamente imagenes',
outOfSize: 'La imagen excede el tamaño maximo:',
lowestPx: 'La imagen es demasiado pequeño. Se espera por lo menos:'
}
},
de: {
hint: 'Klick hier oder zieh eine Datei hier rein zum Hochladen',
loading: 'Hochladen…',
noSupported: 'Browser wird nicht unterstützt, bitte verwende IE10+ oder andere Browser',
success: 'Upload erfolgreich',
fail: 'Upload fehlgeschlagen',
preview: 'Vorschau',
btn: {
off: 'Abbrechen',
close: 'Schließen',
back: 'Zurück',
save: 'Speichern'
},
error: {
onlyImg: 'Nur Bilder',
outOfSize: 'Das Bild ist zu groß: ',
lowestPx: 'Das Bild ist zu klein. Mindestens: '
}
},
ja: {
hint: 'クリック・ドラッグしてファイルをアップロード',
loading: 'アップロード中...',
noSupported: 'このブラウザは対応されていません。IE10+かその他の主要ブラウザをお使いください。',
success: 'アップロード成功',
fail: 'アップロード失敗',
preview: 'プレビュー',
btn: {
off: 'キャンセル',
close: '閉じる',
back: '戻る',
save: '保存'
},
error: {
onlyImg: '画像のみ',
outOfSize: '画像サイズが上限を超えています。上限: ',
lowestPx: '画像が小さすぎます。最小サイズ: '
}
}
}

View file

@ -0,0 +1,7 @@
export default {
'jpg': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'svg': 'image/svg+xml',
'psd': 'image/photoshop'
}

View file

@ -0,0 +1,72 @@
<template>
<div class="json-editor">
<textarea ref="textarea"/>
</div>
</template>
<script>
import CodeMirror from 'codemirror'
import 'codemirror/addon/lint/lint.css'
import 'codemirror/lib/codemirror.css'
import 'codemirror/theme/rubyblue.css'
require('script-loader!jsonlint')
import 'codemirror/mode/javascript/javascript'
import 'codemirror/addon/lint/lint'
import 'codemirror/addon/lint/json-lint'
export default {
name: 'JsonEditor',
/* eslint-disable vue/require-prop-types */
props: ['value'],
data() {
return {
jsonEditor: false
}
},
watch: {
value(value) {
const editor_value = this.jsonEditor.getValue()
if (value !== editor_value) {
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
}
}
},
mounted() {
this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
lineNumbers: true,
mode: 'application/json',
gutters: ['CodeMirror-lint-markers'],
theme: 'rubyblue',
lint: true
})
this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
this.jsonEditor.on('change', cm => {
this.$emit('changed', cm.getValue())
this.$emit('input', cm.getValue())
})
},
methods: {
getValue() {
return this.jsonEditor.getValue()
}
}
}
</script>
<style scoped>
.json-editor{
height: 100%;
position: relative;
}
.json-editor >>> .CodeMirror {
height: auto;
min-height: 300px;
}
.json-editor >>> .CodeMirror-scroll{
min-height: 300px;
}
.json-editor >>> .cm-s-rubyblue span.cm-string {
color: #F08047;
}
</style>

View file

@ -0,0 +1,89 @@
<template>
<div class="board-column">
<div class="board-column-header">
{{ headerText }}
</div>
<draggable
:list="list"
:options="options"
class="board-column-content">
<div v-for="element in list" :key="element.id" class="board-item">
{{ element.name }} {{ element.id }}
</div>
</draggable>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: 'DragKanbanDemo',
components: {
draggable
},
props: {
headerText: {
type: String,
default: 'Header'
},
options: {
type: Object,
default() {
return {}
}
},
list: {
type: Array,
default() {
return []
}
}
}
}
</script>
<style lang="scss">
.board-column {
min-width: 300px;
min-height: 100px;
height: auto;
overflow: hidden;
background: #f0f0f0;
border-radius: 3px;
.board-column-header {
height: 50px;
line-height: 50px;
overflow: hidden;
padding: 0 20px;
text-align: center;
background: #333;
color: #fff;
border-radius: 3px 3px 0 0;
}
.board-column-content {
height: auto;
overflow: hidden;
border: 10px solid transparent;
min-height: 60px;
display: flex;
justify-content: flex-start;
flex-direction: column;
align-items: center;
.board-item {
cursor: pointer;
width: 100%;
height: 64px;
margin: 5px 0;
background-color: #fff;
text-align: left;
line-height: 54px;
padding: 5px 10px;
box-sizing: border-box;
box-shadow: 0px 1px 3px 0 rgba(0,0,0,0.2);
}
}
}
</style>

View file

@ -0,0 +1,40 @@
<template>
<el-dropdown trigger="click" class="international" @command="handleSetLanguage">
<div>
<svg-icon class-name="international-icon" icon-class="language" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="language==='zh'" command="zh">中文</el-dropdown-item>
<el-dropdown-item :disabled="language==='en'" command="en">English</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
computed: {
language() {
return this.$store.getters.language
}
},
methods: {
handleSetLanguage(lang) {
this.$i18n.locale = lang
this.$store.dispatch('setLanguage', lang)
this.$message({
message: 'Switch Language Success',
type: 'success'
})
}
}
}
</script>
<style scoped>
.international-icon {
font-size: 20px;
cursor: pointer;
vertical-align: -5px!important;
}
</style>

View file

@ -0,0 +1,354 @@
<template>
<div :class="computedClasses" class="material-input__component">
<div :class="{iconClass:icon}">
<i v-if="icon" :class="['el-icon-' + icon]" class="el-input__icon material-input__icon"/>
<input
v-if="type === 'email'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:required="required"
type="email"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'url'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:required="required"
type="url"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'number'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:step="step"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:max="max"
:min="min"
:minlength="minlength"
:maxlength="maxlength"
:required="required"
type="number"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'password'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:max="max"
:min="min"
:required="required"
type="password"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'tel'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:required="required"
type="tel"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<input
v-if="type === 'text'"
:name="name"
:placeholder="fillPlaceHolder"
v-model="currentValue"
:readonly="readonly"
:disabled="disabled"
:autoComplete="autoComplete"
:minlength="minlength"
:maxlength="maxlength"
:required="required"
type="text"
class="material-input"
@focus="handleMdFocus"
@blur="handleMdBlur"
@input="handleModelInput">
<span class="material-input-bar"/>
<label class="material-label">
<slot/>
</label>
</div>
</div>
</template>
<script>
// source:https://github.com/wemake-services/vue-material-input/blob/master/src/components/MaterialInput.vue
export default {
name: 'MdInput',
props: {
/* eslint-disable */
icon: String,
name: String,
type: {
type: String,
default: 'text'
},
value: [String, Number],
placeholder: String,
readonly: Boolean,
disabled: Boolean,
min: String,
max: String,
step: String,
minlength: Number,
maxlength: Number,
required: {
type: Boolean,
default: true
},
autoComplete: {
type: String,
default: 'off'
},
validateEvent: {
type: Boolean,
default: true
}
},
data() {
return {
currentValue: this.value,
focus: false,
fillPlaceHolder: null
}
},
computed: {
computedClasses() {
return {
'material--active': this.focus,
'material--disabled': this.disabled,
'material--raised': Boolean(this.focus || this.currentValue) // has value
}
}
},
watch: {
value(newValue) {
this.currentValue = newValue
}
},
methods: {
handleModelInput(event) {
const value = event.target.value
this.$emit('input', value)
if (this.$parent.$options.componentName === 'ElFormItem') {
if (this.validateEvent) {
this.$parent.$emit('el.form.change', [value])
}
}
this.$emit('change', value)
},
handleMdFocus(event) {
this.focus = true
this.$emit('focus', event)
if (this.placeholder && this.placeholder !== '') {
this.fillPlaceHolder = this.placeholder
}
},
handleMdBlur(event) {
this.focus = false
this.$emit('blur', event)
this.fillPlaceHolder = null
if (this.$parent.$options.componentName === 'ElFormItem') {
if (this.validateEvent) {
this.$parent.$emit('el.form.blur', [this.currentValue])
}
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
// Fonts:
$font-size-base: 16px;
$font-size-small: 18px;
$font-size-smallest: 12px;
$font-weight-normal: normal;
$font-weight-bold: bold;
$apixel: 1px;
// Utils
$spacer: 12px;
$transition: 0.2s ease all;
$index: 0px;
$index-has-icon: 30px;
// Theme:
$color-white: white;
$color-grey: #9E9E9E;
$color-grey-light: #E0E0E0;
$color-blue: #2196F3;
$color-red: #F44336;
$color-black: black;
// Base clases:
%base-bar-pseudo {
content: '';
height: 1px;
width: 0;
bottom: 0;
position: absolute;
transition: $transition;
}
// Mixins:
@mixin slided-top() {
top: - ($font-size-base + $spacer);
left: 0;
font-size: $font-size-base;
font-weight: $font-weight-bold;
}
// Component:
.material-input__component {
margin-top: 36px;
position: relative;
* {
box-sizing: border-box;
}
.iconClass {
.material-input__icon {
position: absolute;
left: 0;
line-height: $font-size-base;
color: $color-blue;
top: $spacer;
width: $index-has-icon;
height: $font-size-base;
font-size: $font-size-base;
font-weight: $font-weight-normal;
pointer-events: none;
}
.material-label {
left: $index-has-icon;
}
.material-input {
text-indent: $index-has-icon;
}
}
.material-input {
font-size: $font-size-base;
padding: $spacer $spacer $spacer - $apixel * 10 $spacer / 2;
display: block;
width: 100%;
border: none;
line-height: 1;
border-radius: 0;
&:focus {
outline: none;
border: none;
border-bottom: 1px solid transparent; // fixes the height issue
}
}
.material-label {
font-weight: $font-weight-normal;
position: absolute;
pointer-events: none;
left: $index;
top: 0;
transition: $transition;
font-size: $font-size-small;
}
.material-input-bar {
position: relative;
display: block;
width: 100%;
&:before {
@extend %base-bar-pseudo;
left: 50%;
}
&:after {
@extend %base-bar-pseudo;
right: 50%;
}
}
// Disabled state:
&.material--disabled {
.material-input {
border-bottom-style: dashed;
}
}
// Raised state:
&.material--raised {
.material-label {
@include slided-top();
}
}
// Active state:
&.material--active {
.material-input-bar {
&:before,
&:after {
width: 50%;
}
}
}
}
.material-input__component {
background: $color-white;
.material-input {
background: none;
color: $color-black;
text-indent: $index;
border-bottom: 1px solid $color-grey-light;
}
.material-label {
color: $color-grey;
}
.material-input-bar {
&:before,
&:after {
background: $color-blue;
}
}
// Active state:
&.material--active {
.material-label {
color: $color-blue;
}
}
// Errors:
&.material--has-errors {
&.material--active .material-label {
color: $color-red;
}
.material-input-bar {
&:before,
&:after {
background: transparent;
}
}
}
}
</style>

View file

@ -0,0 +1,123 @@
<template>
<div :style="{height:height+'px',zIndex:zIndex}" class="simplemde-container">
<textarea :id="id"/>
</div>
</template>
<script>
import 'font-awesome/css/font-awesome.min.css'
import 'simplemde/dist/simplemde.min.css'
import SimpleMDE from 'simplemde'
export default {
name: 'SimplemdeMd',
props: {
value: {
type: String,
default: ''
},
id: {
type: String,
required: false,
default: 'markdown-editor-' + +new Date()
},
autofocus: {
type: Boolean,
default: false
},
placeholder: {
type: String,
default: ''
},
height: {
type: Number,
default: 150
},
zIndex: {
type: Number,
default: 10
},
toolbar: {
type: Array,
default: function() {
return []
}
}
},
data() {
return {
simplemde: null,
hasChange: false
}
},
watch: {
value(val) {
if (val === this.simplemde.value() && !this.hasChange) return
this.simplemde.value(val)
}
},
mounted() {
this.simplemde = new SimpleMDE({
element: document.getElementById(this.id),
autoDownloadFontAwesome: false,
autofocus: this.autofocus,
toolbar: this.toolbar.length > 0 ? this.toolbar : undefined,
spellChecker: false,
insertTexts: {
link: ['[', ']( )']
},
// hideIcons: ['guide', 'heading', 'quote', 'image', 'preview', 'side-by-side', 'fullscreen'],
placeholder: this.placeholder
})
if (this.value) {
this.simplemde.value(this.value)
}
this.simplemde.codemirror.on('change', () => {
if (this.hasChange) {
this.hasChange = true
}
this.$emit('input', this.simplemde.value())
})
},
destroyed() {
this.simplemde.toTextArea()
this.simplemde = null
}
}
</script>
<style scoped>
.simplemde-container>>>.CodeMirror {
min-height: 150px;
line-height: 20px;
}
.simplemde-container>>>.CodeMirror-scroll {
min-height: 150px;
}
.simplemde-container>>>.CodeMirror-code {
padding-bottom: 40px;
}
.simplemde-container>>>.editor-statusbar {
display: none;
}
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-link {
color: #1890ff;
}
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-string.cm-url {
color: #2d3b4d;
}
.simplemde-container>>>.CodeMirror .CodeMirror-code .cm-formatting-link-string.cm-url {
padding: 0 2px;
color: #E61E1E;
}
.simplemde-container >>> .editor-toolbar.fullscreen,
.simplemde-container >>> .CodeMirror-fullscreen {
z-index: 1003;
}
</style>

View file

@ -0,0 +1,99 @@
<template>
<div :class="{'hidden':hidden}" class="pagination-container">
<el-pagination
:background="background"
:current-page.sync="currentPage"
:page-size.sync="pageSize"
:layout="layout"
:total="total"
v-bind="$attrs"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"/>
</div>
</template>
<script>
import { scrollTo } from '@/utils/scrollTo'
export default {
name: 'Pagination',
props: {
total: {
required: true,
type: Number
},
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 20
},
pageSizes: {
type: Array,
default() {
return [10, 20, 30, 50]
}
},
layout: {
type: String,
default: 'total, sizes, prev, pager, next, jumper'
},
background: {
type: Boolean,
default: true
},
autoScroll: {
type: Boolean,
default: true
},
hidden: {
type: Boolean,
default: false
}
},
computed: {
currentPage: {
get() {
return this.page
},
set(val) {
this.$emit('update:page', val)
}
},
pageSize: {
get() {
return this.limit
},
set(val) {
this.$emit('update:limit', val)
}
}
},
methods: {
handleSizeChange(val) {
this.$emit('pagination', { page: this.currentPage, limit: val })
if (this.autoScroll) {
scrollTo(0, 800)
}
},
handleCurrentChange(val) {
this.$emit('pagination', { page: val, limit: this.pageSize })
if (this.autoScroll) {
scrollTo(0, 800)
}
}
}
}
</script>
<style scoped>
.pagination-container {
background: #fff;
padding: 32px 16px;
}
.pagination-container.hidden {
display: none;
}
</style>

View file

@ -0,0 +1,140 @@
<template>
<div :style="{zIndex:zIndex,height:height,width:width}" class="pan-item">
<div class="pan-info">
<div class="pan-info-roles-container">
<slot/>
</div>
</div>
<img :src="image" class="pan-thumb">
</div>
</template>
<script>
export default {
name: 'PanThumb',
props: {
image: {
type: String,
required: true
},
zIndex: {
type: Number,
default: 1
},
width: {
type: String,
default: '150px'
},
height: {
type: String,
default: '150px'
}
}
}
</script>
<style scoped>
.pan-item {
width: 200px;
height: 200px;
border-radius: 50%;
display: inline-block;
position: relative;
cursor: default;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.pan-info-roles-container {
padding: 20px;
text-align: center;
}
.pan-thumb {
width: 100%;
height: 100%;
background-size: 100%;
border-radius: 50%;
overflow: hidden;
position: absolute;
transform-origin: 95% 40%;
transition: all 0.3s ease-in-out;
}
.pan-thumb:after {
content: '';
width: 8px;
height: 8px;
position: absolute;
border-radius: 50%;
top: 40%;
left: 95%;
margin: -4px 0 0 -4px;
background: radial-gradient(ellipse at center, rgba(14, 14, 14, 1) 0%, rgba(125, 126, 125, 1) 100%);
box-shadow: 0 0 1px rgba(255, 255, 255, 0.9);
}
.pan-info {
position: absolute;
width: inherit;
height: inherit;
border-radius: 50%;
overflow: hidden;
box-shadow: inset 0 0 0 5px rgba(0, 0, 0, 0.05);
}
.pan-info h3 {
color: #fff;
text-transform: uppercase;
position: relative;
letter-spacing: 2px;
font-size: 18px;
margin: 0 60px;
padding: 22px 0 0 0;
height: 85px;
font-family: 'Open Sans', Arial, sans-serif;
text-shadow: 0 0 1px #fff, 0 1px 2px rgba(0, 0, 0, 0.3);
}
.pan-info p {
color: #fff;
padding: 10px 5px;
font-style: italic;
margin: 0 30px;
font-size: 12px;
border-top: 1px solid rgba(255, 255, 255, 0.5);
}
.pan-info p a {
display: block;
color: #333;
width: 80px;
height: 80px;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
color: #fff;
font-style: normal;
font-weight: 700;
text-transform: uppercase;
font-size: 9px;
letter-spacing: 1px;
padding-top: 24px;
margin: 7px auto 0;
font-family: 'Open Sans', Arial, sans-serif;
opacity: 0;
transition: transform 0.3s ease-in-out 0.2s, opacity 0.3s ease-in-out 0.2s, background 0.2s linear 0s;
transform: translateX(60px) rotate(90deg);
}
.pan-info p a:hover {
background: rgba(255, 255, 255, 0.5);
}
.pan-item:hover .pan-thumb {
transform: rotate(-110deg);
}
.pan-item:hover .pan-info p a {
opacity: 1;
transform: translateX(0px) rotate(0deg);
}
</style>

View file

@ -0,0 +1,78 @@
<template>
<div>
<svg
t="1508738709248"
class="screenfull-svg"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="2069"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="32"
height="32"
@click="click">
<path
d="M333.493443 428.647617 428.322206 333.832158 262.572184 168.045297 366.707916 64.444754 64.09683 64.444754 63.853283 366.570793 167.283957 262.460644Z"
p-id="2070"/>
<path
d="M854.845439 760.133334 688.61037 593.95864 593.805144 688.764889 759.554142 854.56096 655.44604 958.161503 958.055079 958.161503 958.274066 656.035464Z"
p-id="2071"/>
<path
d="M688.535669 428.550403 854.31025 262.801405 957.935352 366.921787 957.935352 64.34754 655.809313 64.081481 759.919463 167.535691 593.70793 333.731874Z"
p-id="2072"/>
<path
d="M333.590658 594.033341 167.8171 759.804852 64.218604 655.67219 64.218604 958.270996 366.342596 958.502263 262.234493 855.071589 428.421466 688.86108Z"
p-id="2073"/>
</svg>
</div>
</template>
<script>
import screenfull from 'screenfull'
export default {
name: 'Screenfull',
props: {
width: {
type: Number,
default: 22
},
height: {
type: Number,
default: 22
},
fill: {
type: String,
default: '#48576a'
}
},
data() {
return {
isFullscreen: false
}
},
methods: {
click() {
if (!screenfull.enabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
})
return false
}
screenfull.toggle()
}
}
}
</script>
<style scoped>
.screenfull-svg {
display: inline-block;
cursor: pointer;
fill: #5a5e66;;
width: 20px;
height: 20px;
vertical-align: 10px;
}
</style>

View file

@ -0,0 +1,92 @@
<template>
<el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
<slot/>
</el-scrollbar>
</template>
<script>
const tagAndTagSpacing = 4 // tagAndTagSpacing
export default {
name: 'ScrollPane',
data() {
return {
left: 0
}
},
methods: {
handleScroll(e) {
const eventDelta = e.wheelDelta || -e.deltaY * 40
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
$scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
},
moveToTarget(currentTag) {
const $container = this.$refs.scrollContainer.$el
const $containerWidth = $container.offsetWidth
const $scrollWrapper = this.$refs.scrollContainer.$refs.wrap
const tagList = this.$parent.$refs.tag
let firstTag = null
let lastTag = null
let prevTag = null
let nextTag = null
// find first tag and last tag
if (tagList.length > 0) {
firstTag = tagList[0]
lastTag = tagList[tagList.length - 1]
}
// find preTag and nextTag
for (let i = 0; i < tagList.length; i++) {
if (tagList[i] === currentTag) {
if (i === 0) {
nextTag = tagList[i].length > 1 && tagList[i + 1]
} else if (i === tagList.length - 1) {
prevTag = tagList[i].length > 1 && tagList[i - 1]
} else {
prevTag = tagList[i - 1]
nextTag = tagList[i + 1]
}
break
}
}
if (firstTag === currentTag) {
$scrollWrapper.scrollLeft = 0
} else if (lastTag === currentTag) {
$scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
} else {
// the tag's offsetLeft after of nextTag
const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
// the tag's offsetLeft before of prevTag
const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
$scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
} else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
$scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
}
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.scroll-container {
white-space: nowrap;
position: relative;
overflow: hidden;
width: 100%;
/deep/ {
.el-scrollbar__bar {
bottom: 0px;
}
.el-scrollbar__wrap {
height: 49px;
}
}
}
</style>

View file

@ -0,0 +1,100 @@
<template>
<div :class="{active:isActive}" class="share-dropdown-menu">
<div class="share-dropdown-menu-wrapper">
<span class="share-dropdown-menu-title" @click.self="clickTitle">{{ title }}</span>
<div v-for="(item,index) of items" :key="index" class="share-dropdown-menu-item">
<a v-if="item.href" :href="item.href" target="_blank">{{ item.title }}</a>
<span v-else>{{ item.title }}</span>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
items: {
type: Array,
default: function() {
return []
}
},
title: {
type: String,
default: 'vue'
}
},
data() {
return {
isActive: false
}
},
methods: {
clickTitle() {
this.isActive = !this.isActive
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" >
$n: 8; //items.length
$t: .1s;
.share-dropdown-menu {
width: 250px;
position: relative;
z-index: 1;
&-title {
width: 100%;
display: block;
cursor: pointer;
background: black;
color: white;
height: 60px;
line-height: 60px;
font-size: 20px;
text-align: center;
z-index: 2;
transform: translate3d(0,0,0);
}
&-wrapper {
position: relative;
}
&-item {
text-align: center;
position: absolute;
width: 100%;
background: #e0e0e0;
line-height: 60px;
height: 60px;
cursor: pointer;
font-size: 20px;
opacity: 1;
transition: transform 0.28s ease;
&:hover {
background: black;
color: white;
}
@for $i from 1 through $n {
&:nth-of-type(#{$i}) {
z-index: -1;
transition-delay: $i*$t;
transform: translate3d(0, -60px, 0);
}
}
}
&.active {
.share-dropdown-menu-wrapper {
z-index: 1;
}
.share-dropdown-menu-item {
@for $i from 1 through $n {
&:nth-of-type(#{$i}) {
transition-delay: ($n - $i)*$t;
transform: translate3d(0, ($i - 1)*60px, 0);
}
}
}
}
}
</style>

View file

@ -0,0 +1,55 @@
<template>
<el-dropdown trigger="click" @command="handleSetSize">
<div>
<svg-icon class-name="size-icon" icon-class="size" />
</div>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :disabled="size==='medium'" command="medium">Medium</el-dropdown-item>
<el-dropdown-item :disabled="size==='small'" command="small">Small</el-dropdown-item>
<el-dropdown-item :disabled="size==='mini'" command="mini">Mini</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template>
<script>
export default {
computed: {
size() {
return this.$store.getters.size
}
},
methods: {
handleSetSize(size) {
this.$ELEMENT.size = size
this.$store.dispatch('setSize', size)
this.refreshView()
this.$message({
message: 'Switch Size Success',
type: 'success'
})
},
refreshView() {
// In order to make the cached page re-rendered
this.$store.dispatch('delAllCachedViews', this.$route)
const { fullPath } = this.$route
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
})
})
}
}
}
</script>
<style scoped>
.size-icon {
font-size: 20px;
cursor: pointer;
vertical-align: -4px!important;
}
</style>

View file

@ -0,0 +1,84 @@
<template>
<div :style="{height:height+'px',zIndex:zIndex}">
<div :class="className" :style="{top:stickyTop+'px',zIndex:zIndex,position:position,width:width,height:height+'px'}">
<slot>
<div>sticky</div>
</slot>
</div>
</div>
</template>
<script>
export default {
name: 'Sticky',
props: {
stickyTop: {
type: Number,
default: 0
},
zIndex: {
type: Number,
default: 1
},
className: {
type: String,
default: ''
}
},
data() {
return {
active: false,
position: '',
width: undefined,
height: undefined,
isSticky: false
}
},
mounted() {
this.height = this.$el.getBoundingClientRect().height
window.addEventListener('scroll', this.handleScroll)
window.addEventListener('resize', this.handleReize)
},
activated() {
this.handleScroll()
},
destroyed() {
window.removeEventListener('scroll', this.handleScroll)
window.removeEventListener('resize', this.handleReize)
},
methods: {
sticky() {
if (this.active) {
return
}
this.position = 'fixed'
this.active = true
this.width = this.width + 'px'
this.isSticky = true
},
reset() {
if (!this.active) {
return
}
this.position = ''
this.width = 'auto'
this.active = false
this.isSticky = false
},
handleScroll() {
this.width = this.$el.getBoundingClientRect().width
const offsetTop = this.$el.getBoundingClientRect().top
if (offsetTop < this.stickyTop) {
this.sticky()
return
}
this.reset()
},
handleReize() {
if (this.isSticky) {
this.width = this.$el.getBoundingClientRect().width + 'px'
}
}
}
}
</script>

View file

@ -0,0 +1,43 @@
<template>
<svg :class="svgClass" aria-hidden="true">
<use :xlink:href="iconName"/>
</svg>
</template>
<script>
export default {
name: 'SvgIcon',
props: {
iconClass: {
type: String,
required: true
},
className: {
type: String,
default: ''
}
},
computed: {
iconName() {
return `#icon-${this.iconClass}`
},
svgClass() {
if (this.className) {
return 'svg-icon ' + this.className
} else {
return 'svg-icon'
}
}
}
}
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
</style>

View file

@ -0,0 +1,113 @@
<template>
<a :class="className" class="link--mallki" href="#">
{{ text }}
<span :data-letters="text"/>
<span :data-letters="text"/>
</a>
</template>
<script>
export default {
props: {
className: {
type: String,
default: ''
},
text: {
type: String,
default: 'vue-element-admin'
}
}
}
</script>
<style>
/* Mallki */
.link--mallki {
font-weight: 800;
color: #4dd9d5;
font-family: 'Dosis', sans-serif;
-webkit-transition: color 0.5s 0.25s;
transition: color 0.5s 0.25s;
overflow: hidden;
position: relative;
display: inline-block;
line-height: 1;
outline: none;
text-decoration: none;
}
.link--mallki:hover {
-webkit-transition: none;
transition: none;
color: transparent;
}
.link--mallki::before {
content: '';
width: 100%;
height: 6px;
margin: -3px 0 0 0;
background: #3888fa;
position: absolute;
left: 0;
top: 50%;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
-webkit-transition: -webkit-transform 0.4s;
transition: transform 0.4s;
-webkit-transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
transition-timing-function: cubic-bezier(0.7, 0, 0.3, 1);
}
.link--mallki:hover::before {
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
.link--mallki span {
position: absolute;
height: 50%;
width: 100%;
left: 0;
top: 0;
overflow: hidden;
}
.link--mallki span::before {
content: attr(data-letters);
color: red;
position: absolute;
left: 0;
width: 100%;
color: #3888fa;
-webkit-transition: -webkit-transform 0.5s;
transition: transform 0.5s;
}
.link--mallki span:nth-child(2) {
top: 50%;
}
.link--mallki span:first-child::before {
top: 0;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
.link--mallki span:nth-child(2)::before {
bottom: 0;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
.link--mallki:hover span::before {
-webkit-transition-delay: 0.3s;
transition-delay: 0.3s;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
-webkit-transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
transition-timing-function: cubic-bezier(0.2, 1, 0.3, 1);
}
</style>

View file

@ -0,0 +1,144 @@
<template>
<el-color-picker
v-model="theme"
class="theme-picker"
popper-class="theme-picker-dropdown"/>
</template>
<script>
const version = require('element-ui/package.json').version // element-ui version from node_modules
const ORIGINAL_THEME = '#409EFF' // default color
export default {
data() {
return {
chalk: '', // content of theme-chalk css
theme: ORIGINAL_THEME
}
},
watch: {
theme(val, oldVal) {
if (typeof val !== 'string') return
const themeCluster = this.getThemeCluster(val.replace('#', ''))
const originalCluster = this.getThemeCluster(oldVal.replace('#', ''))
console.log(themeCluster, originalCluster)
const getHandler = (variable, id) => {
return () => {
const originalCluster = this.getThemeCluster(ORIGINAL_THEME.replace('#', ''))
const newStyle = this.updateStyle(this[variable], originalCluster, themeCluster)
let styleTag = document.getElementById(id)
if (!styleTag) {
styleTag = document.createElement('style')
styleTag.setAttribute('id', id)
document.head.appendChild(styleTag)
}
styleTag.innerText = newStyle
}
}
const chalkHandler = getHandler('chalk', 'chalk-style')
if (!this.chalk) {
const url = `https://unpkg.com/element-ui@${version}/lib/theme-chalk/index.css`
this.getCSSString(url, chalkHandler, 'chalk')
} else {
chalkHandler()
}
const styles = [].slice.call(document.querySelectorAll('style'))
.filter(style => {
const text = style.innerText
return new RegExp(oldVal, 'i').test(text) && !/Chalk Variables/.test(text)
})
styles.forEach(style => {
const { innerText } = style
if (typeof innerText !== 'string') return
style.innerText = this.updateStyle(innerText, originalCluster, themeCluster)
})
this.$message({
message: '换肤成功',
type: 'success'
})
}
},
methods: {
updateStyle(style, oldCluster, newCluster) {
let newStyle = style
oldCluster.forEach((color, index) => {
newStyle = newStyle.replace(new RegExp(color, 'ig'), newCluster[index])
})
return newStyle
},
getCSSString(url, callback, variable) {
const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
if (xhr.readyState === 4 && xhr.status === 200) {
this[variable] = xhr.responseText.replace(/@font-face{[^}]+}/, '')
callback()
}
}
xhr.open('GET', url)
xhr.send()
},
getThemeCluster(theme) {
const tintColor = (color, tint) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
if (tint === 0) { // when primary color is in its rgb space
return [red, green, blue].join(',')
} else {
red += Math.round(tint * (255 - red))
green += Math.round(tint * (255 - green))
blue += Math.round(tint * (255 - blue))
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
}
const shadeColor = (color, shade) => {
let red = parseInt(color.slice(0, 2), 16)
let green = parseInt(color.slice(2, 4), 16)
let blue = parseInt(color.slice(4, 6), 16)
red = Math.round((1 - shade) * red)
green = Math.round((1 - shade) * green)
blue = Math.round((1 - shade) * blue)
red = red.toString(16)
green = green.toString(16)
blue = blue.toString(16)
return `#${red}${green}${blue}`
}
const clusters = [theme]
for (let i = 0; i <= 9; i++) {
clusters.push(tintColor(theme, Number((i / 10).toFixed(2))))
}
clusters.push(shadeColor(theme, 0.1))
return clusters
}
}
}
</script>
<style>
.theme-picker .el-color-picker__trigger {
vertical-align: middle;
}
.theme-picker-dropdown .el-color-dropdown__link-btn {
display: none;
}
</style>

View file

@ -0,0 +1,103 @@
<template>
<div class="upload-container">
<el-button :style="{background:color,borderColor:color}" icon="el-icon-upload" size="mini" type="primary" @click=" dialogVisible=true">上传图片
</el-button>
<el-dialog :visible.sync="dialogVisible">
<el-upload
:multiple="true"
:file-list="fileList"
:show-file-list="true"
:on-remove="handleRemove"
:on-success="handleSuccess"
:before-upload="beforeUpload"
class="editor-slide-upload"
action="https://httpbin.org/post"
list-type="picture-card">
<el-button size="small" type="primary">点击上传</el-button>
</el-upload>
<el-button @click="dialogVisible = false"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</el-dialog>
</div>
</template>
<script>
// import { getToken } from 'api/qiniu'
export default {
name: 'EditorSlideUpload',
props: {
color: {
type: String,
default: '#1890ff'
}
},
data() {
return {
dialogVisible: false,
listObj: {},
fileList: []
}
},
methods: {
checkAllSuccess() {
return Object.keys(this.listObj).every(item => this.listObj[item].hasSuccess)
},
handleSubmit() {
const arr = Object.keys(this.listObj).map(v => this.listObj[v])
if (!this.checkAllSuccess()) {
this.$message('请等待所有图片上传成功 或 出现了网络问题,请刷新页面重新上传!')
return
}
this.$emit('successCBK', arr)
this.listObj = {}
this.fileList = []
this.dialogVisible = false
},
handleSuccess(response, file) {
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) {
this.listObj[objKeyArr[i]].url = response.files.file
this.listObj[objKeyArr[i]].hasSuccess = true
return
}
}
},
handleRemove(file) {
const uid = file.uid
const objKeyArr = Object.keys(this.listObj)
for (let i = 0, len = objKeyArr.length; i < len; i++) {
if (this.listObj[objKeyArr[i]].uid === uid) {
delete this.listObj[objKeyArr[i]]
return
}
}
},
beforeUpload(file) {
const _self = this
const _URL = window.URL || window.webkitURL
const fileName = file.uid
this.listObj[fileName] = {}
return new Promise((resolve, reject) => {
const img = new Image()
img.src = _URL.createObjectURL(file)
img.onload = function() {
_self.listObj[fileName] = { hasSuccess: false, uid: file.uid, width: this.width, height: this.height }
}
resolve(true)
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.editor-slide-upload {
margin-bottom: 20px;
/deep/ .el-upload--picture-card {
width: 100%;
}
}
</style>

View file

@ -0,0 +1,204 @@
<template>
<div :class="{fullscreen:fullscreen}" class="tinymce-container editor-container">
<textarea :id="tinymceId" class="tinymce-textarea"/>
<div class="editor-custom-btn-container">
<editorImage color="#1890ff" class="editor-upload-btn" @successCBK="imageSuccessCBK"/>
</div>
</div>
</template>
<script>
import editorImage from './components/editorImage'
import plugins from './plugins'
import toolbar from './toolbar'
export default {
name: 'Tinymce',
components: { editorImage },
props: {
id: {
type: String,
default: function() {
return 'vue-tinymce-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
}
},
value: {
type: String,
default: ''
},
toolbar: {
type: Array,
required: false,
default() {
return []
}
},
menubar: {
type: String,
default: 'file edit insert view format table'
},
height: {
type: Number,
required: false,
default: 360
}
},
data() {
return {
hasChange: false,
hasInit: false,
tinymceId: this.id,
fullscreen: false,
languageTypeList: {
'en': 'en',
'zh': 'zh_CN'
}
}
},
computed: {
language() {
return this.languageTypeList[this.$store.getters.language]
}
},
watch: {
value(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() =>
window.tinymce.get(this.tinymceId).setContent(val || ''))
}
},
language() {
this.destroyTinymce()
this.$nextTick(() => this.initTinymce())
}
},
mounted() {
this.initTinymce()
},
activated() {
this.initTinymce()
},
deactivated() {
this.destroyTinymce()
},
destroyed() {
this.destroyTinymce()
},
methods: {
initTinymce() {
const _this = this
window.tinymce.init({
language: this.language,
selector: `#${this.tinymceId}`,
height: this.height,
body_class: 'panel-body ',
object_resizing: false,
toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
menubar: this.menubar,
plugins: plugins,
end_container_on_empty_block: true,
powerpaste_word_import: 'clean',
code_dialog_height: 450,
code_dialog_width: 1000,
advlist_bullet_styles: 'square',
advlist_number_styles: 'default',
imagetools_cors_hosts: ['www.tinymce.com', 'codepen.io'],
default_link_target: '_blank',
link_title: false,
nonbreaking_force_tab: true, // inserting nonbreaking space &nbsp; need Nonbreaking Space Plugin
init_instance_callback: editor => {
if (_this.value) {
editor.setContent(_this.value)
}
_this.hasInit = true
editor.on('NodeChange Change KeyUp SetContent', () => {
this.hasChange = true
this.$emit('input', editor.getContent())
})
},
setup(editor) {
editor.on('FullscreenStateChanged', (e) => {
_this.fullscreen = e.state
})
}
//
// images_dataimg_filter(img) {
// setTimeout(() => {
// const $image = $(img);
// $image.removeAttr('width');
// $image.removeAttr('height');
// if ($image[0].height && $image[0].width) {
// $image.attr('data-wscntype', 'image');
// $image.attr('data-wscnh', $image[0].height);
// $image.attr('data-wscnw', $image[0].width);
// $image.addClass('wscnph');
// }
// }, 0);
// return img
// },
// images_upload_handler(blobInfo, success, failure, progress) {
// progress(0);
// const token = _this.$store.getters.token;
// getToken(token).then(response => {
// const url = response.data.qiniu_url;
// const formData = new FormData();
// formData.append('token', response.data.qiniu_token);
// formData.append('key', response.data.qiniu_key);
// formData.append('file', blobInfo.blob(), url);
// upload(formData).then(() => {
// success(url);
// progress(100);
// })
// }).catch(err => {
// failure('')
// console.log(err);
// });
// },
})
},
destroyTinymce() {
if (window.tinymce.get(this.tinymceId)) {
window.tinymce.get(this.tinymceId).destroy()
}
},
setContent(value) {
window.tinymce.get(this.tinymceId).setContent(value)
},
getContent() {
window.tinymce.get(this.tinymceId).getContent()
},
imageSuccessCBK(arr) {
const _this = this
arr.forEach(v => {
window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" src="${v.url}" >`)
})
}
}
}
</script>
<style scoped>
.tinymce-container {
position: relative;
}
.tinymce-container>>>.mce-fullscreen {
z-index: 10000;
}
.tinymce-textarea {
visibility: hidden;
z-index: -1;
}
.editor-custom-btn-container {
position: absolute;
right: 4px;
top: 4px;
/*z-index: 2005;*/
}
.fullscreen .editor-custom-btn-container {
z-index: 10000;
position: fixed;
}
.editor-upload-btn {
display: inline-block;
}
</style>

View file

@ -0,0 +1,7 @@
// Any plugins you want to use has to be imported
// Detail plugins list see https://www.tinymce.com/docs/plugins/
// Custom builds see https://www.tinymce.com/download/custom-builds/
const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools importcss insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount']
export default plugins

View file

@ -0,0 +1,6 @@
// Here is a list of the toolbar
// Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols
const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen']
export default toolbar

View file

@ -0,0 +1,29 @@
/**
* @Author: jianglei
* @Date: 2017-10-12 12:06:49
*/
'use strict'
import Vue from 'vue'
export default function treeToArray(data, expandAll, parent = null, level = null) {
let tmp = []
Array.from(data).forEach(function(record) {
if (record._expanded === undefined) {
Vue.set(record, '_expanded', expandAll)
}
let _level = 1
if (level !== undefined && level !== null) {
_level = level + 1
}
Vue.set(record, '_level', _level)
// 如果有父元素
if (parent) {
Vue.set(record, 'parent', parent)
}
tmp.push(record)
if (record.children && record.children.length > 0) {
const children = treeToArray(record.children, expandAll, record, _level)
tmp = tmp.concat(children)
}
})
return tmp
}

View file

@ -0,0 +1,127 @@
<template>
<el-table :data="formatData" :row-style="showRow" v-bind="$attrs">
<el-table-column v-if="columns.length===0" width="150">
<template slot-scope="scope">
<span v-for="space in scope.row._level" :key="space" class="ms-tree-space"/>
<span v-if="iconShow(0,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
<i v-else class="el-icon-minus"/>
</span>
{{ scope.$index }}
</template>
</el-table-column>
<el-table-column v-for="(column, index) in columns" v-else :key="column.value" :label="column.text" :width="column.width">
<template slot-scope="scope">
<!-- Todo -->
<!-- eslint-disable-next-line vue/no-confusing-v-for-v-if -->
<span v-for="space in scope.row._level" v-if="index === 0" :key="space" class="ms-tree-space"/>
<span v-if="iconShow(index,scope.row)" class="tree-ctrl" @click="toggleExpanded(scope.$index)">
<i v-if="!scope.row._expanded" class="el-icon-plus"/>
<i v-else class="el-icon-minus"/>
</span>
{{ scope.row[column.value] }}
</template>
</el-table-column>
<slot/>
</el-table>
</template>
<script>
/**
Auth: Lei.j1ang
Created: 2018/1/19-13:59
*/
import treeToArray from './eval'
export default {
name: 'TreeTable',
props: {
/* eslint-disable */
data: {
type: [Array, Object],
required: true
},
columns: {
type: Array,
default: () => []
},
evalFunc: Function,
evalArgs: Array,
expandAll: {
type: Boolean,
default: false
}
},
computed: {
//
formatData: function() {
let tmp
if (!Array.isArray(this.data)) {
tmp = [this.data]
} else {
tmp = this.data
}
const func = this.evalFunc || treeToArray
const args = this.evalArgs ? Array.concat([tmp, this.expandAll], this.evalArgs) : [tmp, this.expandAll]
return func.apply(null, args)
}
},
methods: {
showRow: function(row) {
const show = (row.row.parent ? (row.row.parent._expanded && row.row.parent._show) : true)
row.row._show = show
return show ? 'animation:treeTableShow 1s;-webkit-animation:treeTableShow 1s;' : 'display:none;'
},
//
toggleExpanded: function(trIndex) {
const record = this.formatData[trIndex]
record._expanded = !record._expanded
},
//
iconShow(index, record) {
return (index === 0 && record.children && record.children.length > 0)
}
}
}
</script>
<style rel="stylesheet/css">
@keyframes treeTableShow {
from {opacity: 0;}
to {opacity: 1;}
}
@-webkit-keyframes treeTableShow {
from {opacity: 0;}
to {opacity: 1;}
}
</style>
<style lang="scss" rel="stylesheet/scss" scoped>
$color-blue: #2196F3;
$space-width: 18px;
.ms-tree-space {
position: relative;
top: 1px;
display: inline-block;
font-style: normal;
font-weight: 400;
line-height: 1;
width: $space-width;
height: 14px;
&::before {
content: ""
}
}
.processContainer{
width: 100%;
height: 100%;
}
table td {
line-height: 26px;
}
.tree-ctrl{
position: relative;
cursor: pointer;
color: $color-blue;
margin-left: -$space-width;
}
</style>

View file

@ -0,0 +1,89 @@
## 写在前面
此组件仅提供一个创建TreeTable的解决思路
## prop说明
#### *data*
**必填**
原始数据,要求是一个数组或者对象
```javascript
[{
key1: value1,
key2: value2,
children: [{
key1: value1
},
{
key1: value1
}]
},
{
key1: value1
}]
```
或者
```javascript
{
key1: value1,
key2: value2,
children: [{
key1: value1
},
{
key1: value1
}]
}
```
#### columns
列属性,要求是一个数组
1. text: 显示在表头的文字
2. value: 对应data的key。treeTable将显示相应的value
3. width: 每列的宽度,为一个数字(可选)
如果你想要每个字段都有自定义的样式或者嵌套其他组件columns可不提供直接像在el-table一样写即可如果没有自定义内容提供columns将更加的便捷方便
如果你有几个字段是需要自定义的几个不需要那么可以将不需要自定义的字段放入columns将需要自定义的内容放入到slot中详情见后文
```javascript
[{
value:string,
text:string,
width:number
},{
value:string,
text:string,
width:number
}]
```
#### expandAll
是否默认全部展开boolean值默认为false
#### evalFunc
解析函数function非必须
如果不提供,将使用默认的[evalFunc](./eval.js)
如果提供了evalFunc,那么会用提供的evalFunc去解析data并返回treeTable渲染所需要的值。如何编写一个evalFunc请参考[*eval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/components/TreeTable/eval.js)或[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)
#### evalArgs
解析函数的参数,是一个数组
**请注意自定义的解析函数参数第一个为this.data第二个参数为 this.expandAll,你不需要在evalArgs填写。一定记住这两个参数是强制性的并且位置不可颠倒** *this.data为需要解析的数据this.expandAll为是否默认展开*
如你的解析函数需要的参数为`(this.data, this.expandAll,1,2,3,4)`,那么你只需要将`[1,2,3,4]`赋值给`evalArgs`就可以了
如果你的解析函数参数只有`(this.data, this.expandAll)`,那么就可以不用填写evalArgs了
具体可参考[*customEval.js*](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customEval.js)的函数参数和[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)的`evalArgs`属性值
## slot
这是一个自定义列的插槽。
默认情况下treeTable只有一行行展示数据的功能。但是一般情况下我们会要给行加上一个操作按钮或者根据当行数据展示不同的样式这时我们就需要自定义列了。请参考[customTreeTable](https://github.com/PanJiaChen/vue-element-admin/blob/master/src/views/table/treeTable/customTreeTable.vue)[实例效果](https://panjiachen.github.io/vue-element-admin/#/table/tree-table)
`slot`和`columns属性`可同时存在,columns里面的数据列会在slot自定义列的左边展示
## 其他
如果有其他的需求,请参考[el-table](http://element-cn.eleme.io/#/en-US/component/table)的api自行修改index.vue

View file

@ -0,0 +1,132 @@
<template>
<div class="upload-container">
<el-upload
:data="dataObj"
:multiple="false"
:show-file-list="false"
:on-success="handleImageSuccess"
class="image-uploader"
drag
action="https://httpbin.org/post">
<i class="el-icon-upload"/>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<div class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl+'?imageView2/1/w/200/h/200'">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div>
</div>
</div>
</div>
</template>
<script>
//
import { getToken } from '@/api/qiniu'
export default {
name: 'SingleImageUpload',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
}
},
computed: {
imageUrl() {
return this.value
}
},
methods: {
rmImage() {
this.emitInput('')
},
emitInput(val) {
this.$emit('input', val)
},
handleImageSuccess() {
this.emitInput(this.tempUrl)
},
beforeUpload() {
const _self = this
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key
const token = response.data.qiniu_token
_self._data.dataObj.token = token
_self._data.dataObj.key = key
this.tempUrl = response.data.qiniu_url
resolve(true)
}).catch(err => {
console.log(err)
reject(false)
})
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "src/styles/mixin.scss";
.upload-container {
width: 100%;
position: relative;
@include clearfix;
.image-uploader {
width: 60%;
float: left;
}
.image-preview {
width: 200px;
height: 200px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
}
</style>

View file

@ -0,0 +1,127 @@
<template>
<div class="singleImageUpload2 upload-container">
<el-upload
:data="dataObj"
:multiple="false"
:show-file-list="false"
:on-success="handleImageSuccess"
class="image-uploader"
drag
action="https://httpbin.org/post">
<i class="el-icon-upload"/>
<div class="el-upload__text">Drag或<em>点击上传</em></div>
</el-upload>
<div v-show="imageUrl.length>0" class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div>
</div>
</div>
</div>
</template>
<script>
import { getToken } from '@/api/qiniu'
export default {
name: 'SingleImageUpload2',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
}
},
computed: {
imageUrl() {
return this.value
}
},
methods: {
rmImage() {
this.emitInput('')
},
emitInput(val) {
this.$emit('input', val)
},
handleImageSuccess() {
this.emitInput(this.tempUrl)
},
beforeUpload() {
const _self = this
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key
const token = response.data.qiniu_token
_self._data.dataObj.token = token
_self._data.dataObj.key = key
this.tempUrl = response.data.qiniu_url
resolve(true)
}).catch(() => {
reject(false)
})
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.upload-container {
width: 100%;
height: 100%;
position: relative;
.image-uploader {
height: 100%;
}
.image-preview {
width: 100%;
height: 100%;
position: absolute;
left: 0px;
top: 0px;
border: 1px dashed #d9d9d9;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
}
</style>

View file

@ -0,0 +1,154 @@
<template>
<div class="upload-container">
<el-upload
:data="dataObj"
:multiple="false"
:show-file-list="false"
:on-success="handleImageSuccess"
class="image-uploader"
drag
action="https://httpbin.org/post">
<i class="el-icon-upload"/>
<div class="el-upload__text">将文件拖到此处<em>点击上传</em></div>
</el-upload>
<div class="image-preview image-app-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div>
</div>
</div>
<div class="image-preview">
<div v-show="imageUrl.length>1" class="image-preview-wrapper">
<img :src="imageUrl">
<div class="image-preview-action">
<i class="el-icon-delete" @click="rmImage"/>
</div>
</div>
</div>
</div>
</template>
<script>
import { getToken } from '@/api/qiniu'
export default {
name: 'SingleImageUpload3',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
tempUrl: '',
dataObj: { token: '', key: '' }
}
},
computed: {
imageUrl() {
return this.value
}
},
methods: {
rmImage() {
this.emitInput('')
},
emitInput(val) {
this.$emit('input', val)
},
handleImageSuccess(file) {
this.emitInput(file.files.file)
},
beforeUpload() {
const _self = this
return new Promise((resolve, reject) => {
getToken().then(response => {
const key = response.data.qiniu_key
const token = response.data.qiniu_token
_self._data.dataObj.token = token
_self._data.dataObj.key = key
this.tempUrl = response.data.qiniu_url
resolve(true)
}).catch(err => {
console.log(err)
reject(false)
})
})
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
@import "~@/styles/mixin.scss";
.upload-container {
width: 100%;
position: relative;
@include clearfix;
.image-uploader {
width: 35%;
float: left;
}
.image-preview {
width: 200px;
height: 200px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.image-preview-wrapper {
position: relative;
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.image-preview-action {
position: absolute;
width: 100%;
height: 100%;
left: 0;
top: 0;
cursor: default;
text-align: center;
color: #fff;
opacity: 0;
font-size: 20px;
background-color: rgba(0, 0, 0, .5);
transition: opacity .3s;
cursor: pointer;
text-align: center;
line-height: 200px;
.el-icon-delete {
font-size: 36px;
}
}
&:hover {
.image-preview-action {
opacity: 1;
}
}
}
.image-app-preview {
width: 320px;
height: 180px;
position: relative;
border: 1px dashed #d9d9d9;
float: left;
margin-left: 50px;
.app-fake-conver {
height: 44px;
position: absolute;
width: 100%; // background: rgba(0, 0, 0, .1);
text-align: center;
line-height: 64px;
color: #fff;
}
}
}
</style>

View file

@ -0,0 +1,145 @@
<template>
<div>
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
<div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
Drop excel file here or
<el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">Browse</el-button>
</div>
</div>
</template>
<script>
import XLSX from 'xlsx'
export default {
props: {
beforeUpload: Function, // eslint-disable-line
onSuccess: Function// eslint-disable-line
},
data() {
return {
loading: false,
excelData: {
header: null,
results: null
}
}
},
methods: {
generateData({ header, results }) {
this.excelData.header = header
this.excelData.results = results
this.onSuccess && this.onSuccess(this.excelData)
},
handleDrop(e) {
e.stopPropagation()
e.preventDefault()
if (this.loading) return
const files = e.dataTransfer.files
if (files.length !== 1) {
this.$message.error('Only support uploading one file!')
return
}
const rawFile = files[0] // only use files[0]
if (!this.isExcel(rawFile)) {
this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
return false
}
this.upload(rawFile)
e.stopPropagation()
e.preventDefault()
},
handleDragover(e) {
e.stopPropagation()
e.preventDefault()
e.dataTransfer.dropEffect = 'copy'
},
handleUpload() {
this.$refs['excel-upload-input'].click()
},
handleClick(e) {
const files = e.target.files
const rawFile = files[0] // only use files[0]
if (!rawFile) return
this.upload(rawFile)
},
upload(rawFile) {
this.$refs['excel-upload-input'].value = null // fix can't select the same excel
if (!this.beforeUpload) {
this.readerData(rawFile)
return
}
const before = this.beforeUpload(rawFile)
if (before) {
this.readerData(rawFile)
}
},
readerData(rawFile) {
this.loading = true
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = e => {
const data = e.target.result
const fixedData = this.fixData(data)
const workbook = XLSX.read(btoa(fixedData), { type: 'base64' })
const firstSheetName = workbook.SheetNames[0]
const worksheet = workbook.Sheets[firstSheetName]
const header = this.getHeaderRow(worksheet)
const results = XLSX.utils.sheet_to_json(worksheet)
this.generateData({ header, results })
this.loading = false
resolve()
}
reader.readAsArrayBuffer(rawFile)
})
},
fixData(data) {
let o = ''
let l = 0
const w = 10240
for (; l < data.byteLength / w; ++l) o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w)))
o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w)))
return o
},
getHeaderRow(sheet) {
const headers = []
const range = XLSX.utils.decode_range(sheet['!ref'])
let C
const R = range.s.r
/* start in the first row */
for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
/* find the cell in the first row */
let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
headers.push(hdr)
}
return headers
},
isExcel(file) {
return /\.(xlsx|xls|csv)$/.test(file.name)
}
}
}
</script>
<style scoped>
.excel-upload-input{
display: none;
z-index: -9999;
}
.drop{
border: 2px dashed #bbb;
width: 600px;
height: 160px;
line-height: 160px;
margin: 0 auto;
font-size: 24px;
border-radius: 5px;
text-align: center;
color: #bbb;
position: relative;
}
</style>

View file

@ -0,0 +1,49 @@
// Inspired by https://github.com/Inndy/vue-clipboard2
const Clipboard = require('clipboard')
if (!Clipboard) {
throw new Error('you should npm install `clipboard` --save at first ')
}
export default {
bind(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
const clipboard = new Clipboard(el, {
text() { return binding.value },
action() { return binding.arg === 'cut' ? 'cut' : 'copy' }
})
clipboard.on('success', e => {
const callback = el._v_clipboard_success
callback && callback(e) // eslint-disable-line
})
clipboard.on('error', e => {
const callback = el._v_clipboard_error
callback && callback(e) // eslint-disable-line
})
el._v_clipboard = clipboard
}
},
update(el, binding) {
if (binding.arg === 'success') {
el._v_clipboard_success = binding.value
} else if (binding.arg === 'error') {
el._v_clipboard_error = binding.value
} else {
el._v_clipboard.text = function() { return binding.value }
el._v_clipboard.action = function() { return binding.arg === 'cut' ? 'cut' : 'copy' }
}
},
unbind(el, binding) {
if (binding.arg === 'success') {
delete el._v_clipboard_success
} else if (binding.arg === 'error') {
delete el._v_clipboard_error
} else {
el._v_clipboard.destroy()
delete el._v_clipboard
}
}
}

View file

@ -0,0 +1,13 @@
import Clipboard from './clipboard'
const install = function(Vue) {
Vue.directive('Clipboard', Clipboard)
}
if (window.Vue) {
window.clipboard = Clipboard
Vue.use(install); // eslint-disable-line
}
Clipboard.install = install
export default Clipboard

View file

@ -0,0 +1,77 @@
export default{
bind(el, binding, vnode) {
const dialogHeaderEl = el.querySelector('.el-dialog__header')
const dragDom = el.querySelector('.el-dialog')
dialogHeaderEl.style.cssText += ';cursor:move;'
dragDom.style.cssText += ';top:0px;'
// 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
const getStyle = (function() {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
dialogHeaderEl.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - dialogHeaderEl.offsetLeft
const disY = e.clientY - dialogHeaderEl.offsetTop
const dragDomWidth = dragDom.offsetWidth
const dragDomHeight = dragDom.offsetHeight
const screenWidth = document.body.clientWidth
const screenHeight = document.body.clientHeight
const minDragDomLeft = dragDom.offsetLeft
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
const minDragDomTop = dragDom.offsetTop
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
// 获取到的值带px 正则匹配替换
let styL = getStyle(dragDom, 'left')
let styT = getStyle(dragDom, 'top')
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
document.onmousemove = function(e) {
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
// 边界处理
if (-(left) > minDragDomLeft) {
left = -minDragDomLeft
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft
}
if (-(top) > minDragDomTop) {
top = -minDragDomTop
} else if (top > maxDragDomTop) {
top = maxDragDomTop
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`
// emit onDrag event
vnode.child.$emit('dragDialog')
}
document.onmouseup = function(e) {
document.onmousemove = null
document.onmouseup = null
}
}
}
}

View file

@ -0,0 +1,13 @@
import drag from './drag'
const install = function(Vue) {
Vue.directive('el-drag-dialog', drag)
}
if (window.Vue) {
window['el-drag-dialog'] = drag
Vue.use(install); // eslint-disable-line
}
drag.install = install
export default drag

View file

@ -0,0 +1,13 @@
import permission from './permission'
const install = function(Vue) {
Vue.directive('permission', permission)
}
if (window.Vue) {
window['permission'] = permission
Vue.use(install); // eslint-disable-line
}
permission.install = install
export default permission

View file

@ -0,0 +1,23 @@
import store from '@/store'
export default{
inserted(el, binding, vnode) {
const { value } = binding
const roles = store.getters && store.getters.roles
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value
const hasPermission = roles.some(role => {
return permissionRoles.includes(role)
})
if (!hasPermission) {
el.parentNode && el.parentNode.removeChild(el)
}
} else {
throw new Error(`need roles! Like v-permission="['admin','editor']"`)
}
}
}

Some files were not shown because too many files have changed in this diff Show more