mirror of
https://github.com/shawncai/ERP.git
synced 2024-09-20 06:56:07 +08:00
first commit
This commit is contained in:
commit
6a2b662b88
17
.babelrc
Normal file
17
.babelrc
Normal 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
14
.editorconfig
Normal 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
6
.eslintignore
Normal 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
195
.eslintrc.js
Normal 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
21
.gitignore
vendored
Normal 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
10
.postcssrc.js
Normal 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
5
.travis.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
language: node_js
|
||||
node_js: stable
|
||||
script: npm run test
|
||||
notifications:
|
||||
email: false
|
21
LICENSE
Normal file
21
LICENSE
Normal 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
219
README.md
Normal 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
231
README.zh-CN.md
Normal 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
67
build/build.js
Normal 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
64
build/check-versions.js
Normal 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
BIN
build/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
108
build/utils.js
Normal file
108
build/utils.js
Normal 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
5
build/vue-loader.conf.js
Normal 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
107
build/webpack.base.conf.js
Normal 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
98
build/webpack.dev.conf.js
Normal 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
188
build/webpack.prod.conf.js
Normal 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
6
config/dev.env.js
Normal 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
88
config/index.js
Normal 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
6
config/prod.env.js
Normal 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
5
config/sit.env.js
Normal 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
BIN
favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
16
index.html
Normal file
16
index.html
Normal 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
133
package.json
Normal 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
11
src/App.vue
Normal 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
96
src/api/area.js
Normal 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
41
src/api/article.js
Normal 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
89
src/api/bigdata.js
Normal 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
60
src/api/client.js
Normal 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
99
src/api/complaint.js
Normal 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
46
src/api/discount.js
Normal 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
244
src/api/employee.js
Normal 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
196
src/api/goods.js
Normal 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
39
src/api/login.js
Normal 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
57
src/api/lostUnits.js
Normal 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
37
src/api/map.js
Normal 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
153
src/api/order.js
Normal 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
8
src/api/qiniu.js
Normal 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
9
src/api/remoteSearch.js
Normal 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
411
src/api/repair.js
Normal 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
57
src/api/roles.js
Normal 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
65
src/api/store.js
Normal 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
9
src/api/transaction.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function fetchList(query) {
|
||||
return request({
|
||||
url: '/transaction/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
BIN
src/assets/401_images/401.gif
Normal file
BIN
src/assets/401_images/401.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
BIN
src/assets/404_images/404.png
Normal file
BIN
src/assets/404_images/404.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
BIN
src/assets/404_images/404_cloud.png
Normal file
BIN
src/assets/404_images/404_cloud.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 KiB |
BIN
src/assets/custom-theme/fonts/element-icons.ttf
Normal file
BIN
src/assets/custom-theme/fonts/element-icons.ttf
Normal file
Binary file not shown.
BIN
src/assets/custom-theme/fonts/element-icons.woff
Normal file
BIN
src/assets/custom-theme/fonts/element-icons.woff
Normal file
Binary file not shown.
1
src/assets/custom-theme/index.css
Normal file
1
src/assets/custom-theme/index.css
Normal file
File diff suppressed because one or more lines are too long
BIN
src/assets/orders/test.jpeg
Normal file
BIN
src/assets/orders/test.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
116
src/components/BackToTop/index.vue
Normal file
116
src/components/BackToTop/index.vue
Normal 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>
|
73
src/components/Breadcrumb/index.vue
Normal file
73
src/components/Breadcrumb/index.vue
Normal 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>
|
156
src/components/Charts/keyboard.vue
Normal file
156
src/components/Charts/keyboard.vue
Normal 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>
|
227
src/components/Charts/lineMarker.vue
Normal file
227
src/components/Charts/lineMarker.vue
Normal 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>
|
271
src/components/Charts/mixChart.vue
Normal file
271
src/components/Charts/mixChart.vue
Normal 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>
|
32
src/components/Charts/mixins/resize.js
Normal file
32
src/components/Charts/mixins/resize.js
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
158
src/components/DndList/index.vue
Normal file
158
src/components/DndList/index.vue
Normal 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>
|
61
src/components/DragSelect/index.vue
Normal file
61
src/components/DragSelect/index.vue
Normal 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>
|
297
src/components/Dropzone/index.vue
Normal file
297
src/components/Dropzone/index.vue
Normal 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>
|
86
src/components/ErrorLog/index.vue
Normal file
86
src/components/ErrorLog/index.vue
Normal 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>
|
51
src/components/GithubCorner/index.vue
Normal file
51
src/components/GithubCorner/index.vue
Normal 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>
|
59
src/components/Hamburger/index.vue
Normal file
59
src/components/Hamburger/index.vue
Normal 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>
|
1420
src/components/ImageCropper/index.vue
Normal file
1420
src/components/ImageCropper/index.vue
Normal file
File diff suppressed because it is too large
Load diff
19
src/components/ImageCropper/utils/data2blob.js
Normal file
19
src/components/ImageCropper/utils/data2blob.js
Normal 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
|
||||
})
|
||||
}
|
39
src/components/ImageCropper/utils/effectRipple.js
Normal file
39
src/components/ImageCropper/utils/effectRipple.js
Normal 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
|
||||
}
|
||||
}
|
232
src/components/ImageCropper/utils/language.js
Normal file
232
src/components/ImageCropper/utils/language.js
Normal 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: '画像が小さすぎます。最小サイズ: '
|
||||
}
|
||||
}
|
||||
}
|
7
src/components/ImageCropper/utils/mimes.js
Normal file
7
src/components/ImageCropper/utils/mimes.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default {
|
||||
'jpg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'gif': 'image/gif',
|
||||
'svg': 'image/svg+xml',
|
||||
'psd': 'image/photoshop'
|
||||
}
|
72
src/components/JsonEditor/index.vue
Normal file
72
src/components/JsonEditor/index.vue
Normal 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>
|
89
src/components/Kanban/index.vue
Normal file
89
src/components/Kanban/index.vue
Normal 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>
|
||||
|
40
src/components/LangSelect/index.vue
Normal file
40
src/components/LangSelect/index.vue
Normal 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>
|
||||
|
354
src/components/MDinput/index.vue
Normal file
354
src/components/MDinput/index.vue
Normal 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>
|
123
src/components/MarkdownEditor/index.vue
Normal file
123
src/components/MarkdownEditor/index.vue
Normal 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>
|
99
src/components/Pagination/index.vue
Normal file
99
src/components/Pagination/index.vue
Normal 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>
|
140
src/components/PanThumb/index.vue
Normal file
140
src/components/PanThumb/index.vue
Normal 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>
|
78
src/components/Screenfull/index.vue
Normal file
78
src/components/Screenfull/index.vue
Normal 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>
|
92
src/components/ScrollPane/index.vue
Normal file
92
src/components/ScrollPane/index.vue
Normal 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>
|
100
src/components/Share/dropdownMenu.vue
Normal file
100
src/components/Share/dropdownMenu.vue
Normal 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>
|
55
src/components/SizeSelect/index.vue
Normal file
55
src/components/SizeSelect/index.vue
Normal 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>
|
||||
|
84
src/components/Sticky/index.vue
Normal file
84
src/components/Sticky/index.vue
Normal 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>
|
43
src/components/SvgIcon/index.vue
Normal file
43
src/components/SvgIcon/index.vue
Normal 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>
|
113
src/components/TextHoverEffect/Mallki.vue
Normal file
113
src/components/TextHoverEffect/Mallki.vue
Normal 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>
|
144
src/components/ThemePicker/index.vue
Normal file
144
src/components/ThemePicker/index.vue
Normal 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>
|
103
src/components/Tinymce/components/editorImage.vue
Normal file
103
src/components/Tinymce/components/editorImage.vue
Normal 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>
|
204
src/components/Tinymce/index.vue
Normal file
204
src/components/Tinymce/index.vue
Normal 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 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>
|
7
src/components/Tinymce/plugins.js
Normal file
7
src/components/Tinymce/plugins.js
Normal 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
|
6
src/components/Tinymce/toolbar.js
Normal file
6
src/components/Tinymce/toolbar.js
Normal 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
|
29
src/components/TreeTable/eval.js
Normal file
29
src/components/TreeTable/eval.js
Normal 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
|
||||
}
|
127
src/components/TreeTable/index.vue
Normal file
127
src/components/TreeTable/index.vue
Normal 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>
|
89
src/components/TreeTable/readme.md
Normal file
89
src/components/TreeTable/readme.md
Normal 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
|
132
src/components/Upload/singleImage.vue
Normal file
132
src/components/Upload/singleImage.vue
Normal 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>
|
127
src/components/Upload/singleImage2.vue
Normal file
127
src/components/Upload/singleImage2.vue
Normal 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>
|
154
src/components/Upload/singleImage3.vue
Normal file
154
src/components/Upload/singleImage3.vue
Normal 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>
|
145
src/components/UploadExcel/index.vue
Normal file
145
src/components/UploadExcel/index.vue
Normal 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>
|
49
src/directive/clipboard/clipboard.js
Normal file
49
src/directive/clipboard/clipboard.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
13
src/directive/clipboard/index.js
Normal file
13
src/directive/clipboard/index.js
Normal 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
|
77
src/directive/el-dragDialog/drag.js
Normal file
77
src/directive/el-dragDialog/drag.js
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
13
src/directive/el-dragDialog/index.js
Normal file
13
src/directive/el-dragDialog/index.js
Normal 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
|
13
src/directive/permission/index.js
Normal file
13
src/directive/permission/index.js
Normal 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
|
23
src/directive/permission/permission.js
Normal file
23
src/directive/permission/permission.js
Normal 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
Loading…
Reference in a new issue