├── .eslintignore
├── favicon.ico
├── gifs
├── tabs.gif
├── 2login.gif
├── editor.gif
├── excel.png
├── login.png
├── order.gif
├── table.gif
├── theme.gif
├── echarts.gif
├── errorlog.gif
├── leftmenu.gif
├── upload1.gif
├── dynamictable.gif
└── uploadAvatar.gif
├── src
├── views
│ ├── example
│ │ ├── table
│ │ │ ├── index.vue
│ │ │ ├── dynamictable
│ │ │ │ ├── index.vue
│ │ │ │ ├── unfixedThead.vue
│ │ │ │ └── fixedThead.vue
│ │ │ └── inlineEditTable.vue
│ │ └── tab
│ │ │ ├── index.vue
│ │ │ └── components
│ │ │ └── tabPane.vue
│ ├── errlog
│ │ ├── errcode.vue
│ │ └── index.vue
│ ├── svg-icons
│ │ ├── generateIconsView.js
│ │ └── index.vue
│ ├── layout
│ │ ├── index.js
│ │ ├── AppMain.vue
│ │ ├── Sidebar.vue
│ │ ├── Levelbar.vue
│ │ ├── Layout.vue
│ │ ├── SidebarItem.vue
│ │ ├── TabsView.vue
│ │ └── Navbar.vue
│ ├── login
│ │ ├── authredirect.vue
│ │ └── socialsignin.vue
│ ├── components
│ │ ├── index.vue
│ │ ├── tinymce.vue
│ │ ├── dropzone.vue
│ │ ├── dndList.vue
│ │ ├── markdown.vue
│ │ ├── mixin.vue
│ │ ├── jsonEditor.vue
│ │ ├── avatarUpload.vue
│ │ ├── splitpane.vue
│ │ └── sticky.vue
│ ├── charts
│ │ ├── index.vue
│ │ ├── line.vue
│ │ ├── keyboard.vue
│ │ ├── keyboard2.vue
│ │ └── mixChart.vue
│ ├── dashboard
│ │ ├── index.vue
│ │ ├── editor
│ │ │ └── index.vue
│ │ └── admin
│ │ │ ├── pieChart.vue
│ │ │ ├── barChart.vue
│ │ │ └── lineChart.vue
│ ├── permission
│ │ └── index.vue
│ ├── excel
│ │ ├── uploadExcel.vue
│ │ ├── index.vue
│ │ └── selectExcel.vue
│ ├── qiniu
│ │ └── upload.vue
│ ├── introduction
│ │ └── index.vue
│ ├── errorPage
│ │ └── 401.vue
│ └── theme
│ │ └── index.vue
├── router
│ ├── _import_production.js
│ └── _import_development.js
├── assets
│ ├── 401_images
│ │ └── 401.gif
│ ├── 404_images
│ │ ├── 404.png
│ │ └── 404_cloud.png
│ └── custom-theme
│ │ └── fonts
│ │ ├── element-icons.ttf
│ │ └── element-icons.woff
├── api
│ ├── qiniu.js
│ ├── remoteSearch.js
│ ├── article.js
│ └── login.js
├── store
│ ├── errLog.js
│ ├── index.js
│ ├── getters.js
│ └── modules
│ │ ├── app.js
│ │ ├── permission.js
│ │ └── user.js
├── utils
│ ├── createUniqueString.js
│ ├── auth.js
│ ├── validate.js
│ ├── openWindow.js
│ └── fetch.js
├── App.vue
├── errorLog.js
├── components
│ ├── Icon-svg
│ │ └── index.vue
│ ├── SplitPane
│ │ ├── Pane.vue
│ │ ├── Resizer.vue
│ │ └── index.vue
│ ├── ImageCropper
│ │ ├── lang.js
│ │ └── utils.js
│ ├── Github
│ │ └── index.vue
│ ├── jsonEditor
│ │ └── index.vue
│ ├── TodoList
│ │ ├── Todo.vue
│ │ └── index.vue
│ ├── Sticky
│ │ └── index.vue
│ ├── Hamburger
│ │ └── index.vue
│ ├── UploadExcel
│ │ └── index.vue
│ ├── ErrLog
│ │ └── index.vue
│ ├── MarkdownEditor
│ │ └── index.vue
│ ├── Screenfull
│ │ └── index.vue
│ ├── Charts
│ │ ├── keyboard.vue
│ │ └── keyboard2.vue
│ ├── Upload
│ │ ├── singleImage2.vue
│ │ ├── singleImage.vue
│ │ └── singleImage3.vue
│ ├── BackToTop
│ │ └── index.vue
│ ├── PanThumb
│ │ └── index.vue
│ └── Tinymce
│ │ └── components
│ │ └── editorImage.vue
├── icons
│ ├── svg
│ │ ├── zujian.svg
│ │ ├── tubiao.svg
│ │ ├── tab.svg
│ │ ├── from.svg
│ │ ├── shouce.svg
│ │ ├── c.svg
│ │ ├── a.svg
│ │ ├── b.svg
│ │ ├── email.svg
│ │ ├── yonghuming.svg
│ │ ├── EXCEL.svg
│ │ ├── tuozhuai.svg
│ │ ├── icons.svg
│ │ ├── theme.svg
│ │ ├── zonghe.svg
│ │ ├── quanxian.svg
│ │ ├── mima.svg
│ │ ├── wujiaoxing.svg
│ │ ├── 404.svg
│ │ ├── bug.svg
│ │ ├── xinrenzhinan.svg
│ │ ├── table.svg
│ │ ├── yanjing.svg
│ │ └── weixin.svg
│ └── index.js
├── mock
│ ├── remoteSearch.js
│ ├── index.js
│ ├── login.js
│ └── article.js
├── main.js
├── directive
│ ├── waves.css
│ ├── waves.js
│ └── sticky.js
├── styles
│ ├── element-ui.scss
│ ├── mixin.scss
│ ├── sidebar.scss
│ └── btn.scss
├── permission.js
└── filters
│ └── index.js
├── static
└── tinymce
│ └── skins
│ └── lightgray
│ ├── img
│ ├── anchor.gif
│ ├── loader.gif
│ ├── object.gif
│ └── trans.gif
│ ├── fonts
│ ├── tinymce.eot
│ ├── tinymce.ttf
│ ├── tinymce.woff
│ ├── tinymce-small.eot
│ ├── tinymce-small.ttf
│ └── tinymce-small.woff
│ └── content.inline.min.css
├── .babelrc
├── config
├── dev.env.js
├── prod.env.js
├── sit.env.js
└── index.js
├── .gitignore
├── .postcssrc.js
├── .editorconfig
├── index.html
├── LICENSE
└── package.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | build/*.js
2 | config/*.js
3 | src/assets
4 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/favicon.ico
--------------------------------------------------------------------------------
/gifs/tabs.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/tabs.gif
--------------------------------------------------------------------------------
/gifs/2login.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/2login.gif
--------------------------------------------------------------------------------
/gifs/editor.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/editor.gif
--------------------------------------------------------------------------------
/gifs/excel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/excel.png
--------------------------------------------------------------------------------
/gifs/login.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/login.png
--------------------------------------------------------------------------------
/gifs/order.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/order.gif
--------------------------------------------------------------------------------
/gifs/table.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/table.gif
--------------------------------------------------------------------------------
/gifs/theme.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/theme.gif
--------------------------------------------------------------------------------
/src/views/example/table/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/gifs/echarts.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/echarts.gif
--------------------------------------------------------------------------------
/gifs/errorlog.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/errorlog.gif
--------------------------------------------------------------------------------
/gifs/leftmenu.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/leftmenu.gif
--------------------------------------------------------------------------------
/gifs/upload1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/upload1.gif
--------------------------------------------------------------------------------
/src/router/_import_production.js:
--------------------------------------------------------------------------------
1 | module.exports = file => () => import('@/views/' + file + '.vue')
2 |
--------------------------------------------------------------------------------
/gifs/dynamictable.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/dynamictable.gif
--------------------------------------------------------------------------------
/gifs/uploadAvatar.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/gifs/uploadAvatar.gif
--------------------------------------------------------------------------------
/src/assets/401_images/401.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/src/assets/401_images/401.gif
--------------------------------------------------------------------------------
/src/assets/404_images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/src/assets/404_images/404.png
--------------------------------------------------------------------------------
/src/assets/404_images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/src/assets/404_images/404_cloud.png
--------------------------------------------------------------------------------
/src/router/_import_development.js:
--------------------------------------------------------------------------------
1 | module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
2 |
--------------------------------------------------------------------------------
/src/views/errlog/errcode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{a.a}}
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/img/anchor.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/img/anchor.gif
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/img/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/img/loader.gif
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/img/object.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/img/object.gif
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/img/trans.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/img/trans.gif
--------------------------------------------------------------------------------
/src/assets/custom-theme/fonts/element-icons.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/src/assets/custom-theme/fonts/element-icons.ttf
--------------------------------------------------------------------------------
/src/assets/custom-theme/fonts/element-icons.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/src/assets/custom-theme/fonts/element-icons.woff
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/fonts/tinymce.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/fonts/tinymce.eot
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/fonts/tinymce.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/fonts/tinymce.ttf
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/fonts/tinymce.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/fonts/tinymce.woff
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | ["env", { "modules": false }],
4 | "stage-2"
5 | ],
6 | "plugins": ["transform-runtime"],
7 | "comments": false
8 | }
9 |
--------------------------------------------------------------------------------
/config/dev.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"development"',
3 | BASE_API: '"https://api-dev"',
4 | APP_ORIGIN: '"https://wallstreetcn.com"'
5 | }
6 |
--------------------------------------------------------------------------------
/config/prod.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"',
3 | BASE_API: '"https://api-prod"',
4 | APP_ORIGIN: '"https://wallstreetcn.com"'
5 | };
6 |
--------------------------------------------------------------------------------
/config/sit.env.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | NODE_ENV: '"production"',
3 | BASE_API: '"https://api-sit"',
4 | APP_ORIGIN: '"https://wallstreetcn.com"'
5 | };
6 |
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/fonts/tinymce-small.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/fonts/tinymce-small.eot
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/fonts/tinymce-small.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/fonts/tinymce-small.ttf
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/fonts/tinymce-small.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/calamus0427/vue-element-admin/master/static/tinymce/skins/lightgray/fonts/tinymce-small.woff
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | static/ckeditor
5 | gifs/
6 | npm-debug.log
7 | test/unit/coverage
8 | test/e2e/reports
9 | selenium-debug.log
10 | .idea
11 |
--------------------------------------------------------------------------------
/src/api/qiniu.js:
--------------------------------------------------------------------------------
1 | import fetch from '@/utils/fetch'
2 |
3 | export function getToken() {
4 | return fetch({
5 | url: '/qiniu/upload/token', // 假地址 自行替换
6 | method: 'get'
7 | })
8 | }
9 |
--------------------------------------------------------------------------------
/src/api/remoteSearch.js:
--------------------------------------------------------------------------------
1 | import fetch from '@/utils/fetch'
2 |
3 | export function userSearch(name) {
4 | return fetch({
5 | url: '/search/user',
6 | method: 'get',
7 | params: { name }
8 | })
9 | }
10 |
--------------------------------------------------------------------------------
/src/views/svg-icons/generateIconsView.js:
--------------------------------------------------------------------------------
1 | const data = {
2 | state: {
3 | iconsMap: []
4 | },
5 | generate(iconsMap) {
6 | this.state.iconsMap = iconsMap
7 | }
8 | }
9 |
10 | export default data
11 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | "plugins": {
5 | // to edit target browsers: use "browserlist" field in package.json
6 | "autoprefixer": {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/views/layout/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navbar } from './Navbar'
2 |
3 | export { default as Sidebar } from './Sidebar'
4 |
5 | export { default as Levelbar } from './Levelbar'
6 |
7 | export { default as AppMain } from './AppMain'
8 |
--------------------------------------------------------------------------------
/src/store/errLog.js:
--------------------------------------------------------------------------------
1 | const errLog = {
2 | state: {
3 | errLog: []
4 | },
5 | pushLog(log) {
6 | this.state.errLog.unshift(log)
7 | },
8 | clearLog() {
9 | this.state.errLog = []
10 | }
11 | }
12 |
13 | export default errLog
14 |
--------------------------------------------------------------------------------
/src/utils/createUniqueString.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jiachenpan on 17/3/8.
3 | */
4 | export default function createUniqueString() {
5 | const timestamp = +new Date() + ''
6 | const randomNum = parseInt((1 + Math.random()) * 65536) + ''
7 | return (+(randomNum + timestamp)).toString(32)
8 | }
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | insert_final_newline = false
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/src/views/login/authredirect.vue:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
17 |
--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const TokenKey = 'Admin-Token'
4 |
5 | export function getToken() {
6 | return Cookies.get(TokenKey)
7 | }
8 |
9 | export function setToken(token) {
10 | return Cookies.set(TokenKey, token)
11 | }
12 |
13 | export function removeToken() {
14 | return Cookies.remove(TokenKey)
15 | }
16 |
--------------------------------------------------------------------------------
/src/errorLog.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import errLog from '@/store/errLog'
3 |
4 | // 生产环境错误日志
5 | if (process.env.NODE_ENV === 'production') {
6 | Vue.config.errorHandler = function(err, vm) {
7 | console.log(err, window.location.href)
8 | errLog.pushLog({
9 | err,
10 | url: window.location.href,
11 | vm
12 | })
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/views/components/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
这里暂时列出了自己在项目中用到的组件和一些自己封装的组件,如有补充可以提 issue
4 | 我个人崇尚自己封装组件,因为很多组件会和业务后高度的耦合,而且第三方封装的组件灵活性可控性都不高,如有需要可以看楼主之前写过的一篇文章
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import Vuex from 'vuex'
3 | import app from './modules/app'
4 | import user from './modules/user'
5 | import permission from './modules/permission'
6 | import getters from './getters'
7 |
8 | Vue.use(Vuex)
9 |
10 | const store = new Vuex.Store({
11 | modules: {
12 | app,
13 | user,
14 | permission
15 | },
16 | getters
17 | })
18 |
19 | export default store
20 |
--------------------------------------------------------------------------------
/src/views/charts/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 这里的所有的图表都基于ECharts,实例代码来源 gallery
其实ECharts封装的很好了,用vue封装是很简单的事情,建议大家自己来封装。相关教程
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/components/Icon-svg/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
23 |
--------------------------------------------------------------------------------
/src/views/layout/AppMain.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
19 |
--------------------------------------------------------------------------------
/src/api/article.js:
--------------------------------------------------------------------------------
1 | import fetch from '@/utils/fetch'
2 |
3 | export function fetchList(query) {
4 | return fetch({
5 | url: '/article/list',
6 | method: 'get',
7 | params: query
8 | })
9 | }
10 |
11 | export function fetchArticle() {
12 | return fetch({
13 | url: '/article/detail',
14 | method: 'get'
15 | })
16 | }
17 |
18 | export function fetchPv(pv) {
19 | return fetch({
20 | url: '/article/pv',
21 | method: 'get',
22 | params: { pv }
23 | })
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/src/icons/svg/zujian.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/example/table/dynamictable/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
固定表头 按照表头顺序排序
4 |
5 |
6 |
不固定表头 按照点击顺序排序
7 |
8 |
9 |
10 |
11 |
19 |
20 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Juicy
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/views/charts/line.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
16 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/src/icons/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import IconSvg from '@/components/Icon-svg'// svg组件
3 | import generateIconsView from '@/views/svg-icons/generateIconsView.js'// just for views/icons , you can delete it
4 | // register globally
5 |
6 | Vue.component('icon-svg', IconSvg)
7 |
8 | const requireAll = requireContext => requireContext.keys().map(requireContext)
9 | const req = require.context('./svg', false, /\.svg$/)
10 | const iconMap = requireAll(req)
11 |
12 | generateIconsView.generate(iconMap) // just for views/icons , you can delete it
13 |
--------------------------------------------------------------------------------
/src/icons/svg/tubiao.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/charts/keyboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
16 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/src/views/charts/keyboard2.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
16 |
17 |
24 |
25 |
--------------------------------------------------------------------------------
/src/views/charts/mixChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
16 |
17 |
25 |
26 |
--------------------------------------------------------------------------------
/src/icons/svg/tab.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/login.js:
--------------------------------------------------------------------------------
1 | import fetch from '@/utils/fetch'
2 |
3 | export function loginByUsername(username, password) {
4 | const data = {
5 | username,
6 | password
7 | }
8 | return fetch({
9 | url: '/login/login',
10 | method: 'post',
11 | data
12 | })
13 | }
14 |
15 | export function logout() {
16 | return fetch({
17 | url: '/login/logout',
18 | method: 'post'
19 | })
20 | }
21 |
22 | export function getUserInfo(token) {
23 | return fetch({
24 | url: '/user/info',
25 | method: 'get',
26 | params: { token }
27 | })
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | const getters = {
2 | sidebar: state => state.app.sidebar,
3 | visitedViews: state => state.app.visitedViews,
4 | token: state => state.user.token,
5 | avatar: state => state.user.avatar,
6 | name: state => state.user.name,
7 | introduction: state => state.user.introduction,
8 | status: state => state.user.status,
9 | roles: state => state.user.roles,
10 | setting: state => state.user.setting,
11 | permission_routers: state => state.permission.routers,
12 | addRouters: state => state.permission.addRouters
13 | }
14 | export default getters
15 |
--------------------------------------------------------------------------------
/src/icons/svg/from.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/shouce.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/errlog/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
请点击右上角bug小图表
6 |
7 | 现在的管理后台基本都是spa的形式了,它增强了用户体验,但同时也会增加页面出问题的可能性,可能一个小小的疏忽就导致整个页面的死锁。好在Vue官网提供了一个方法来捕获处理异常
8 |
9 |

10 |
11 |
12 |
13 |
20 |
21 |
26 |
--------------------------------------------------------------------------------
/src/views/layout/Sidebar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
24 |
--------------------------------------------------------------------------------
/src/icons/svg/c.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/a.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/b.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/email.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/mock/remoteSearch.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import { param2Obj } from '@/utils'
3 |
4 | const NameList = []
5 | const count = 100
6 |
7 | for (let i = 0; i < count; i++) {
8 | NameList.push(Mock.mock({
9 | name: '@first'
10 | }))
11 | }
12 | NameList.push({ name: 'mockPan' })
13 |
14 | export default {
15 | searchUser: config => {
16 | const { name } = param2Obj(config.url)
17 | const mockNameList = NameList.filter(item => {
18 | const lowerCaseName = item.name.toLowerCase()
19 | if (name && lowerCaseName.indexOf(name.toLowerCase()) < 0) return false
20 | return true
21 | })
22 | return { items: mockNameList }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/mock/index.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import loginAPI from './login'
3 | import articleAPI from './article'
4 | import remoteSearchAPI from './remoteSearch'
5 |
6 | Mock.setup({
7 | timeout: '350-600'
8 | })
9 |
10 | // 登录相关
11 | Mock.mock(/\/login\/login/, 'post', loginAPI.loginByUsername)
12 | Mock.mock(/\/login\/logout/, 'post', loginAPI.logout)
13 | Mock.mock(/\/user\/info\.*/, 'get', loginAPI.getUserInfo)
14 |
15 | // 文章相关
16 | Mock.mock(/\/article\/list/, 'get', articleAPI.getList)
17 | Mock.mock(/\/article\/detail/, 'get', articleAPI.getArticle)
18 | Mock.mock(/\/article\/pv/, 'get', articleAPI.getPv)
19 |
20 | // 搜索相关
21 | Mock.mock(/\/search\/user/, 'get', remoteSearchAPI.searchUser)
22 |
23 | export default Mock
24 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import ElementUI from 'element-ui'
3 | import 'element-ui/lib/theme-default/index.css'
4 | import App from './App'
5 | import router from './router'
6 | import store from './store'
7 | import * as filters from './filters' // 全局filter
8 | import './icons' // icon
9 | import './errorLog'// error log
10 | import './permission' // 权限
11 | import './mock' // 该项目所有请求使用mockjs模拟
12 |
13 | Vue.use(ElementUI)
14 |
15 | // register global utility filters.
16 | Object.keys(filters).forEach(key => {
17 | Vue.filter(key, filters[key])
18 | })
19 |
20 | Vue.config.productionTip = false
21 |
22 | new Vue({
23 | el: '#app',
24 | router,
25 | store,
26 | template: '',
27 | components: { App }
28 | })
29 |
--------------------------------------------------------------------------------
/src/views/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
33 |
--------------------------------------------------------------------------------
/src/views/permission/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
你的权限: {{roles}}
4 | 切换权限:
5 |
6 |
7 |
8 |
9 |
10 |
11 |
34 |
--------------------------------------------------------------------------------
/src/icons/svg/yonghuming.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/excel/uploadExcel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
30 |
--------------------------------------------------------------------------------
/src/views/components/tinymce.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
公司做的后台主要是一个cms系统,公司也是以自媒体为核心的,所以富文本是后台很核心的功能。在选择富文本的过程中也走了不少的弯路,市面上常见的富文本都基本用过了,最终选择了Tinymce 相关文章 官网
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
23 |
24 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/src/icons/svg/EXCEL.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/SplitPane/Pane.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
45 |
--------------------------------------------------------------------------------
/src/views/components/dropzone.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 基于 dropzone 封装 ,
5 | 由于我司业务有特殊需求,而且要传七牛 所以没用第三方 选择了自己封装
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/src/directive/waves.css:
--------------------------------------------------------------------------------
1 | .waves-ripple {
2 | position: absolute;
3 | border-radius: 100%;
4 | background-color: rgba(0, 0, 0, 0.15);
5 | background-clip: padding-box;
6 | pointer-events: none;
7 | -webkit-user-select: none;
8 | -moz-user-select: none;
9 | -ms-user-select: none;
10 | user-select: none;
11 | -webkit-transform: scale(0);
12 | -ms-transform: scale(0);
13 | transform: scale(0);
14 | opacity: 1;
15 | }
16 |
17 | .waves-ripple.z-active {
18 | opacity: 0;
19 | -webkit-transform: scale(2);
20 | -ms-transform: scale(2);
21 | transform: scale(2);
22 | -webkit-transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
23 | transition: opacity 1.2s ease-out, -webkit-transform 0.6s ease-out;
24 | transition: opacity 1.2s ease-out, transform 0.6s ease-out;
25 | transition: opacity 1.2s ease-out, transform 0.6s ease-out, -webkit-transform 0.6s ease-out;
26 | }
--------------------------------------------------------------------------------
/src/icons/svg/tuozhuai.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/components/dndList.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
10 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/icons/svg/icons.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/theme.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/zonghe.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/utils/validate.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by jiachenpan on 16/11/18.
3 | */
4 |
5 | export function isvalidUsername(str) {
6 | const valid_map = ['admin', 'editor']
7 | return valid_map.indexOf(str.trim()) >= 0
8 | }
9 |
10 | /* 合法uri*/
11 | export function validateURL(textval) {
12 | const urlregex = /^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
13 | return urlregex.test(textval)
14 | }
15 |
16 | /* 小写字母*/
17 | export function validateLowerCase(str) {
18 | const reg = /^[a-z]+$/
19 | return reg.test(str)
20 | }
21 |
22 | /* 大写字母*/
23 | export function validateUpperCase(str) {
24 | const reg = /^[A-Z]+$/
25 | return reg.test(str)
26 | }
27 |
28 | /* 大小写字母*/
29 | export function validatAlphabets(str) {
30 | const reg = /^[A-Za-z]+$/
31 | return reg.test(str)
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/src/icons/svg/quanxian.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/ImageCropper/lang.js:
--------------------------------------------------------------------------------
1 | const langBag = {
2 | zh: {
3 | hint: '点击,或拖动图片至此处',
4 | loading: '正在上传……',
5 | noSupported: '浏览器不支持该功能,请使用IE10以上或其他现在浏览器!',
6 | success: '上传成功',
7 | fail: '图片上传失败',
8 | preview: '头像预览',
9 | btn: {
10 | off: '取消',
11 | close: '关闭',
12 | back: '上一步',
13 | save: '保存'
14 | },
15 | error: {
16 | onlyImg: '仅限图片格式',
17 | outOfSize: '单文件大小不能超过 ',
18 | lowestPx: '图片最低像素为(宽*高):'
19 | }
20 | },
21 | en: {
22 | hint: 'Click, or drag the file here',
23 | loading: 'Uploading……',
24 | noSupported: 'Browser does not support, please use IE10+ or other browsers',
25 | success: 'Upload success',
26 | fail: 'Upload failed',
27 | preview: 'Preview',
28 | btn: {
29 | off: 'Cancel',
30 | close: 'Close',
31 | back: 'Back',
32 | save: 'Save'
33 | },
34 | error: {
35 | onlyImg: 'Image only',
36 | outOfSize: 'Image exceeds size limit: ',
37 | lowestPx: 'The lowest pixel in the image: '
38 | }
39 | }
40 | }
41 | export default langBag
42 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 PanJiaChen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/mock/login.js:
--------------------------------------------------------------------------------
1 | import { param2Obj } from '@/utils'
2 |
3 | const userMap = {
4 | admin: {
5 | role: ['admin'],
6 | token: 'admin',
7 | introduction: '我是超级管理员',
8 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
9 | name: 'Super Admin'
10 | },
11 | editor: {
12 | role: ['editor'],
13 | token: 'editor',
14 | introduction: '我是编辑',
15 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
16 | name: 'Normal Editor'
17 | },
18 | developer: {
19 | role: ['develop'],
20 | token: 'develop',
21 | introduction: '我是开发',
22 | avatar: 'https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif',
23 | name: '工程师小王'
24 | }
25 | }
26 |
27 | export default {
28 | loginByUsername: config => {
29 | const { username } = JSON.parse(config.body)
30 | return userMap[username]
31 | },
32 | getUserInfo: config => {
33 | const { token } = param2Obj(config.url)
34 | if (userMap[token]) {
35 | return userMap[token]
36 | } else {
37 | return Promise.reject('error')
38 | }
39 | },
40 | logout: () => 'success'
41 | }
42 |
--------------------------------------------------------------------------------
/src/views/qiniu/upload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 将文件拖到此处,或点击上传
5 |
6 |
7 |
8 |
9 |
41 |
--------------------------------------------------------------------------------
/src/icons/svg/mima.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/wujiaoxing.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/components/markdown.vue:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/views/example/tab/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | mounted times :{{createdTimes}}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
39 |
40 |
45 |
--------------------------------------------------------------------------------
/src/views/components/mixin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 标题
6 |
7 | Material Design 的input
8 |
9 |
10 |
11 |
12 | 上海花裤衩
13 |
14 |
图片hover效果
15 |
16 |
17 |
18 | 水波纹效果
19 | 水波纹 v-directive
20 |
21 |
22 |
23 |
24 |
41 |
42 |
50 |
--------------------------------------------------------------------------------
/src/utils/openWindow.js:
--------------------------------------------------------------------------------
1 | /**
2 | *Created by jiachenpan on 16/11/29.
3 | * @param {Sting} url
4 | * @param {Sting} title
5 | * @param {Number} w
6 | * @param {Number} h
7 | */
8 |
9 | export default function openWindow(url, title, w, h) {
10 | // Fixes dual-screen position Most browsers Firefox
11 | const dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left
12 | const dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top
13 |
14 | const width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width
15 | const height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height
16 |
17 | const left = ((width / 2) - (w / 2)) + dualScreenLeft
18 | const top = ((height / 2) - (h / 2)) + dualScreenTop
19 | const newWindow = window.open(url, title, 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=yes, copyhistory=no, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left)
20 |
21 | // Puts focus on the newWindow
22 | if (window.focus) {
23 | newWindow.focus()
24 | }
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/components/Github/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/views/example/table/dynamictable/unfixedThead.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | apple
7 | banana
8 | orange
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{scope.row[fruit]}}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
48 |
--------------------------------------------------------------------------------
/src/views/layout/Levelbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{item.name}}
5 | {{item.name}}
6 |
7 |
8 |
9 |
10 |
37 |
38 |
50 |
--------------------------------------------------------------------------------
/src/views/introduction/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 这半年来一直在用vue写管理后台,目前后台已经有百来个个页面,十几种权限,但维护成本依然很低,所以准备开源分享一下后台开发的经验和成果。目前的技术栈主要的采用vue+element+axios由webpack2打包.由于是个人项目,所以数据请求都是用了mockjs模拟。注意:在次项目基础上改造开发时请移除mock文件。
6 | 写了一个系列的教程配套文章,如何从零构建后一个完整的后台项目:
7 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
--------------------------------------------------------------------------------
/src/views/svg-icons/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{`<icon-svg :icon-class="${item}" />`}}
8 |
9 |
10 |
11 |
{{item}}
12 |
13 |
14 |
15 |
16 |
17 |
34 |
35 |
59 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const app = {
4 | state: {
5 | sidebar: {
6 | opened: !+Cookies.get('sidebarStatus')
7 | },
8 | visitedViews: []
9 | },
10 | mutations: {
11 | TOGGLE_SIDEBAR: state => {
12 | if (state.sidebar.opened) {
13 | Cookies.set('sidebarStatus', 1)
14 | } else {
15 | Cookies.set('sidebarStatus', 0)
16 | }
17 | state.sidebar.opened = !state.sidebar.opened
18 | },
19 | ADD_VISITED_VIEWS: (state, view) => {
20 | if (state.visitedViews.some(v => v.path === view.path)) return
21 | state.visitedViews.push({ name: view.name, path: view.path })
22 | },
23 | DEL_VISITED_VIEWS: (state, view) => {
24 | let index
25 | for (const [i, v] of state.visitedViews.entries()) {
26 | if (v.path === view.path) {
27 | index = i
28 | break
29 | }
30 | }
31 | state.visitedViews.splice(index, 1)
32 | }
33 | },
34 | actions: {
35 | ToggleSideBar({ commit }) {
36 | commit('TOGGLE_SIDEBAR')
37 | },
38 | addVisitedViews({ commit }, view) {
39 | commit('ADD_VISITED_VIEWS', view)
40 | },
41 | delVisitedViews({ commit, state }, view) {
42 | return new Promise((resolve) => {
43 | commit('DEL_VISITED_VIEWS', view)
44 | resolve([...state.visitedViews])
45 | })
46 | }
47 | }
48 | }
49 |
50 | export default app
51 |
--------------------------------------------------------------------------------
/src/views/layout/Layout.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
28 |
29 |
64 |
--------------------------------------------------------------------------------
/src/views/components/jsonEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
jsonEditor is base on CodeMirrorr , lint base on json-lint
4 |
5 |
6 |
7 |
8 |
9 |
10 |
23 |
24 |
30 |
31 |
--------------------------------------------------------------------------------
/src/views/layout/SidebarItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
31 |
32 |
33 |
43 |
44 |
--------------------------------------------------------------------------------
/src/icons/svg/404.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | //覆盖一些element-ui样式
2 | .block-checkbox {
3 | display: block;
4 | }
5 |
6 | .operation-container {
7 | .cell {
8 | padding: 10px !important;
9 | }
10 | .el-button {
11 | &:nth-child(3) {
12 | margin-top: 10px;
13 | margin-left: 0px;
14 | }
15 | &:nth-child(4) {
16 | margin-top: 10px;
17 | }
18 | }
19 | }
20 |
21 | .el-upload {
22 | input[type="file"] {
23 | display: none !important;
24 | }
25 | }
26 |
27 | .el-upload__input {
28 | display: none;
29 | }
30 |
31 | .cell {
32 | .el-tag {
33 | margin-right: 8px;
34 | }
35 | }
36 |
37 | .small-padding {
38 | .cell {
39 | padding-left: 8px;
40 | padding-right: 8px;
41 | }
42 | }
43 |
44 | .status-col {
45 | .cell {
46 | padding: 0 10px;
47 | text-align: center;
48 | .el-tag {
49 | margin-right: 0px;
50 | }
51 | }
52 | }
53 |
54 | //暂时性解决diolag 问题 https://github.com/ElemeFE/element/issues/2461
55 | .el-dialog {
56 | transform: none;
57 | left: 0;
58 | position: relative;
59 | margin: 0 auto;
60 | }
61 |
62 | //文章页textarea修改样式
63 | .article-textarea {
64 | textarea {
65 | padding-right: 40px;
66 | resize: none;
67 | border: none;
68 | border-radius: 0px;
69 | border-bottom: 1px solid #bfcbd9;
70 | }
71 | }
72 |
73 | //element ui upload
74 | .upload-container {
75 | .el-upload {
76 | width: 100%;
77 | .el-upload-dragger {
78 | width: 100%;
79 | height: 200px;
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/styles/mixin.scss:
--------------------------------------------------------------------------------
1 | @mixin clearfix {
2 | &:after {
3 | content: "";
4 | display: table;
5 | clear: both;
6 | }
7 | }
8 |
9 | @mixin scrollBar {
10 | &::-webkit-scrollbar-track-piece {
11 | background: #d3dce6;
12 | }
13 | &::-webkit-scrollbar {
14 | width: 6px;
15 | }
16 | &::-webkit-scrollbar-thumb {
17 | background: #99a9bf;
18 | border-radius: 20px;
19 | }
20 | }
21 |
22 | @mixin relative {
23 | position: relative;
24 | width: 100%;
25 | height: 100%;
26 | }
27 |
28 | @mixin pct($pct) {
29 | width: #{$pct};
30 | position: relative;
31 | margin: 0 auto;
32 | }
33 |
34 | @mixin triangle($width, $height, $color, $direction) {
35 | $width: $width/2;
36 | $color-border-style: $height solid $color;
37 | $transparent-border-style: $width solid transparent;
38 | height: 0;
39 | width: 0;
40 | @if $direction==up {
41 | border-bottom: $color-border-style;
42 | border-left: $transparent-border-style;
43 | border-right: $transparent-border-style;
44 | }
45 | @else if $direction==right {
46 | border-left: $color-border-style;
47 | border-top: $transparent-border-style;
48 | border-bottom: $transparent-border-style;
49 | }
50 | @else if $direction==down {
51 | border-top: $color-border-style;
52 | border-left: $transparent-border-style;
53 | border-right: $transparent-border-style;
54 | }
55 | @else if $direction==left {
56 | border-right: $color-border-style;
57 | border-top: $transparent-border-style;
58 | border-bottom: $transparent-border-style;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/views/components/avatarUpload.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
这里核心代码用的是 vue-image-crop-upload
4 | 由于我在使用时它只有vue@1版本,而且有些业务的需求耦合到七牛等等原因吧,自己改造了一下,如果大家要使用的话,优先还是使用官方component
5 |
6 |
7 |
8 |
9 |
修改头像
10 |
11 |
12 |
13 |
14 |
15 |
16 |
41 |
42 |
49 |
50 |
--------------------------------------------------------------------------------
/src/views/components/splitpane.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
splitPane 如果你用过 codepen, jsfiddle 就不会陌生了
4 | 项目地址
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
37 |
38 |
66 |
--------------------------------------------------------------------------------
/src/components/jsonEditor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
55 |
56 |
65 |
--------------------------------------------------------------------------------
/src/directive/waves.js:
--------------------------------------------------------------------------------
1 | import './waves.css'
2 |
3 | export default{
4 | bind(el, binding) {
5 | el.addEventListener('click', e => {
6 | const customOpts = Object.assign({}, binding.value)
7 | const opts = Object.assign({
8 | ele: el, // 波纹作用元素
9 | type: 'hit', // hit点击位置扩散center中心点扩展
10 | color: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
11 | }, customOpts)
12 | const target = opts.ele
13 | if (target) {
14 | target.style.position = 'relative'
15 | target.style.overflow = 'hidden'
16 | const rect = target.getBoundingClientRect()
17 | let ripple = target.querySelector('.waves-ripple')
18 | if (!ripple) {
19 | ripple = document.createElement('span')
20 | ripple.className = 'waves-ripple'
21 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px'
22 | target.appendChild(ripple)
23 | } else {
24 | ripple.className = 'waves-ripple'
25 | }
26 | switch (opts.type) {
27 | case 'center':
28 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px'
29 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px'
30 | break
31 | default:
32 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px'
33 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px'
34 | }
35 | ripple.style.backgroundColor = opts.color
36 | ripple.className = 'waves-ripple z-active'
37 | return false
38 | }
39 | }, false)
40 | }
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/src/store/modules/permission.js:
--------------------------------------------------------------------------------
1 | import { asyncRouterMap, constantRouterMap } from '@/router'
2 |
3 | /**
4 | * 通过meta.role判断是否与当前用户权限匹配
5 | * @param roles
6 | * @param route
7 | */
8 | function hasPermission(roles, route) {
9 | if (route.meta && route.meta.role) {
10 | return roles.some(role => route.meta.role.indexOf(role) >= 0)
11 | } else {
12 | return true
13 | }
14 | }
15 |
16 | /**
17 | * 递归过滤异步路由表,返回符合用户角色权限的路由表
18 | * @param asyncRouterMap
19 | * @param roles
20 | */
21 | function filterAsyncRouter(asyncRouterMap, roles) {
22 | const accessedRouters = asyncRouterMap.filter(route => {
23 | if (hasPermission(roles, route)) {
24 | if (route.children && route.children.length) {
25 | route.children = filterAsyncRouter(route.children, roles)
26 | }
27 | return true
28 | }
29 | return false
30 | })
31 | return accessedRouters
32 | }
33 |
34 | const permission = {
35 | state: {
36 | routers: constantRouterMap,
37 | addRouters: []
38 | },
39 | mutations: {
40 | SET_ROUTERS: (state, routers) => {
41 | state.addRouters = routers
42 | state.routers = constantRouterMap.concat(routers)
43 | }
44 | },
45 | actions: {
46 | GenerateRoutes({ commit }, data) {
47 | return new Promise(resolve => {
48 | const { roles } = data
49 | let accessedRouters
50 | if (roles.indexOf('admin') >= 0) {
51 | accessedRouters = asyncRouterMap
52 | } else {
53 | accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
54 | }
55 | commit('SET_ROUTERS', accessedRouters)
56 | resolve()
57 | })
58 | }
59 | }
60 | }
61 |
62 | export default permission
63 |
--------------------------------------------------------------------------------
/src/icons/svg/bug.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/xinrenzhinan.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/config/index.js:
--------------------------------------------------------------------------------
1 | // see http://vuejs-templates.github.io/webpack for documentation.
2 | var path = require('path')
3 |
4 | module.exports = {
5 | build: {
6 | sitEnv: require('./sit.env'),
7 | prodEnv: require('./prod.env'),
8 | index: path.resolve(__dirname, '../dist/index.html'),
9 | assetsRoot: path.resolve(__dirname, '../dist'),
10 | assetsSubDirectory: 'static',
11 | assetsPublicPath: './', //请根据自己路径配置更改
12 | productionSourceMap: false,
13 | // Gzip off by default as many popular static hosts such as
14 | // Surge or Netlify already gzip all static assets for you.
15 | // Before setting to `true`, make sure to:
16 | // npm install --save-dev compression-webpack-plugin
17 | productionGzip: false,
18 | productionGzipExtensions: ['js', 'css'],
19 | // Run the build command with an extra argument to
20 | // View the bundle analyzer report after build finishes:
21 | // `npm run build --report`
22 | // Set to `true` or `false` to always turn it on or off
23 | bundleAnalyzerReport: process.env.npm_config_report
24 | },
25 | dev: {
26 | env: require('./dev.env'),
27 | port: 9527,
28 | autoOpenBrowser: true,
29 | assetsSubDirectory: 'static',
30 | assetsPublicPath: '/',
31 | proxyTable: {},
32 | // CSS Sourcemaps off by default because relative paths are "buggy"
33 | // with this option, according to the CSS-Loader README
34 | // (https://github.com/webpack/css-loader#sourcemaps)
35 | // In our experience, they generally work as expected,
36 | // just be aware of this issue when enabling this option.
37 | cssSourceMap: false
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/components/SplitPane/Resizer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
27 |
28 |
73 |
--------------------------------------------------------------------------------
/src/icons/svg/table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/example/table/dynamictable/fixedThead.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | apple
7 | banana
8 | orange
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | {{scope.row[fruit]}}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
58 |
59 |
--------------------------------------------------------------------------------
/src/components/TodoList/Todo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
18 |
19 |
20 |
21 |
71 |
--------------------------------------------------------------------------------
/src/components/Sticky/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
11 |
75 |
--------------------------------------------------------------------------------
/src/icons/svg/yanjing.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/layout/TabsView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{tag.name}}
6 |
7 |
8 |
9 |
10 |
11 |
53 |
54 |
64 |
--------------------------------------------------------------------------------
/src/styles/sidebar.scss:
--------------------------------------------------------------------------------
1 | // 侧边栏
2 | .sidebar-container>.el-menu {
3 | width: 100%!important;
4 | min-height: 100%;
5 | }
6 |
7 | .sidebar-container .svg-icon {
8 | margin-right: 16px;
9 | }
10 |
11 | .hideSidebar .el-submenu>.el-submenu__title,
12 | .hideSidebar .submenu-title-noDropdown {
13 | padding-left: 10px!important;
14 | }
15 |
16 | .hideSidebar .submenu-title-noDropdown span,
17 | .hideSidebar .el-submenu>.el-submenu__title>span {
18 | height: 0;
19 | width: 0;
20 | overflow: hidden;
21 | visibility: hidden;
22 | display: inline-block;
23 | }
24 |
25 | .hideSidebar .nest-menu .el-submenu__title {
26 | text-align: initial!important;
27 | padding-left: 20px!important;
28 | span {
29 | height: auto;
30 | width: auto;
31 | visibility: visible;
32 | display: inline;
33 | }
34 | .el-submenu__icon-arrow {
35 | display: block!important;
36 | }
37 | }
38 |
39 | .hideSidebar .menu-wrapper>.el-menu-item,
40 | .hideSidebar .submenu-title-noDropdown,
41 | .hideSidebar .menu-wrapper>.el-submenu .el-submenu__title {
42 | text-align: center;
43 | }
44 |
45 | .hideSidebar .el-menu-item .el-submenu__icon-arrow,
46 | .hideSidebar .el-submenu .el-submenu__title .el-submenu__icon-arrow {
47 | display: none;
48 | }
49 |
50 | .hideSidebar .submenu-title-noDropdown {
51 | position: relative;
52 | span {
53 | transition: opacity .3s cubic-bezier(.55, 0, .1, 1);
54 | opacity: 0;
55 | }
56 | &:hover {
57 | span {
58 | display: block;
59 | border-radius: 3px;
60 | z-index: 1002;
61 | width: 140px;
62 | height: 56px;
63 | visibility: visible;
64 | position: absolute;
65 | right: -145px;
66 | text-align: left;
67 | text-indent: 20px;
68 | top: 0px;
69 | background-color: #1f2d3d;
70 | opacity: 1;
71 | }
72 | }
73 | }
74 |
75 | .el-submenu .el-menu-item {
76 | min-width: 180px!important;
77 | }
78 |
--------------------------------------------------------------------------------
/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
12 |
13 |
14 |
15 |
30 |
31 |
47 |
--------------------------------------------------------------------------------
/src/views/dashboard/editor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
你的权限:
5 | {{item}}
6 |
7 |
8 |
9 | {{name}}
10 | 普通编辑dashboard
11 |
12 |
13 |
14 |
![]()
15 |
16 |
17 |
18 |
19 |
41 |
42 |
75 |
--------------------------------------------------------------------------------
/src/permission.js:
--------------------------------------------------------------------------------
1 | import router from './router'
2 | import store from './store'
3 | import NProgress from 'nprogress' // Progress 进度条
4 | import 'nprogress/nprogress.css'// Progress 进度条样式
5 | import { getToken } from '@/utils/auth' // 验权
6 |
7 | // permissiom judge
8 | function hasPermission(roles, permissionRoles) {
9 | if (roles.indexOf('admin') >= 0) return true // admin权限 直接通过
10 | if (!permissionRoles) return true
11 | return roles.some(role => permissionRoles.indexOf(role) >= 0)
12 | }
13 |
14 | // register global progress.
15 | const whiteList = ['/login', '/authredirect']// 不重定向白名单
16 | router.beforeEach((to, from, next) => {
17 | NProgress.start() // 开启Progress
18 | if (getToken()) { // 判断是否有token
19 | if (to.path === '/login') {
20 | next({ path: '/' })
21 | } else {
22 | if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
23 | store.dispatch('GetUserInfo').then(res => { // 拉取user_info
24 | const roles = res.data.role
25 | store.dispatch('GenerateRoutes', { roles }).then(() => { // 生成可访问的路由表
26 | router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
27 | next({ ...to }) // hack方法 确保addRoutes已完成
28 | })
29 | }).catch(() => {
30 | store.dispatch('FedLogOut').then(() => {
31 | next({ path: '/login' })
32 | })
33 | })
34 | } else {
35 | // 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
36 | if (hasPermission(store.getters.roles, to.meta.role)) {
37 | next()//
38 | } else {
39 | next({ path: '/401', query: { noGoBack: true }})
40 | }
41 | // 可删 ↑
42 | }
43 | }
44 | } else {
45 | if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
46 | next()
47 | } else {
48 | next('/login') // 否则全部重定向到登录页
49 | NProgress.done() // 在hash模式下 改变手动改变hash 重定向回来 不会触发afterEach 暂时hack方案 ps:history模式下无问题,可删除该行!
50 | }
51 | }
52 | })
53 |
54 | router.afterEach(() => {
55 | NProgress.done() // 结束Progress
56 | })
57 |
--------------------------------------------------------------------------------
/src/styles/btn.scss:
--------------------------------------------------------------------------------
1 | $blue:#324157;
2 | $light-blue:#3A71A8;
3 | $red:#C03639;
4 | $pink: #E65D6E;
5 | $green: #30B08F;
6 | $tiffany: #4AB7BD;
7 | $yellow:#FEC171;
8 | $panGreen: #30B08F;
9 |
10 | @mixin colorBtn($color) {
11 | background: $color;
12 | &:hover {
13 | color: $color;
14 | &:before,
15 | &:after {
16 | background: $color;
17 | }
18 | }
19 | }
20 |
21 | .blue-btn {
22 | @include colorBtn($blue)
23 | }
24 |
25 | .light-blue-btn {
26 | @include colorBtn($light-blue)
27 | }
28 |
29 | .red-btn {
30 | @include colorBtn($red)
31 | }
32 |
33 | .pink-btn {
34 | @include colorBtn($pink)
35 | }
36 |
37 | .green-btn {
38 | @include colorBtn($green)
39 | }
40 |
41 | .tiffany-btn {
42 | @include colorBtn($tiffany)
43 | }
44 |
45 | .yellow-btn {
46 | @include colorBtn($yellow)
47 | }
48 |
49 | .pan-btn {
50 | font-size: 14px;
51 | color: #fff;
52 | padding: 14px 36px;
53 | border-radius: 8px;
54 | border: none;
55 | outline: none;
56 | margin-right: 25px;
57 | transition: 600ms ease all;
58 | position: relative;
59 | display: inline-block;
60 | &:hover {
61 | background: #fff;
62 | &:before,
63 | &:after {
64 | width: 100%;
65 | transition: 600ms ease all;
66 | }
67 | }
68 | &:before,
69 | &:after {
70 | content: '';
71 | position: absolute;
72 | top: 0;
73 | right: 0;
74 | height: 2px;
75 | width: 0;
76 | transition: 400ms ease all;
77 | }
78 | &::after {
79 | right: inherit;
80 | top: inherit;
81 | left: 0;
82 | bottom: 0;
83 | }
84 | }
85 |
86 | .custom-button {
87 | display: inline-block;
88 | line-height: 1;
89 | white-space: nowrap;
90 | cursor: pointer;
91 | background: #fff;
92 | color: #fff;
93 | -webkit-appearance: none;
94 | text-align: center;
95 | box-sizing: border-box;
96 | outline: 0;
97 | margin: 0;
98 | padding: 10px 15px;
99 | font-size: 14px;
100 | border-radius: 4px;
101 | }
102 |
103 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/pieChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
80 |
--------------------------------------------------------------------------------
/src/utils/fetch.js:
--------------------------------------------------------------------------------
1 | import axios from 'axios'
2 | import { Message } from 'element-ui'
3 | import store from '@/store'
4 | import { getToken } from '@/utils/auth'
5 |
6 | // 创建axios实例
7 | const service = axios.create({
8 | baseURL: process.env.BASE_API, // api的base_url
9 | timeout: 5000 // 请求超时时间
10 | })
11 |
12 | // request拦截器
13 | service.interceptors.request.use(config => {
14 | // Do something before request is sent
15 | if (store.getters.token) {
16 | config.headers['X-Token'] = getToken() // 让每个请求携带token--['X-Token']为自定义key 请根据实际情况自行修改
17 | }
18 | return config
19 | }, error => {
20 | // Do something with request error
21 | console.log(error) // for debug
22 | Promise.reject(error)
23 | })
24 |
25 | // respone拦截器
26 | service.interceptors.response.use(
27 | response => response,
28 | /**
29 | * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页
30 | * 如通过xmlhttprequest 状态码标识 逻辑可写在下面error中
31 | */
32 | // const res = response.data;
33 | // if (res.code !== 20000) {
34 | // Message({
35 | // message: res.message,
36 | // type: 'error',
37 | // duration: 5 * 1000
38 | // });
39 | // // 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
40 | // if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
41 | // MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
42 | // confirmButtonText: '重新登录',
43 | // cancelButtonText: '取消',
44 | // type: 'warning'
45 | // }).then(() => {
46 | // store.dispatch('FedLogOut').then(() => {
47 | // location.reload();// 为了重新实例化vue-router对象 避免bug
48 | // });
49 | // })
50 | // }
51 | // return Promise.reject('error');
52 | // } else {
53 | // return response.data;
54 | // }
55 | error => {
56 | console.log('err' + error)// for debug
57 | Message({
58 | message: error.message,
59 | type: 'error',
60 | duration: 5 * 1000
61 | })
62 | return Promise.reject(error)
63 | }
64 | )
65 |
66 | export default service
67 |
--------------------------------------------------------------------------------
/src/components/ImageCropper/utils.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 |
3 | /**
4 | *
5 | * @param e
6 | * @param arg_opts
7 | * @returns {boolean}
8 | */
9 | export function effectRipple(e, arg_opts) {
10 | let opts = Object.assign({
11 | ele: e.target, // 波纹作用元素
12 | type: 'hit', // hit点击位置扩散 center中心点扩展
13 | bgc: 'rgba(0, 0, 0, 0.15)' // 波纹颜色
14 | }, arg_opts),
15 | target = opts.ele;
16 | if (target) {
17 | let rect = target.getBoundingClientRect(),
18 | ripple = target.querySelector('.e-ripple');
19 | if (!ripple) {
20 | ripple = document.createElement('span');
21 | ripple.className = 'e-ripple';
22 | ripple.style.height = ripple.style.width = Math.max(rect.width, rect.height) + 'px';
23 | target.appendChild(ripple);
24 | } else {
25 | ripple.className = 'e-ripple';
26 | }
27 | switch (opts.type) {
28 | case 'center':
29 | ripple.style.top = (rect.height / 2 - ripple.offsetHeight / 2) + 'px';
30 | ripple.style.left = (rect.width / 2 - ripple.offsetWidth / 2) + 'px';
31 | break;
32 | default:
33 | ripple.style.top = (e.pageY - rect.top - ripple.offsetHeight / 2 - document.body.scrollTop) + 'px';
34 | ripple.style.left = (e.pageX - rect.left - ripple.offsetWidth / 2 - document.body.scrollLeft) + 'px';
35 | }
36 | ripple.style.backgroundColor = opts.bgc;
37 | ripple.className = 'e-ripple z-active';
38 | return false;
39 | }
40 | }
41 | // database64文件格式转换为2进制
42 | /**
43 | *
44 | * @param data
45 | * @param mime
46 | * @returns {*}
47 | */
48 | export function data2blob(data, mime) {
49 | // dataURL 的格式为 “data:image/png;base64,****”,逗号之前都是一些说明性的文字,我们只需要逗号之后的就行了
50 | data = data.split(',')[1];
51 | data = window.atob(data);
52 | var ia = new Uint8Array(data.length);
53 | for (var i = 0; i < data.length; i++) {
54 | ia[i] = data.charCodeAt(i);
55 | }
56 | // canvas.toDataURL 返回的默认格式就是 image/png
57 | return new Blob([ia], {type: mime});
58 | };
59 |
--------------------------------------------------------------------------------
/src/icons/svg/weixin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/mock/article.js:
--------------------------------------------------------------------------------
1 | import Mock from 'mockjs'
2 | import { param2Obj } from '@/utils'
3 |
4 | const List = []
5 | const count = 100
6 |
7 | for (let i = 0; i < count; i++) {
8 | List.push(Mock.mock({
9 | id: '@increment',
10 | timestamp: +Mock.Random.date('T'),
11 | author: '@cname',
12 | auditor: '@cname',
13 | title: '@ctitle(10, 20)',
14 | forecast: '@float(0, 100, 2, 2)',
15 | importance: '@integer(1, 3)',
16 | 'type|1': ['CN', 'US', 'JP', 'EU'],
17 | 'status|1': ['published', 'draft', 'deleted'],
18 | display_time: '@datetime',
19 | pageviews: '@integer(300, 5000)'
20 | }))
21 | }
22 |
23 | export default {
24 | getList: config => {
25 | const { importance, type, title, page = 1, limit = 20, sort } = param2Obj(config.url)
26 |
27 | let mockList = List.filter(item => {
28 | if (importance && item.importance !== +importance) return false
29 | if (type && item.type !== type) return false
30 | if (title && item.title.indexOf(title) < 0) return false
31 | return true
32 | })
33 |
34 | if (sort === '-id') {
35 | mockList = mockList.reverse()
36 | }
37 |
38 | const pageList = mockList.filter((item, index) => index < limit * page && index >= limit * (page - 1))
39 |
40 | return {
41 | total: mockList.length,
42 | items: pageList
43 | }
44 | },
45 | getPv: () => ({
46 | pvData: [{ key: 'PC网站', pv: 1024 }, { key: 'mobile网站', pv: 1024 }, { key: 'ios', pv: 1024 }, { key: 'android', pv: 1024 }]
47 | }),
48 | getArticle: () => ({
49 | id: 120000000001,
50 | author: { key: 'mockPan' },
51 | source_name: '原创作者',
52 | category_item: [{ key: 'global', name: '全球' }],
53 | comment_disabled: false,
54 | content: '我是测试数据我是测试数据

"',
55 | content_short: '我是测试数据',
56 | display_time: +new Date(),
57 | image_uri: 'https://wpimg.wallstcn.com/e4558086-631c-425c-9430-56ffb46e70b3',
58 | platforms: ['a-platform'],
59 | source_uri: 'https://github.com/PanJiaChen/vue-element-admin',
60 | status: 'published',
61 | tags: [],
62 | title: ''
63 | })
64 | }
65 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/barChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
94 |
--------------------------------------------------------------------------------
/src/views/login/socialsignin.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 微信
5 |
6 |
7 | QQ
8 |
9 |
10 |
11 |
12 |
35 |
36 |
69 |
--------------------------------------------------------------------------------
/src/views/errorPage/401.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
返回
4 |
5 |
6 | Oops!
7 | gif来源airbnb 页面
8 | 你没有权限去该页面
9 | 如有不满请联系你领导
10 |
11 | - 或者你可以去:
12 | -
13 | 回首页
14 |
15 | - 随便看看
16 | - 点我看图
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
51 |
52 |
88 |
--------------------------------------------------------------------------------
/src/views/theme/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 | 换肤:
10 |
11 |
12 |
13 |
14 |
15 |
16 | Button:
17 |
18 | 成功按钮
19 | 警告按钮
20 | 危险按钮
21 | 信息按钮
22 |
23 |
24 |
25 |
26 |
27 | {{tag.name}}
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
71 |
72 |
90 |
--------------------------------------------------------------------------------
/src/views/excel/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 导出excel
4 |
5 |
6 |
7 | {{scope.$index}}
8 |
9 |
10 |
11 |
12 | {{scope.row.title}}
13 |
14 |
15 |
16 |
17 | {{scope.row.author}}
18 |
19 |
20 |
21 |
22 | {{scope.row.pageviews}}
23 |
24 |
25 |
26 |
27 |
28 | {{scope.row.display_time}}
29 |
30 |
31 |
32 |
33 |
34 |
35 |
75 |
--------------------------------------------------------------------------------
/src/components/UploadExcel/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | select excel file
4 |
5 |
6 |
7 |
8 |
72 |
73 |
79 |
--------------------------------------------------------------------------------
/static/tinymce/skins/lightgray/content.inline.min.css:
--------------------------------------------------------------------------------
1 | .mce-content-body .mce-reset{margin:0;padding:0;border:0;outline:0;vertical-align:top;background:transparent;text-decoration:none;color:black;font-family:Arial;font-size:11px;text-shadow:none;float:none;position:static;width:auto;height:auto;white-space:nowrap;cursor:inherit;line-height:normal;font-weight:normal;text-align:left;-webkit-tap-highlight-color:transparent;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;direction:ltr;max-width:none}.mce-object{border:1px dotted #3A3A3A;background:#D5D5D5 url(img/object.gif) no-repeat center}.mce-preview-object{display:inline-block;position:relative;margin:0 2px 0 2px;line-height:0;border:1px solid gray}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-preview-object .mce-shim{position:absolute;top:0;left:0;width:100%;height:100%;background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)}figure.align-left{float:left}figure.align-right{float:right}figure.image.align-center{display:table;margin-left:auto;margin-right:auto}figure.image{display:inline-block;border:1px solid gray;margin:0 2px 0 1px;background:#f5f2f0}figure.image img{margin:8px 8px 0 8px}figure.image figcaption{margin:6px 8px 6px 8px;text-align:center}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-pagebreak{cursor:default;display:block;border:0;width:100%;height:5px;border:1px dashed #666;margin-top:15px;page-break-before:always}@media print{.mce-pagebreak{border:0}}.mce-item-anchor{cursor:default;display:inline-block;-webkit-user-select:all;-webkit-user-modify:read-only;-moz-user-select:all;-moz-user-modify:read-only;user-select:all;user-modify:read-only;width:9px !important;height:9px !important;border:1px dotted #3A3A3A;background:#D5D5D5 url(img/anchor.gif) no-repeat center}.mce-nbsp,.mce-shy{background:#AAA}.mce-shy::after{content:'-'}hr{cursor:default}.mce-match-marker{background:#AAA;color:#fff}.mce-match-marker-selected{background:#3399ff;color:#fff}.mce-spellchecker-word{border-bottom:2px solid #F00;cursor:default}.mce-spellchecker-grammar{border-bottom:2px solid #008000;cursor:default}.mce-item-table,.mce-item-table td,.mce-item-table th,.mce-item-table caption{border:1px dashed #BBB}td[data-mce-selected],th[data-mce-selected]{background-color:#3399ff !important}.mce-edit-focus{outline:1px dotted #333}.mce-content-body *[contentEditable=false] *[contentEditable=true]:focus{outline:2px solid #2d8ac7}.mce-content-body *[contentEditable=false] *[contentEditable=true]:hover{outline:2px solid #7ACAFF}.mce-content-body *[contentEditable=false][data-mce-selected]{outline:2px solid #2d8ac7}.mce-resize-bar-dragging{background-color:blue;opacity:.25;filter:alpha(opacity=25);zoom:1}
--------------------------------------------------------------------------------
/src/components/ErrLog/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | msg:{{ scope.row.err.message }}
17 |
18 | url: {{scope.row.url}}
19 |
20 |
21 |
22 |
23 | {{ scope.row.err.stack}}
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
47 |
48 |
57 |
--------------------------------------------------------------------------------
/src/views/example/tab/components/tabPane.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | {{scope.row.id}}
8 |
9 |
10 |
11 |
12 |
13 | {{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}
14 |
15 |
16 |
17 |
18 |
19 | {{scope.row.title}}
20 | {{scope.row.type}}
21 |
22 |
23 |
24 |
25 |
26 | {{scope.row.author}}
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {{scope.row.pageviews}}
39 |
40 |
41 |
42 |
43 |
44 | {{scope.row.status}}
45 |
46 |
47 |
48 |
49 |
50 |
51 |
98 |
99 |
--------------------------------------------------------------------------------
/src/components/MarkdownEditor/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 |
7 |
8 |
79 |
80 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/src/directive/sticky.js:
--------------------------------------------------------------------------------
1 | const vueSticky = {}
2 | let listenAction
3 | vueSticky.install = Vue => {
4 | Vue.directive('sticky', {
5 | inserted(el, binding) {
6 | const params = binding.value || {}
7 | const stickyTop = params.stickyTop || 0
8 | const zIndex = params.zIndex || 1000
9 | const elStyle = el.style
10 |
11 | elStyle.position = '-webkit-sticky'
12 | elStyle.position = 'sticky'
13 | // if the browser support css sticky(Currently Safari, Firefox and Chrome Canary)
14 | // if (~elStyle.position.indexOf('sticky')) {
15 | // elStyle.top = `${stickyTop}px`;
16 | // elStyle.zIndex = zIndex;
17 | // return
18 | // }
19 | const elHeight = el.getBoundingClientRect().height
20 | const elWidth = el.getBoundingClientRect().width
21 | elStyle.cssText = `top: ${stickyTop}px; z-index: ${zIndex}`
22 |
23 | const parentElm = el.parentNode || document.documentElement
24 | const placeholder = document.createElement('div')
25 | placeholder.style.display = 'none'
26 | placeholder.style.width = `${elWidth}px`
27 | placeholder.style.height = `${elHeight}px`
28 | parentElm.insertBefore(placeholder, el)
29 |
30 | let active = false
31 |
32 | const getScroll = (target, top) => {
33 | const prop = top ? 'pageYOffset' : 'pageXOffset'
34 | const method = top ? 'scrollTop' : 'scrollLeft'
35 | let ret = target[prop]
36 | if (typeof ret !== 'number') {
37 | ret = window.document.documentElement[method]
38 | }
39 | return ret
40 | }
41 |
42 | const sticky = () => {
43 | if (active) {
44 | return
45 | }
46 | if (!elStyle.height) {
47 | elStyle.height = `${el.offsetHeight}px`
48 | }
49 |
50 | elStyle.position = 'fixed'
51 | elStyle.width = `${elWidth}px`
52 | placeholder.style.display = 'inline-block'
53 | active = true
54 | }
55 |
56 | const reset = () => {
57 | if (!active) {
58 | return
59 | }
60 |
61 | elStyle.position = ''
62 | placeholder.style.display = 'none'
63 | active = false
64 | }
65 |
66 | const check = () => {
67 | const scrollTop = getScroll(window, true)
68 | const offsetTop = el.getBoundingClientRect().top
69 | if (offsetTop < stickyTop) {
70 | sticky()
71 | } else {
72 | if (scrollTop < elHeight + stickyTop) {
73 | reset()
74 | }
75 | }
76 | }
77 | listenAction = () => {
78 | check()
79 | }
80 |
81 | window.addEventListener('scroll', listenAction)
82 | },
83 |
84 | unbind() {
85 | window.removeEventListener('scroll', listenAction)
86 | }
87 | })
88 | }
89 |
90 | export default vueSticky
91 |
92 |
--------------------------------------------------------------------------------
/src/components/Screenfull/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
47 |
48 |
55 |
--------------------------------------------------------------------------------
/src/components/Charts/keyboard.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
114 |
--------------------------------------------------------------------------------
/src/components/Upload/singleImage2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | Drag或点击上传
7 |
8 |
9 |
10 |
![]()
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
66 |
67 |
119 |
--------------------------------------------------------------------------------
/src/views/example/table/inlineEditTable.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | {{scope.row.id}}
9 |
10 |
11 |
12 |
13 |
14 | {{scope.row.timestamp | parseTime('{y}-{m}-{d} {h}:{i}')}}
15 |
16 |
17 |
18 |
19 |
20 | {{scope.row.author}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | {{scope.row.status}}
33 |
34 |
35 |
36 |
37 |
38 |
39 | {{ scope.row.title }}
40 |
41 |
42 |
43 |
44 |
45 | {{scope.row.edit?'完成':'编辑'}}
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
96 |
--------------------------------------------------------------------------------
/src/views/excel/selectExcel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 导出已选择项
4 |
6 |
7 |
8 |
9 | {{scope.$index}}
10 |
11 |
12 |
13 |
14 | {{scope.row.title}}
15 |
16 |
17 |
18 |
19 | {{scope.row.author}}
20 |
21 |
22 |
23 |
24 | {{scope.row.pageviews}}
25 |
26 |
27 |
28 |
29 |
30 | {{scope.row.display_time}}
31 |
32 |
33 |
34 |
35 |
36 |
37 |
89 |
--------------------------------------------------------------------------------
/src/filters/index.js:
--------------------------------------------------------------------------------
1 | function pluralize(time, label) {
2 | if (time === 1) {
3 | return time + label
4 | }
5 | return time + label + 's'
6 | }
7 | export function timeAgo(time) {
8 | const between = Date.now() / 1000 - Number(time)
9 | if (between < 3600) {
10 | return pluralize(~~(between / 60), ' minute')
11 | } else if (between < 86400) {
12 | return pluralize(~~(between / 3600), ' hour')
13 | } else {
14 | return pluralize(~~(between / 86400), ' day')
15 | }
16 | }
17 |
18 | export function parseTime(time, cFormat) {
19 | if (arguments.length === 0) {
20 | return null
21 | }
22 |
23 | if ((time + '').length === 10) {
24 | time = +time * 1000
25 | }
26 |
27 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
28 | let date
29 | if (typeof time === 'object') {
30 | date = time
31 | } else {
32 | date = new Date(parseInt(time))
33 | }
34 | const formatObj = {
35 | y: date.getFullYear(),
36 | m: date.getMonth() + 1,
37 | d: date.getDate(),
38 | h: date.getHours(),
39 | i: date.getMinutes(),
40 | s: date.getSeconds(),
41 | a: date.getDay()
42 | }
43 | const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
44 | let value = formatObj[key]
45 | if (key === 'a') return ['一', '二', '三', '四', '五', '六', '日'][value - 1]
46 | if (result.length > 0 && value < 10) {
47 | value = '0' + value
48 | }
49 | return value || 0
50 | })
51 | return time_str
52 | }
53 |
54 | export function formatTime(time, option) {
55 | time = +time * 1000
56 | const d = new Date(time)
57 | const now = Date.now()
58 |
59 | const diff = (now - d) / 1000
60 |
61 | if (diff < 30) {
62 | return '刚刚'
63 | } else if (diff < 3600) { // less 1 hour
64 | return Math.ceil(diff / 60) + '分钟前'
65 | } else if (diff < 3600 * 24) {
66 | return Math.ceil(diff / 3600) + '小时前'
67 | } else if (diff < 3600 * 24 * 2) {
68 | return '1天前'
69 | }
70 | if (option) {
71 | return parseTime(time, option)
72 | } else {
73 | return d.getMonth() + 1 + '月' + d.getDate() + '日' + d.getHours() + '时' + d.getMinutes() + '分'
74 | }
75 | }
76 |
77 | /* 数字 格式化*/
78 | export function nFormatter(num, digits) {
79 | const si = [
80 | { value: 1E18, symbol: 'E' },
81 | { value: 1E15, symbol: 'P' },
82 | { value: 1E12, symbol: 'T' },
83 | { value: 1E9, symbol: 'G' },
84 | { value: 1E6, symbol: 'M' },
85 | { value: 1E3, symbol: 'k' }
86 | ]
87 | for (let i = 0; i < si.length; i++) {
88 | if (num >= si[i].value) {
89 | return (num / si[i].value + 0.1).toFixed(digits).replace(/\.0+$|(\.[0-9]*[1-9])0+$/, '$1') + si[i].symbol
90 | }
91 | }
92 | return num.toString()
93 | }
94 |
95 | export function html2Text(val) {
96 | const div = document.createElement('div')
97 | div.innerHTML = val
98 | return div.textContent || div.innerText
99 | }
100 |
101 | export function toThousandslsFilter(num) {
102 | return (+num || 0).toString().replace(/^-?\d+/g, m => m.replace(/(?=(?!\b)(\d{3})+$)/g, ','))
103 | }
104 |
--------------------------------------------------------------------------------
/src/components/BackToTop/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
13 |
14 |
83 |
84 |
111 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "juicy",
3 | "version": "2.1.0",
4 | "description": "A Vue.js admin",
5 | "author": "Pan ",
6 | "license": "MIT",
7 | "private": true,
8 | "scripts": {
9 | "dev": "node build/dev-server.js",
10 | "build:prod": "cross-env NODE_ENV=production node build/build.js",
11 | "build:sit": "cross-env NODE_ENV=sit node build/build.js",
12 | "build:sit-preview": "cross-env NODE_ENV=sit npm_config_preview=true npm_config_report=true node build/build.js",
13 | "lint": "eslint --ext .js,.vue src"
14 | },
15 | "dependencies": {
16 | "axios": "0.16.2",
17 | "codemirror": "5.26.0",
18 | "dropzone": "5.1.0",
19 | "echarts": "3.6.2",
20 | "element-ui": "1.4.2",
21 | "file-saver": "1.3.3",
22 | "js-cookie": "2.1.4",
23 | "jsonlint": "1.6.2",
24 | "mockjs": "1.0.1-beta3",
25 | "normalize.css": "7.0.0",
26 | "nprogress": "0.2.0",
27 | "screenfull": "3.2.2",
28 | "showdown": "1.7.1",
29 | "simplemde": "1.11.2",
30 | "sortablejs": "1.5.1",
31 | "vue": "2.4.2",
32 | "vue-count-to": "1.0.5",
33 | "vue-multiselect": "2.0.2",
34 | "vue-router": "2.7.0",
35 | "vue-splitpane": "^1.0.0",
36 | "vuedraggable": "2.14.1",
37 | "vuex": "2.3.1",
38 | "xlsx": "^0.10.8"
39 | },
40 | "devDependencies": {
41 | "autoprefixer": "7.1.1",
42 | "babel-core": "6.25.0",
43 | "babel-eslint": "7.2.3",
44 | "babel-loader": "7.0.0",
45 | "babel-plugin-transform-runtime": "6.23.0",
46 | "babel-preset-env": "1.5.2",
47 | "babel-preset-stage-2": "6.24.1",
48 | "babel-register": "6.24.1",
49 | "chalk": "1.1.3",
50 | "connect-history-api-fallback": "1.3.0",
51 | "copy-webpack-plugin": "4.0.1",
52 | "cross-env": "5.0.1",
53 | "css-loader": "0.28.4",
54 | "eslint": "3.19.0",
55 | "eslint-friendly-formatter": "3.0.0",
56 | "eslint-import-resolver-webpack": "0.8.1",
57 | "eslint-loader": "1.7.1",
58 | "eslint-plugin-html": "3.0.0",
59 | "eslint-plugin-import": "2.3.0",
60 | "eventsource-polyfill": "0.9.6",
61 | "express": "4.15.3",
62 | "extract-text-webpack-plugin": "2.1.2",
63 | "file-loader": "0.11.2",
64 | "friendly-errors-webpack-plugin": "1.6.1",
65 | "function-bind": "1.1.0",
66 | "html-webpack-plugin": "2.28.0",
67 | "http-proxy-middleware": "0.17.4",
68 | "node-sass": "^4.5.0",
69 | "opn": "4.0.2",
70 | "optimize-css-assets-webpack-plugin": "1.3.0",
71 | "ora": "1.1.0",
72 | "pushstate-server": "2.1.0",
73 | "rimraf": "2.6.0",
74 | "sass-loader": "6.0.5",
75 | "script-loader": "0.7.0",
76 | "semver": "5.3.0",
77 | "style-loader": "0.17.0",
78 | "svg-sprite-loader": "3.2.4",
79 | "url-loader": "0.5.8",
80 | "vue-loader": "13.0.4",
81 | "vue-style-loader": "3.0.1",
82 | "vue-template-compiler": "2.4.2",
83 | "webpack": "2.6.1",
84 | "webpack-bundle-analyzer": "2.8.2",
85 | "webpack-dev-middleware": "1.10.2",
86 | "webpack-hot-middleware": "2.18.0",
87 | "webpack-merge": "4.1.0"
88 | },
89 | "engines": {
90 | "node": ">= 4.0.0",
91 | "npm": ">= 3.0.0"
92 | },
93 | "browserlist": [
94 | "> 1%",
95 | "last 2 versions",
96 | "not ie <= 8"
97 | ]
98 | }
99 |
--------------------------------------------------------------------------------
/src/components/SplitPane/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
95 |
96 |
112 |
--------------------------------------------------------------------------------
/src/views/dashboard/admin/lineChart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
123 |
--------------------------------------------------------------------------------
/src/components/PanThumb/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
![]()
9 |
10 |
11 |
12 |
35 |
36 |
141 |
--------------------------------------------------------------------------------
/src/views/layout/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
![]()
11 |
12 |
13 |
14 |
15 |
16 | 首页
17 |
18 |
19 |
20 |
21 | 项目地址
22 |
23 |
24 | 退出登录
25 |
26 |
27 |
28 |
29 |
30 |
71 |
72 |
118 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/src/components/Tinymce/components/editorImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | 上传图片
4 |
5 |
6 |
16 | 点击上传
17 |
18 | 取 消
19 | 确 定
20 |
21 |
22 |
23 |
95 |
96 |
103 |
--------------------------------------------------------------------------------
/src/components/TodoList/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
16 |
17 |
31 |
32 |
33 |
34 |
112 |
113 |
116 |
--------------------------------------------------------------------------------
/src/views/components/sticky.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 平台
7 |
8 |
9 |
10 |
11 | {{item.name}}
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 外链
20 |
21 |
22 |
23 | 填写url
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | 发布
34 |
35 |
36 |
37 |
38 |
Sticky header 当页面滚动到预设的位置会吸附在顶部
39 |
我是占位
40 |
我是占位
41 |
我是占位
42 |
我是占位
43 |
我是占位
44 |
我是占位
45 |
我是占位
46 |
我是占位
47 |
我是占位
48 |
我是占位
49 |
我是占位
50 |
我是占位
51 |
我是占位
52 |
我是占位
53 |
我是占位
54 |
我是占位
55 |
我是占位
56 |
我是占位
57 |
我是占位
58 |
我是占位
59 |
我是占位
60 |
我是占位
61 |
我是占位
62 |
我是占位
63 |
我是占位
64 |
我是占位
65 |
我是占位
66 |
我是占位
67 |
我是占位
68 |
我是占位
69 |
我是占位
70 |
我是占位
71 |
我是占位
72 |
我是占位
73 |
我是占位
74 |
我是占位
75 |
我是占位
76 |
我是占位
77 |
我是占位
78 |
我是占位
79 |
我是占位
80 |
我是占位
81 |
我是占位
82 |
我是占位
83 |
我是占位
84 |
我是占位
85 |
我是占位
86 |
我是占位
87 |
我是占位
88 |
我是占位
89 |
我是占位
90 |
我是占位
91 |
92 |
93 |
94 |
95 |
96 |
120 |
121 |
126 |
127 |
--------------------------------------------------------------------------------
/src/components/Upload/singleImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | 将文件拖到此处,或点击上传
7 |
8 |
9 |
10 |
![]()
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
68 |
69 |
124 |
--------------------------------------------------------------------------------
/src/components/Upload/singleImage3.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 |
6 | 将文件拖到此处,或点击上传
7 |
8 |
9 |
10 |
  全球 付费节目单 最热 经济
11 |
![]()
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
![]()
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
76 |
77 |
147 |
--------------------------------------------------------------------------------
/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import { loginByUsername, logout, getUserInfo } from '@/api/login'
2 | import { getToken, setToken, removeToken } from '@/utils/auth'
3 |
4 | const user = {
5 | state: {
6 | user: '',
7 | status: '',
8 | code: '',
9 | token: getToken(),
10 | name: '',
11 | avatar: '',
12 | introduction: '',
13 | roles: [],
14 | setting: {
15 | articlePlatform: []
16 | }
17 | },
18 |
19 | mutations: {
20 | SET_CODE: (state, code) => {
21 | state.code = code
22 | },
23 | SET_TOKEN: (state, token) => {
24 | state.token = token
25 | },
26 | SET_INTRODUCTION: (state, introduction) => {
27 | state.introduction = introduction
28 | },
29 | SET_SETTING: (state, setting) => {
30 | state.setting = setting
31 | },
32 | SET_STATUS: (state, status) => {
33 | state.status = status
34 | },
35 | SET_NAME: (state, name) => {
36 | state.name = name
37 | },
38 | SET_AVATAR: (state, avatar) => {
39 | state.avatar = avatar
40 | },
41 | SET_ROLES: (state, roles) => {
42 | state.roles = roles
43 | }
44 | },
45 |
46 | actions: {
47 | // 用户名登录
48 | LoginByUsername({ commit }, userInfo) {
49 | const username = userInfo.username.trim()
50 | return new Promise((resolve, reject) => {
51 | loginByUsername(username, userInfo.password).then(response => {
52 | const data = response.data
53 | setToken(response.data.token)
54 | commit('SET_TOKEN', data.token)
55 | resolve()
56 | }).catch(error => {
57 | reject(error)
58 | })
59 | })
60 | },
61 |
62 | // 获取用户信息
63 | GetUserInfo({ commit, state }) {
64 | return new Promise((resolve, reject) => {
65 | getUserInfo(state.token).then(response => {
66 | const data = response.data
67 | commit('SET_ROLES', data.role)
68 | commit('SET_NAME', data.name)
69 | commit('SET_AVATAR', data.avatar)
70 | commit('SET_INTRODUCTION', data.introduction)
71 | resolve(response)
72 | }).catch(error => {
73 | reject(error)
74 | })
75 | })
76 | },
77 |
78 | // 第三方验证登录
79 | // LoginByThirdparty({ commit, state }, code) {
80 | // return new Promise((resolve, reject) => {
81 | // commit('SET_CODE', code)
82 | // loginByThirdparty(state.status, state.email, state.code).then(response => {
83 | // commit('SET_TOKEN', response.data.token)
84 | // setToken(response.data.token)
85 | // resolve()
86 | // }).catch(error => {
87 | // reject(error)
88 | // })
89 | // })
90 | // },
91 |
92 | // 登出
93 | LogOut({ commit, state }) {
94 | return new Promise((resolve, reject) => {
95 | logout(state.token).then(() => {
96 | commit('SET_TOKEN', '')
97 | commit('SET_ROLES', [])
98 | removeToken()
99 | resolve()
100 | }).catch(error => {
101 | reject(error)
102 | })
103 | })
104 | },
105 |
106 | // 前端 登出
107 | FedLogOut({ commit }) {
108 | return new Promise(resolve => {
109 | commit('SET_TOKEN', '')
110 | removeToken()
111 | resolve()
112 | })
113 | },
114 |
115 | // 动态修改权限
116 | ChangeRole({ commit }, role) {
117 | return new Promise(resolve => {
118 | commit('SET_TOKEN', role)
119 | setToken(role)
120 | getUserInfo(role).then(response => {
121 | const data = response.data
122 | commit('SET_ROLES', data.role)
123 | commit('SET_NAME', data.name)
124 | commit('SET_AVATAR', data.avatar)
125 | commit('SET_INTRODUCTION', data.introduction)
126 | resolve()
127 | })
128 | })
129 | }
130 | }
131 | }
132 |
133 | export default user
134 |
--------------------------------------------------------------------------------
/src/components/Charts/keyboard2.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
151 |
--------------------------------------------------------------------------------