├── babel.config.js
├── TODO.md
├── public
├── favicon.ico
└── index.html
├── .travis.yml
├── src
├── assets
│ └── 404_images
│ │ ├── 404.png
│ │ └── 404_cloud.png
├── layout
│ ├── components
│ │ ├── index.js
│ │ ├── Sidebar
│ │ │ ├── Item.vue
│ │ │ ├── FixiOSBug.js
│ │ │ ├── Link.vue
│ │ │ ├── index.vue
│ │ │ ├── Logo.vue
│ │ │ └── SidebarItem.vue
│ │ ├── AppMain.vue
│ │ └── Navbar.vue
│ ├── mixin
│ │ └── ResizeHandler.js
│ └── index.vue
├── icons
│ ├── svg
│ │ ├── link.svg
│ │ ├── example.svg
│ │ ├── table.svg
│ │ ├── user.svg
│ │ ├── nested.svg
│ │ ├── market.svg
│ │ ├── eye.svg
│ │ ├── feedback.svg
│ │ ├── comment.svg
│ │ ├── admin.svg
│ │ ├── setting.svg
│ │ ├── eye-open.svg
│ │ ├── activity.svg
│ │ ├── order.svg
│ │ ├── ordersvg.svg
│ │ ├── banner.svg
│ │ ├── password.svg
│ │ ├── brand.svg
│ │ ├── operation.svg
│ │ ├── tree.svg
│ │ ├── afterSales.svg
│ │ ├── cat.svg
│ │ ├── recommends.svg
│ │ ├── dashboard.svg
│ │ ├── form.svg
│ │ ├── coupon.svg
│ │ ├── supperlier.svg
│ │ └── service.svg
│ ├── index.js
│ └── svgo.yml
├── utils
│ ├── auth.js
│ ├── getPageTitle.js
│ ├── logisticsCompany.js
│ ├── validate.js
│ ├── scroll-to.js
│ ├── guid.js
│ ├── request.js
│ └── index.js
├── core
│ └── directives
│ │ ├── directives.js
│ │ ├── index.js
│ │ └── module
│ │ └── clipboard.js
├── api
│ ├── feedback.js
│ ├── upload.js
│ ├── app.js
│ ├── brand.js
│ ├── supplier.js
│ ├── orderAfterService.js
│ ├── cat.js
│ ├── afterService.js
│ ├── banner.js
│ ├── refundReasons.js
│ ├── recommends.js
│ ├── admin.js
│ ├── order.js
│ ├── user.js
│ ├── comment.js
│ ├── catSpec.js
│ ├── coupon.js
│ ├── goods.js
│ └── activity.js
├── config
│ └── index.js
├── main.js
├── styles
│ ├── mixin.scss
│ ├── variables.scss
│ ├── transition.scss
│ ├── element-ui.scss
│ ├── index.scss
│ └── sidebar.scss
├── settings.js
├── store
│ ├── getters.js
│ ├── modules
│ │ ├── settings.js
│ │ ├── app.js
│ │ └── user.js
│ └── index.js
├── install.js
├── App.vue
├── components
│ ├── Hamburger
│ │ └── index.vue
│ ├── SvgIcon
│ │ └── index.vue
│ ├── Breadcrumb
│ │ └── index.vue
│ ├── password
│ │ └── index.vue
│ ├── Table
│ │ └── index.vue
│ ├── upload
│ │ └── index.vue
│ └── cropperImg
│ │ └── index.vue
├── views
│ ├── dashboard
│ │ └── index.vue
│ ├── worksheet
│ │ ├── index.vue
│ │ └── worksheetcom.vue
│ ├── 404.vue
│ ├── login
│ │ └── index.vue
│ └── Admin
│ │ └── index.vue
└── router
│ └── index.js
├── jsconfig.json
├── postcss.config.js
├── .gitignore
├── README.md
├── .env.development
├── .env.staging
├── .env.production
├── package.json
└── vue.config.js
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/TODO.md:
--------------------------------------------------------------------------------
1 | # 待完成时间列表
2 |
3 | - 陈伟完成基础框架搭建
4 |
5 | - 表格封装 田浩
6 | - tree 田浩
7 | - 进度 田浩
8 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoliancaijing/admin-vue-template/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: 10
3 | script: npm run test
4 | notifications:
5 | email: false
6 |
--------------------------------------------------------------------------------
/src/assets/404_images/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoliancaijing/admin-vue-template/HEAD/src/assets/404_images/404.png
--------------------------------------------------------------------------------
/src/assets/404_images/404_cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xiaoliancaijing/admin-vue-template/HEAD/src/assets/404_images/404_cloud.png
--------------------------------------------------------------------------------
/src/layout/components/index.js:
--------------------------------------------------------------------------------
1 | export { default as Navbar } from './Navbar'
2 | export { default as Sidebar } from './Sidebar'
3 | export { default as AppMain } from './AppMain'
4 |
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "@/*": ["src/*"]
6 | }
7 | },
8 | "exclude": ["node_modules", "dist"]
9 | }
10 |
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | // https://github.com/michael-ciniawsky/postcss-load-config
2 |
3 | module.exports = {
4 | 'plugins': {
5 | // to edit target browsers: use "browserslist" field in package.json
6 | 'autoprefixer': {}
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules/
3 | dist/
4 | build/
5 | npm-debug.log*
6 | yarn-debug.log*
7 | yarn-error.log*
8 | package-lock.json
9 | tests/**/coverage/
10 | .history/
11 |
12 | # Editor directories and files
13 | .idea
14 | .vscode
15 | *.suo
16 | *.ntvs*
17 | *.njsproj
18 | *.sln
19 |
--------------------------------------------------------------------------------
/src/icons/svg/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-admin-template
2 | vue3.0 后台管理系统模板 开箱即用,基于vue-cli、element、vuex, 七牛 演示地址:
3 |
4 |
5 | ## 技术
6 |
7 | > 当前模板基于 vue3.0、 vue-cli3.0 、element 2.23.x 、七牛的文件存储
8 | -
9 |
10 | ## 组件
11 | -
12 | ## units 说明
13 | -
14 |
15 | ## 外部插件
16 | -
17 |
18 | ## 七牛集成 图片上传,文件上传说明
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/icons/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import SvgIcon from '@/components/SvgIcon'// svg component
3 |
4 | // register globally
5 | Vue.component('svg-icon', SvgIcon)
6 |
7 | const req = require.context('./svg', false, /\.svg$/)
8 | const requireAll = requireContext => requireContext.keys().map(requireContext)
9 | requireAll(req)
10 |
--------------------------------------------------------------------------------
/src/utils/auth.js:
--------------------------------------------------------------------------------
1 | import Cookies from 'js-cookie'
2 |
3 | const TokenKey = 'Authorization'
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/core/directives/directives.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-07-31 14:58:58
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 14:59:28
6 | * @Descripttion: 所有的指令集合
7 | */
8 |
9 | import clipboard from './module/clipboard'
10 |
11 | const directives = {
12 | clipboard,
13 | }
14 |
15 | export default directives
16 |
--------------------------------------------------------------------------------
/src/icons/svgo.yml:
--------------------------------------------------------------------------------
1 | # replace default config
2 |
3 | # multipass: true
4 | # full: true
5 |
6 | plugins:
7 |
8 | # - name
9 | #
10 | # or:
11 | # - name: false
12 | # - name: true
13 | #
14 | # or:
15 | # - name:
16 | # param1: 1
17 | # param2: 2
18 |
19 | - removeAttrs:
20 | attrs:
21 | - 'fill'
22 | - 'fill-rule'
23 |
--------------------------------------------------------------------------------
/src/api/feedback.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-18 20:22:23
4 | * @LastEditors: tianhao
5 | * @LastEditTime: 2020-06-18 20:22:49
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request';
9 | export function getList(data) {
10 | return request({
11 | url: '/adminApi/feedback',
12 | method: 'get',
13 | data,
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/src/api/upload.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-12 18:25:22
4 | * @LastEditors: 刘家辰
5 | * @LastEditTime: 2020-07-03 10:11:27
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 | export function getToken(data) {
10 | return request({
11 | url: '/adminApi/qiniu/token',
12 | method: 'get',
13 | data,
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/src/config/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-06-12 18:39:19
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 11:51:04
6 | * @Descripttion:
7 | */
8 | export const url = {
9 | qiniuUrl: process.env.VUE_APP_QN_UP_URL, //七牛上传url
10 | qiniuDownload: process.env.VUE_APP_QN_DOWNLOAD_URL, //七牛上传文件下载url qbqompd3x.bkt.clouddn.com
11 | }
12 |
--------------------------------------------------------------------------------
/.env.development:
--------------------------------------------------------------------------------
1 | # 模式
2 | ENV = 'production'
3 |
4 | # 基础api 地址
5 | VUE_APP_BASE_API = 'http://192.168.101.100:3000'
6 |
7 | # 七牛下载地址
8 | VUE_APP_QN_DOWNLOAD_URL = 'http://cdn.test.xiang360.com'
9 |
10 | # 七牛上传地址
11 | VUE_APP_QN_up_URL = 'https://up-z2.qiniup.com'
12 |
13 | # 优惠券h5分享地址
14 | VUE_APP_H5_API = 'http://h5.test.xiang360.com/'
15 |
16 | VUE_CLI_BABEL_TRANSPILE_MODULES = true
17 |
--------------------------------------------------------------------------------
/src/api/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: xin.chen
3 | * @Date: 2020-09-21 19:03:58
4 | * @LastEditors: xin.chen
5 | * @LastEditTime: 2020-09-21 19:08:38
6 | * @Descripttion:
7 | */
8 |
9 | import request from '@/utils/request';
10 |
11 | // 获取支持服务
12 | export function getWorksheet(data) {
13 | return request({
14 | url: '/worksheetlist',
15 | method: 'get',
16 | data,
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/.env.staging:
--------------------------------------------------------------------------------
1 | # 模式
2 | ENV = 'production'
3 |
4 | # 接口API 地址
5 | VUE_APP_BASE_API = 'http://api.test.xiang360.com/'
6 |
7 | # 第三方地址配置
8 | # 七牛上传地址 上传地址都是同一个
9 | VUE_APP_QN_UP_URL = 'https://up-z2.qiniup.com'
10 | # 七牛下载地址
11 | VUE_APP_QN_DOWNLOAD_URL = 'http://cdn.test.xiang360.com'
12 |
13 |
14 |
15 | # 优惠券h5分享地址
16 | VUE_APP_H5_API = 'http://h5.test.xiang360.com/'
17 |
18 | VUE_CLI_BABEL_TRANSPILE_MODULES = true
19 |
--------------------------------------------------------------------------------
/.env.production:
--------------------------------------------------------------------------------
1 | # 模式
2 | ENV = 'production'
3 |
4 | # 接口API 地址
5 | VUE_APP_BASE_API = 'http://api.test.xiang360.com/'
6 |
7 | # 第三方地址配置
8 | # 七牛上传地址 上传地址都是同一个
9 | VUE_APP_QN_UP_URL = 'https://up-z2.qiniup.com'
10 | # 七牛下载地址
11 | VUE_APP_QN_DOWNLOAD_URL = 'http://cdn.test.xiang360.com'
12 |
13 |
14 |
15 | # 优惠券h5分享地址
16 | VUE_APP_H5_API = 'http://h5.test.xiang360.com/'
17 |
18 | VUE_CLI_BABEL_TRANSPILE_MODULES = true
19 |
20 |
--------------------------------------------------------------------------------
/src/utils/getPageTitle.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-06-11 12:42:39
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 15:52:21
6 | * @Descripttion:
7 | */
8 |
9 | import defaultSettings from '@/settings'
10 |
11 | const title = defaultSettings.title || 'Admin'
12 |
13 | export function getPageTitle(pageTitle) {
14 | if (pageTitle) {
15 | return `${pageTitle} - ${title}`
16 | }
17 | return `${title}`
18 | }
19 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-11 12:42:56
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 12:49:58
6 | * @Descripttion:
7 | */
8 |
9 | import Vue from 'vue'
10 |
11 | import './install'
12 |
13 | import App from './App'
14 | import store from './store'
15 | import router from './router'
16 |
17 | Vue.config.productionTip = false
18 |
19 | new Vue({
20 | el: '#app',
21 | router,
22 | store,
23 | render: h => h(App),
24 | })
25 |
--------------------------------------------------------------------------------
/src/icons/svg/example.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/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 |
14 | &::-webkit-scrollbar {
15 | width: 6px;
16 | }
17 |
18 | &::-webkit-scrollbar-thumb {
19 | background: #99a9bf;
20 | border-radius: 20px;
21 | }
22 | }
23 |
24 | @mixin relative {
25 | position: relative;
26 | width: 100%;
27 | height: 100%;
28 | }
29 |
--------------------------------------------------------------------------------
/src/settings.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-06-11 12:42:39
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 12:21:34
6 | * @Descripttion: 项目的配置文件,主要是配置惨淡 头部logo 等
7 | */
8 |
9 | module.exports = {
10 | /**
11 | * 项目的默认标题
12 | */
13 | title: 'Admin',
14 |
15 | /**
16 | * @type {boolean} true | false
17 | * @description 固定顶部
18 | */
19 | fixedHeader: true,
20 |
21 | /**
22 | * @type {boolean} true | false
23 | * @description 侧边栏是否显示logo
24 | */
25 | sidebarLogo: true,
26 | }
27 |
--------------------------------------------------------------------------------
/src/store/getters.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-06-20 18:55:32
4 | * @LastEditors: xin.chen
5 | * @LastEditTime: 2020-09-21 12:14:06
6 | * @Descripttion:
7 | */
8 |
9 | const getters = {
10 | sidebar: state => state.app.sidebar,
11 | device: state => state.app.device,
12 | upLoadToken: state => state.app.upLoadToken,
13 | token: state => state.user.token,
14 | avatar: state => state.user.avatar,
15 | name: state => state.user.name,
16 | field: state => state.app.field,
17 | };
18 | export default getters;
19 |
--------------------------------------------------------------------------------
/src/core/directives/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-07-31 14:57:36
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 15:02:21
6 | * @Descripttion: 配置vue 的指令
7 | */
8 |
9 | import directive from './directives'
10 |
11 | const importDirective = Vue => {
12 | /**
13 | * clipboard指令 v-draggable="options"
14 | * options = {
15 | * value: /在输入框中使用v-model绑定的值/,
16 | * success: /复制成功后的回调/,
17 | * error: /复制失败后的回调/
18 | * }
19 | */
20 | Vue.directive('clipboard', directive.clipboard)
21 | }
22 |
23 | export default importDirective
24 |
--------------------------------------------------------------------------------
/src/icons/svg/table.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layout/components/Sidebar/Item.vue:
--------------------------------------------------------------------------------
1 |
30 |
--------------------------------------------------------------------------------
/src/api/brand.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-16 10:10:11
4 | * @LastEditors: 刘家辰
5 | * @LastEditTime: 2020-07-03 10:10:32
6 | * @Descripttion: 品牌管理
7 | */
8 | import request from '@/utils/request'
9 | export function getBrand(data) {
10 | return request({
11 | url: '/adminApi/brand',
12 | method: 'get',
13 | data,
14 | })
15 | }
16 | export function addBrand(data) {
17 | return request({
18 | url: '/adminApi/brand',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 | export function editBrand(data) {
24 | return request({
25 | url: `/adminApi/brand/${data.id}`,
26 | method: 'put',
27 | data,
28 | })
29 | }
30 |
--------------------------------------------------------------------------------
/src/icons/svg/user.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/store/modules/settings.js:
--------------------------------------------------------------------------------
1 | import defaultSettings from '@/settings'
2 |
3 | const { showSettings, fixedHeader, sidebarLogo } = defaultSettings
4 |
5 | const state = {
6 | showSettings: showSettings,
7 | fixedHeader: fixedHeader,
8 | sidebarLogo: sidebarLogo
9 | }
10 |
11 | const mutations = {
12 | CHANGE_SETTING: (state, { key, value }) => {
13 | if (state.hasOwnProperty(key)) {
14 | state[key] = value
15 | }
16 | }
17 | }
18 |
19 | const actions = {
20 | changeSetting({ commit }, data) {
21 | commit('CHANGE_SETTING', data)
22 | }
23 | }
24 |
25 | export default {
26 | namespaced: true,
27 | state,
28 | mutations,
29 | actions
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | <%= webpackConfig.name %>
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/layout/components/Sidebar/FixiOSBug.js:
--------------------------------------------------------------------------------
1 | export default {
2 | computed: {
3 | device() {
4 | return this.$store.state.app.device;
5 | },
6 | },
7 | mounted() {
8 | // In order to fix the click on menu on the ios device will trigger the mouseleave bug
9 | // https://github.com/PanJiaChen/vue-element-adminApi/issues/1135
10 | this.fixBugIniOS();
11 | },
12 | methods: {
13 | fixBugIniOS() {
14 | const $subMenu = this.$refs.subMenu;
15 | if ($subMenu) {
16 | const handleMouseleave = $subMenu.handleMouseleave;
17 | $subMenu.handleMouseleave = e => {
18 | if (this.device === 'mobile') {
19 | return;
20 | }
21 | handleMouseleave(e);
22 | };
23 | }
24 | },
25 | },
26 | };
27 |
--------------------------------------------------------------------------------
/src/store/index.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-12 17:14:55
4 | * @LastEditors: tianhao
5 | * @LastEditTime: 2020-06-12 17:58:35
6 | * @Descripttion:
7 | */
8 |
9 | import Vue from 'vue';
10 | import Vuex from 'vuex';
11 | import getters from './getters';
12 | import app from './modules/app';
13 | import settings from './modules/settings';
14 | import user from './modules/user';
15 | import createPersistedState from 'vuex-persistedstate';
16 | Vue.use(Vuex);
17 |
18 | const store = new Vuex.Store({
19 | modules: {
20 | app,
21 | settings,
22 | user,
23 | },
24 | getters,
25 | plugins: [
26 | createPersistedState({
27 | storage: window.sessionStorage,
28 | }),
29 | ],
30 | });
31 |
32 | export default store;
33 |
--------------------------------------------------------------------------------
/src/utils/logisticsCompany.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-06-23 14:51:35
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-06-23 14:54:54
6 | * @Descripttion:
7 | */
8 |
9 | let logistiCsompany = [
10 | { code: '1', name: '顺丰快递' },
11 | { code: '2', name: '中通快递' },
12 | { code: '3', name: '圆通快递' },
13 | { code: '4', name: '韵达快递' },
14 | { code: '5', name: '申通快递' },
15 | { code: '6', name: '百世汇通' },
16 | { code: '7', name: '邮政EMS' },
17 | { code: '8', name: '联通快递' },
18 | { code: '9', name: '天天快递' },
19 | ]
20 |
21 | // 1.顺丰快递
22 |
23 | // 2.中通快递
24 |
25 | // 3.圆通快递
26 |
27 | // 4.韵达快递
28 |
29 | // 5.申通快递
30 |
31 | // 6.百世汇通
32 |
33 | // 7.邮政EMS
34 |
35 | // 8.联通快递
36 |
37 | // 9.天天快递
38 |
39 | export { logistiCsompany }
40 |
--------------------------------------------------------------------------------
/src/utils/validate.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-11 11:46:00
4 | * @LastEditTime: 2020-07-31 12:54:10
5 | * @LastEditors: wei.chen
6 | * @Description:
7 | */
8 |
9 | /**
10 | * Created by PanJiaChen on 16/11/18.
11 | */
12 |
13 | /**
14 | * @param {string} path
15 | * @returns {Boolean}
16 | */
17 | export function isExternal(path) {
18 | return /^(https?:|mailto:|tel:)/.test(path)
19 | }
20 |
21 | /**
22 | * @name:
23 | * @description: 手机号码校验
24 | * 进行严谨的规则匹配
25 | * /^[1](([3][0-9])|([4][5-9])|([5][0-3,5-9])|([6][5,6])|([7][0-8])|([8][0-9])|([9][1,8,9]))[0-9]{8}$/
26 | * 非强校验
27 | * @param {type}
28 | * @return:
29 | */
30 | export function isPhone(val) {
31 | const phoneReg = /^[1]([3-9])[0-9]{9}$/
32 | return phoneReg.test(val)
33 | }
34 |
--------------------------------------------------------------------------------
/src/styles/variables.scss:
--------------------------------------------------------------------------------
1 | // sidebar
2 | $menuText: #bfcbd9;
3 | $menuActiveText: #409eff;
4 | $subMenuActiveText: #f4f4f5; //https://github.com/ElemeFE/element/issues/12951
5 | $navTextColor: #fff;
6 | $menuBg: #2e2f30;
7 | $menuHover: #001529;
8 | $subMenuBg: #000c17;
9 | $subMenuHover: #000c17;
10 |
11 | $sideBarWidth: 210px;
12 |
13 | // the :export directive is the magic sauce for webpack
14 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
15 | :export {
16 | menuText: $menuText;
17 | menuActiveText: $menuActiveText;
18 | subMenuActiveText: $subMenuActiveText;
19 | menuBg: $menuBg;
20 | menuHover: $menuHover;
21 | subMenuBg: $subMenuBg;
22 | subMenuHover: $subMenuHover;
23 | sideBarWidth: $sideBarWidth;
24 | navTextColor: $navTextColor;
25 | }
26 |
--------------------------------------------------------------------------------
/src/icons/svg/nested.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/supplier.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-12 17:33:37
4 | * @LastEditors: 刘家辰
5 | * @LastEditTime: 2020-07-03 10:11:15
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 |
10 | export function getList(data) {
11 | return request({
12 | url: '/adminApi/supplier',
13 | method: 'get',
14 | data,
15 | })
16 | }
17 | export function addSupplier(data) {
18 | return request({
19 | url: '/adminApi/supplier',
20 | method: 'post',
21 | data,
22 | })
23 | }
24 | export function editSupplier(data) {
25 | return request({
26 | url: `/adminApi/supplier/${data.id}`,
27 | method: 'put',
28 | data,
29 | })
30 | }
31 | export function getSupplierBrand(data) {
32 | return request({
33 | url: `/adminApi/supplier/${data.id}/brand`,
34 | method: 'get',
35 | data,
36 | })
37 | }
38 |
--------------------------------------------------------------------------------
/src/layout/components/Sidebar/Link.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
44 |
--------------------------------------------------------------------------------
/src/icons/svg/market.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/eye.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/transition.scss:
--------------------------------------------------------------------------------
1 | // global transition css
2 |
3 | /* fade */
4 | .fade-enter-active,
5 | .fade-leave-active {
6 | transition: opacity 0.28s;
7 | }
8 |
9 | .fade-enter,
10 | .fade-leave-active {
11 | opacity: 0;
12 | }
13 |
14 | /* fade-transform */
15 | .fade-transform-leave-active,
16 | .fade-transform-enter-active {
17 | transition: all .5s;
18 | }
19 |
20 | .fade-transform-enter {
21 | opacity: 0;
22 | transform: translateX(-30px);
23 | }
24 |
25 | .fade-transform-leave-to {
26 | opacity: 0;
27 | transform: translateX(30px);
28 | }
29 |
30 | /* breadcrumb transition */
31 | .breadcrumb-enter-active,
32 | .breadcrumb-leave-active {
33 | transition: all .5s;
34 | }
35 |
36 | .breadcrumb-enter,
37 | .breadcrumb-leave-active {
38 | opacity: 0;
39 | transform: translateX(20px);
40 | }
41 |
42 | .breadcrumb-move {
43 | transition: all .5s;
44 | }
45 |
46 | .breadcrumb-leave-active {
47 | position: absolute;
48 | }
49 |
--------------------------------------------------------------------------------
/src/icons/svg/feedback.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/orderAfterService.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-07-01 19:38:31
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-07-01 19:43:20
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 |
10 | // 获取售后单列表
11 | export function getAfterServiceList(data) {
12 | return request({
13 | url: '/adminApi/order/afterService',
14 | method: 'get',
15 | data,
16 | })
17 | }
18 | // 获取售后单详情
19 | export function getAfterServiceInfo(data) {
20 | return request({
21 | url: `/adminApi/order/afterService/${data.id}`,
22 | method: 'get',
23 | data,
24 | })
25 | }
26 | // 售后申请通过
27 | export function applyForPass(data) {
28 | return request({
29 | url: `/adminApi/order/afterService/${data.id}/pass`,
30 | method: 'put',
31 | data,
32 | })
33 | }
34 | // 售后申请拒绝
35 | export function applyForReject(data) {
36 | return request({
37 | url: `/adminApi/order/afterService/${data.id}/reject`,
38 | method: 'put',
39 | data,
40 | })
41 | }
42 |
--------------------------------------------------------------------------------
/src/install.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-07-31 12:45:27
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 12:51:09
6 | * @Descripttion: 这个文件是用来安装其它第三方组件配置
7 | */
8 | import Vue from 'vue'
9 | // 图片剪切组件
10 | import VueCropper from 'vue-cropper'
11 | // 样式
12 | import 'normalize.css/normalize.css' // css 重置样式
13 | import '@/styles/index.scss' // 全局样式
14 |
15 | // element 配置
16 | import ElementUI from 'element-ui'
17 | import 'element-ui/lib/theme-chalk/index.css'
18 | import locale from 'element-ui/lib/locale/lang/en' // lang i18n
19 |
20 | // 全局修改默认值
21 | ElementUI.Button.props.size.default = 'small'
22 | // console.log('ElementUI', ElementUI.Button.props);
23 | ElementUI.Dialog.props.destroyOnClose.default = true
24 | ElementUI.Dialog.props.customClass.default = 'dialogStyle'
25 | // console.log('ElementUI', ElementUI.Dialog);
26 | Vue.use(ElementUI)
27 |
28 | // vue
29 |
30 | Vue.use(VueCropper)
31 |
32 | // icon 配置导入
33 | import '@/icons' // icon
34 |
--------------------------------------------------------------------------------
/src/icons/svg/comment.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/element-ui.scss:
--------------------------------------------------------------------------------
1 | // cover some element-ui styles
2 |
3 | .el-breadcrumb__inner,
4 | .el-breadcrumb__inner a {
5 | font-weight: 400 !important;
6 | }
7 |
8 | .el-upload {
9 | input[type='file'] {
10 | display: none !important;
11 | }
12 | }
13 |
14 | .el-upload__input {
15 | display: none;
16 | }
17 |
18 | // to fixed https://github.com/ElemeFE/element/issues/2461
19 | .el-dialog {
20 | transform: none;
21 | left: 0;
22 | position: relative;
23 | margin: 0 auto;
24 | }
25 |
26 | // refine element ui upload
27 | .upload-container {
28 | .el-upload {
29 | width: 100%;
30 |
31 | .el-upload-dragger {
32 | width: 100%;
33 | height: 200px;
34 | }
35 | }
36 | }
37 |
38 | // dropdown
39 | .el-dropdown-menu {
40 | a {
41 | display: block;
42 | }
43 | }
44 |
45 | // to fix el-date-picker css style
46 | .el-range-separator {
47 | box-sizing: content-box;
48 | }
49 |
50 | .el-input {
51 | width: auto;
52 | }
53 |
54 | .el-row {
55 | margin-bottom: 10px;
56 | &:last-child {
57 | margin-bottom: 0;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/icons/svg/admin.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/cat.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-16 10:49:24
4 | * @LastEditTime: 2020-06-16 11:13:30
5 | * @LastEditors: Please set LastEditors
6 | * @Description:一二级类目管理接口
7 | */
8 |
9 | import request from '@/utils/request';
10 |
11 | // 获取类目管理
12 | export function getAllCat(data) {
13 | return request({
14 | url: '/adminApi/cats',
15 | method: 'get',
16 | data,
17 | });
18 | }
19 | // 创建类目管理
20 | export function createCat(data) {
21 | return request({
22 | url: '/adminApi/cats',
23 | method: 'post',
24 | data,
25 | });
26 | }
27 | // 编辑类目管理
28 | export function editCat(data) {
29 | return request({
30 | url: `/adminApi/cats/${data.id}`,
31 | method: 'put',
32 | data,
33 | });
34 | }
35 | // 删除类目管理
36 | export function deleteCat(data) {
37 | return request({
38 | url: `/adminApi/cats/${data.id}`,
39 | method: 'delete',
40 | data,
41 | });
42 | }
43 |
44 | // 禁用类目管理
45 | export function banCat(data) {
46 | return request({
47 | url: `/adminApi/cats/${data.id}/banOrEnable`,
48 | method: 'put',
49 | data,
50 | });
51 | }
52 |
--------------------------------------------------------------------------------
/src/api/afterService.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-16 18:55:40
4 | * @LastEditTime: 2020-06-19 20:19:05
5 | * @LastEditors: tianhao
6 | * @Description:服务管理 serviceManage
7 | */
8 |
9 | import request from '@/utils/request';
10 |
11 | // 支持服务管理
12 |
13 | // 获取支持服务
14 | export function getService(data) {
15 | return request({
16 | url: '/adminApi/afterService',
17 | method: 'get',
18 | data,
19 | });
20 | }
21 |
22 | // 创建支持服务
23 | export function createService(data) {
24 | return request({
25 | url: '/adminApi/afterService',
26 | method: 'post',
27 | data,
28 | });
29 | }
30 |
31 | // 编辑支持服务
32 | export function editService(data) {
33 | return request({
34 | url: `/adminApi/afterService/${data.id}`,
35 | method: 'put',
36 | data,
37 | });
38 | }
39 | // 运费规则
40 | export function getFreight(data) {
41 | return request({
42 | url: '/adminApi/fareConfig',
43 | method: 'get',
44 | data,
45 | });
46 | }
47 | // 保存运费规则
48 | export function saveFreight(data) {
49 | return request({
50 | url: '/adminApi/fareConfig',
51 | method: 'post',
52 | data,
53 | });
54 | }
55 |
--------------------------------------------------------------------------------
/src/api/banner.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-17 16:57:20
4 | * @LastEditors: hao.tian
5 | * @LastEditTime: 2020-06-23 14:39:16
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 | export function getList(data) {
10 | return request({
11 | url: '/adminApi/banner',
12 | method: 'get',
13 | data,
14 | })
15 | }
16 | export function AddBanner(data) {
17 | return request({
18 | url: '/adminApi/banner',
19 | method: 'post',
20 | data,
21 | })
22 | }
23 | export function deleteBanner(data) {
24 | return request({
25 | url: `/adminApi/banner/${data.id}`,
26 | method: 'delete',
27 | data,
28 | })
29 | }
30 | // 启用禁用
31 | export function banPick(data) {
32 | return request({
33 | url: `/adminApi/banner/${data.id}/ban`,
34 | method: 'put',
35 | data,
36 | })
37 | }
38 | export function edit(data) {
39 | return request({
40 | url: `/adminApi/banner/${data.id}`,
41 | method: 'put',
42 | data,
43 | })
44 | }
45 |
46 | export function bannerDetail(data) {
47 | return request({
48 | url: `/adminApi/banner/${data}`,
49 | method: 'get',
50 | data,
51 | })
52 | }
53 |
--------------------------------------------------------------------------------
/src/api/refundReasons.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-07-02 11:35:06
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-07-02 14:51:11
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 |
10 | // 分页获取退货原因列表
11 | export function getRefundReasonsList(data) {
12 | return request({
13 | url: '/adminApi/refundReasons',
14 | method: 'get',
15 | data,
16 | })
17 | }
18 | // 不分页获取退货原因列表
19 | export function getAllRefundReasonsList(data) {
20 | return request({
21 | url: '/adminApi/refundReasons/all',
22 | method: 'get',
23 | data,
24 | })
25 | }
26 | // 添加退货原因
27 | export function addRefundReasons(data) {
28 | return request({
29 | url: '/adminApi/refundReasons',
30 | method: 'post',
31 | data,
32 | })
33 | }
34 | // 修改退货原因
35 | export function editRefundReasons(data) {
36 | return request({
37 | url: `/adminApi/refundReasons/${data.id}`,
38 | method: 'put',
39 | data,
40 | })
41 | }
42 | // 删除退货原因
43 | export function deleteRefundReasons(data) {
44 | return request({
45 | url: `/adminApi/refundReasons/${data.id}`,
46 | method: 'delete',
47 | data,
48 | })
49 | }
50 |
--------------------------------------------------------------------------------
/src/icons/svg/setting.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/core/directives/module/clipboard.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-07-31 15:00:39
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-07-31 15:00:51
6 | * @Descripttion: 复制到粘贴板
7 | */
8 |
9 | import Clipboard from 'clipboard'
10 | export default {
11 | bind: (el, binding) => {
12 | const clipboard = new Clipboard(el, {
13 | text: () => binding.value.value,
14 | })
15 | el.__success_callback__ = binding.value.success
16 | el.__error_callback__ = binding.value.error
17 | clipboard.on('success', e => {
18 | const callback = el.__success_callback__
19 | callback && callback(e)
20 | })
21 | clipboard.on('error', e => {
22 | const callback = el.__error_callback__
23 | callback && callback(e)
24 | })
25 | el.__clipboard__ = clipboard
26 | },
27 | update: (el, binding) => {
28 | el.__clipboard__.text = () => binding.value.value
29 | el.__success_callback__ = binding.value.success
30 | el.__error_callback__ = binding.value.error
31 | },
32 | unbind: (el, binding) => {
33 | delete el.__success_callback__
34 | delete el.__error_callback__
35 | el.__clipboard__.destroy()
36 | delete el.__clipboard__
37 | },
38 | }
39 |
--------------------------------------------------------------------------------
/src/layout/components/AppMain.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
28 |
29 |
46 |
47 |
55 |
--------------------------------------------------------------------------------
/src/api/recommends.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-24 15:07:14
4 | * @LastEditTime: 2020-06-24 15:12:53
5 | * @LastEditors: Please set LastEditors
6 | * @Description: 商品推荐
7 | */
8 |
9 | import request from '@/utils/request'
10 |
11 | // 获取分页查询商品推荐信息
12 | export function getGoodsRecommends(data) {
13 | return request({
14 | url: '/adminApi/recommends',
15 | method: 'get',
16 | data,
17 | })
18 | }
19 |
20 | // 保存商品推荐信息
21 | export function createGoodsRecommends(data) {
22 | return request({
23 | url: '/adminApi/recommends',
24 | method: 'post',
25 | data,
26 | })
27 | }
28 |
29 | // 删除推荐信息
30 | export function delGoodsRecommends(data) {
31 | return request({
32 | url: `/adminApi/recommends/${data.id}`,
33 | method: 'delete',
34 | data,
35 | })
36 | }
37 |
38 | // 启用/禁用热销商品
39 | export function banGoodsRecommends(data) {
40 | return request({
41 | url: `/adminApi/recommends/${data.id}/banOrEnable`,
42 | method: 'put',
43 | data,
44 | })
45 | }
46 |
47 | // 修改推荐信息顺序
48 | export function sortGoodsRecommends(data) {
49 | return request({
50 | url: `/adminApi/recommends/${data.id}/sort`,
51 | method: 'put',
52 | data,
53 | })
54 | }
55 |
--------------------------------------------------------------------------------
/src/icons/svg/eye-open.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/admin.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-11 11:46:00
4 | * @LastEditTime: 2020-06-16 10:27:20
5 | * @LastEditors: Please set LastEditors
6 | * @Description:系统用户管理
7 | */
8 |
9 | import request from '@/utils/request';
10 |
11 | // 获取系统用户
12 | export function getAllOperator(data) {
13 | return request({
14 | url: '/adminApi/admins',
15 | method: 'get',
16 | data,
17 | });
18 | }
19 |
20 | // 创建系统用户
21 | export function createOperator(data) {
22 | return request({
23 | url: '/adminApi/admins',
24 | method: 'post',
25 | data,
26 | });
27 | }
28 |
29 | // 编辑系统用户
30 | export function editOperator(data) {
31 | return request({
32 | url: `/adminApi/admins/${data.id}`,
33 | method: 'put',
34 | data,
35 | });
36 | }
37 |
38 | // 删除系统用户
39 | export function delOperator(data) {
40 | return request({
41 | url: `/adminApi/admins/${data.id}`,
42 | method: 'delete',
43 | data,
44 | });
45 | }
46 |
47 | // 禁用/启用系统用户
48 | export function banOperator(data) {
49 | return request({
50 | url: `/adminApi/admins/${data.id}/ban`,
51 | method: 'put',
52 | data,
53 | });
54 | }
55 |
56 | // 修改密码
57 | export function changePassword(data) {
58 | return request({
59 | url: `/adminApi/admins/${data.id}/passWord`,
60 | method: 'put',
61 | data,
62 | });
63 | }
64 |
--------------------------------------------------------------------------------
/src/icons/svg/activity.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
60 |
--------------------------------------------------------------------------------
/src/components/Hamburger/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
32 |
33 |
45 |
--------------------------------------------------------------------------------
/src/icons/svg/order.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/order.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-06-18 12:38:14
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-07-04 17:43:53
6 | * @Descripttion:
7 | */
8 |
9 | import request from '@/utils/request'
10 |
11 | // 获取订单列表
12 | export function getOrderList(data) {
13 | return request({
14 | url: '/adminApi/order',
15 | method: 'get',
16 | data,
17 | })
18 | }
19 | // 订单详情
20 | export function orderDetail(data) {
21 | return request({
22 | url: `/adminApi/order/${data.id}`,
23 | method: 'get',
24 | data,
25 | })
26 | }
27 | // 关闭订单
28 | export function orderClose(data) {
29 | return request({
30 | url: `/adminApi/order/${data.id}/cancel`,
31 | method: 'put',
32 | data,
33 | })
34 | }
35 | // 发货
36 | export function sendExpress(data) {
37 | return request({
38 | url: `/adminApi/order/${data.id}/sendExpress`,
39 | method: 'put',
40 | data,
41 | })
42 | }
43 | // 申请退货
44 | export function refund(data) {
45 | return request({
46 | url: `/adminApi/order/${data.id}/refund`,
47 | method: 'post',
48 | data,
49 | })
50 | }
51 | // 导出订单单
52 | export function exportUnShippedOrder(data) {
53 | return request({
54 | url: `/adminApi/order/export/unShipped`,
55 | method: 'get',
56 | data,
57 | })
58 | }
59 | // 导入发货单
60 | export function importShipped(data) {
61 | return request({
62 | url: `/adminApi/order/import/shipped`,
63 | method: 'post',
64 | data,
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/src/api/user.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-06-15 12:32:38
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-07-03 11:05:20
6 | * @Descripttion:
7 | */
8 |
9 | import request from '@/utils/request'
10 |
11 | // 登录
12 | export function login(data) {
13 | return request({
14 | url: '/adminApi/login',
15 | method: 'get',
16 | data,
17 | })
18 | }
19 | // 获取用户列表
20 | export function getUserList(data) {
21 | return request({
22 | url: '/adminApi/users',
23 | method: 'get',
24 | data,
25 | })
26 | }
27 | // 获取用户信息
28 | export function getUserInfo(data) {
29 | return request({
30 | url: `/adminApi/users/${data.id}`,
31 | method: 'get',
32 | data,
33 | })
34 | }
35 | // 用户状态管理
36 | export function banState(data) {
37 | return request({
38 | url: `/adminApi/users/${data.userId}/banOrEnable`,
39 | method: 'put',
40 | data,
41 | })
42 | }
43 | // 获取用户收货地址列表
44 | export function getUserAddress(data) {
45 | return request({
46 | url: `/adminApi/users/${data.id}/addresses`,
47 | method: 'get',
48 | data,
49 | })
50 | }
51 | // 获取用户自购记录
52 | export function getUserOrder(data) {
53 | return request({
54 | url: `/adminApi/users/${data.id}/orders`,
55 | method: 'get',
56 | data,
57 | })
58 | }
59 | // 获取用户优惠券信息
60 | export function getUserCoupons(data) {
61 | return request({
62 | url: `/adminApi/users/${data.id}/coupons`,
63 | method: 'get',
64 | data,
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/src/icons/svg/ordersvg.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layout/mixin/ResizeHandler.js:
--------------------------------------------------------------------------------
1 | import store from '@/store'
2 |
3 | const { body } = document
4 | const WIDTH = 992 // refer to Bootstrap's responsive design
5 |
6 | export default {
7 | watch: {
8 | $route(route) {
9 | if (this.device === 'mobile' && this.sidebar.opened) {
10 | store.dispatch('app/closeSideBar', { withoutAnimation: false })
11 | }
12 | }
13 | },
14 | beforeMount() {
15 | window.addEventListener('resize', this.$_resizeHandler)
16 | },
17 | beforeDestroy() {
18 | window.removeEventListener('resize', this.$_resizeHandler)
19 | },
20 | mounted() {
21 | const isMobile = this.$_isMobile()
22 | if (isMobile) {
23 | store.dispatch('app/toggleDevice', 'mobile')
24 | store.dispatch('app/closeSideBar', { withoutAnimation: true })
25 | }
26 | },
27 | methods: {
28 | // use $_ for mixins properties
29 | // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
30 | $_isMobile() {
31 | const rect = body.getBoundingClientRect()
32 | return rect.width - 1 < WIDTH
33 | },
34 | $_resizeHandler() {
35 | if (!document.hidden) {
36 | const isMobile = this.$_isMobile()
37 | store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
38 |
39 | if (isMobile) {
40 | store.dispatch('app/closeSideBar', { withoutAnimation: true })
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/api/comment.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: tianhao
3 | * @Date: 2020-06-22 16:33:34
4 | * @LastEditors: 刘家辰
5 | * @LastEditTime: 2020-07-04 12:28:06
6 | * @Descripttion: 评论
7 | */
8 | import request from '@/utils/request'
9 | export function getCommentList(data) {
10 | return request({
11 | url: '/adminApi/comment',
12 | method: 'get',
13 | data,
14 | })
15 | }
16 | export function getDetail(data) {
17 | return request({
18 | url: `/adminApi/comment/${data.id}`,
19 | method: 'get',
20 | data,
21 | })
22 | }
23 | export function agree(data) {
24 | return request({
25 | url: `/adminApi/comment/${data.id}/agree`,
26 | method: 'put',
27 | data,
28 | })
29 | }
30 | export function ban(data) {
31 | return request({
32 | url: `/adminApi/comment/${data.id}/ban`,
33 | method: 'put',
34 | data,
35 | })
36 | }
37 | export function noAgree(data) {
38 | return request({
39 | url: `/adminApi/comment/${data.id}/notAgree`,
40 | method: 'put',
41 | data,
42 | })
43 | }
44 | export function cancelBan(data) {
45 | return request({
46 | url: `/adminApi/comment/${data.id}/cancelBan`,
47 | method: 'put',
48 | data,
49 | })
50 | }
51 | // 添加假评论
52 | export function createComment(data) {
53 | return request({
54 | url: `/adminApi/comment`,
55 | method: 'post',
56 | data,
57 | })
58 | }
59 | // 删除加评论
60 | export function delComment(data) {
61 | return request({
62 | url: `/adminApi/comment/${data.id}`,
63 | method: 'delete',
64 | data,
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/src/views/dashboard/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
name: {{ name }}
11 |
12 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
49 |
50 |
61 |
--------------------------------------------------------------------------------
/src/icons/svg/banner.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/password.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/brand.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/catSpec.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-06-16 14:48:16
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-06-17 12:13:20
6 | * @Descripttion:
7 | */
8 |
9 | import request from '@/utils/request'
10 |
11 | // 获取类目管理下面的规格列表
12 | export function getCatSpec(data) {
13 | return request({
14 | url: '/adminApi/catSpec',
15 | method: 'get',
16 | data,
17 | })
18 | }
19 | // 添加类目管理下面的参数
20 | export function createCatSpec(data) {
21 | return request({
22 | url: '/adminApi/catSpec',
23 | method: 'post',
24 | data,
25 | })
26 | }
27 | // 删除类目管理下面的参数
28 | export function deleteCatSpec(data) {
29 | return request({
30 | url: `/adminApi/catSpec/${data.id}`,
31 | method: 'delete',
32 | data,
33 | })
34 | }
35 | // 编辑类目管理下面的参数
36 | export function editCatSpec(data) {
37 | return request({
38 | url: `/adminApi/catSpec/${data.id}`,
39 | method: 'put',
40 | data,
41 | })
42 | }
43 | // 添加类目管理下面的属性
44 | export function createCatSkus(data) {
45 | return request({
46 | url: '/adminApi/catSkus',
47 | method: 'post',
48 | data,
49 | })
50 | }
51 | // 编辑类目管理下面的属性
52 | export function editCatSkus(data) {
53 | return request({
54 | url: `/adminApi/catSkus/${data.id}`,
55 | method: 'put',
56 | data,
57 | })
58 | }
59 | // 删除类目管理下面的属性
60 | export function deleteCatSkus(data) {
61 | return request({
62 | url: `/adminApi/catSkus/${data.id}`,
63 | method: 'delete',
64 | data,
65 | })
66 | }
67 | // 获取类目管理下面的属性
68 | export function getCatSkus(data) {
69 | return request({
70 | url: `/adminApi/catSkus`,
71 | method: 'get',
72 | data,
73 | })
74 | }
75 |
--------------------------------------------------------------------------------
/src/components/SvgIcon/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
47 |
48 |
63 |
--------------------------------------------------------------------------------
/src/api/coupon.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-07-01 11:33:48
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-07-06 11:11:19
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 |
10 | // 新增优惠券
11 | export function addCoupon(data) {
12 | return request({
13 | url: '/adminApi/coupon',
14 | method: 'post',
15 | data,
16 | })
17 | }
18 | // 获取优惠券列表
19 | export function getCouponList(data) {
20 | return request({
21 | url: '/adminApi/coupon',
22 | method: 'get',
23 | data,
24 | })
25 | }
26 | // 获取优惠券详情
27 | export function getCouponInfo(data) {
28 | return request({
29 | url: `/adminApi/coupon/${data.id}`,
30 | method: 'get',
31 | data,
32 | })
33 | }
34 | // 编辑优惠券
35 | export function editCoupon(data) {
36 | return request({
37 | url: `/adminApi/coupon/${data.id}`,
38 | method: 'put',
39 | data,
40 | })
41 | }
42 | // 删除优惠券
43 | export function deleteCoupon(data) {
44 | return request({
45 | url: `/adminApi/coupon/${data.id}`,
46 | method: 'delete',
47 | data,
48 | })
49 | }
50 | // 获取优惠券统计信息
51 | export function getStatisticsInfo(data) {
52 | return request({
53 | url: `/adminApi/coupon/${data.id}/statistics`,
54 | method: 'get',
55 | data,
56 | })
57 | }
58 | // 获取持有详情
59 | export function getHoldDetailInfo(data) {
60 | return request({
61 | url: `/adminApi/coupon/${data.id}/holdDetail`,
62 | method: 'get',
63 | data,
64 | })
65 | }
66 | // 优惠券启用/禁用管理
67 | export function banState(data) {
68 | return request({
69 | url: `/adminApi/coupon/${data.id}/banOrEnable`,
70 | method: 'put',
71 | data,
72 | })
73 | }
74 |
--------------------------------------------------------------------------------
/src/icons/svg/operation.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/goods.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-17 12:07:33
4 | * @LastEditTime: 2020-06-20 18:27:51
5 | * @LastEditors: Please set LastEditors
6 | * @Description: 商品列表
7 | */
8 | import request from '@/utils/request'
9 |
10 | // 获取商品
11 | export function getGoods(data) {
12 | return request({
13 | url: '/adminApi/item',
14 | method: 'get',
15 | data,
16 | })
17 | }
18 | // 创建商品
19 | export function createGoods(data) {
20 | return request({
21 | url: '/adminApi/item',
22 | method: 'post',
23 | data,
24 | })
25 | }
26 | // 编辑商品
27 | export function editGoods(data) {
28 | return request({
29 | url: `/adminApi/item/${data.id}`,
30 | method: 'put',
31 | data,
32 | })
33 | }
34 | // 删除商品
35 | export function deleteGoods(data) {
36 | return request({
37 | url: `/adminApi/item/${data.id}`,
38 | method: 'delete',
39 | data,
40 | })
41 | }
42 |
43 | // 上新商品
44 | export function putnewGoods(data) {
45 | return request({
46 | url: `/adminApi/item/${data.id}/newFlagOrNot`,
47 | method: 'put',
48 | data,
49 | })
50 | }
51 | // 上架商品
52 | export function putawayGoods(data) {
53 | return request({
54 | url: `/adminApi/item/${data.id}/putOnOrDown`,
55 | method: 'put',
56 | data,
57 | })
58 | }
59 | // 获取商品详情
60 | export function getGoodsDetail(data) {
61 | return request({
62 | url: `/adminApi/item/${data.id}`,
63 | method: 'get',
64 | data,
65 | })
66 | }
67 | // 根据商品ID查询sku列表
68 | export function getGoodsInfo(data) {
69 | return request({
70 | url: `/adminApi/item/${data.id}/sku`,
71 | method: 'get',
72 | data,
73 | })
74 | }
75 | // 商品是否可编辑
76 | export function isEdit(data) {
77 | return request({
78 | url: `/adminApi/item/${data.id}/editFlag`,
79 | method: 'get',
80 | data,
81 | })
82 | }
83 |
--------------------------------------------------------------------------------
/src/icons/svg/tree.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/api/activity.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: peng.wan
3 | * @Date: 2020-06-18 20:01:05
4 | * @LastEditors: peng.wan
5 | * @LastEditTime: 2020-07-27 11:56:00
6 | * @Descripttion:
7 | */
8 | import request from '@/utils/request'
9 |
10 | // 获取活动列表
11 | export function getActivityList(data) {
12 | return request({
13 | url: '/adminApi/limitPromotions',
14 | method: 'get',
15 | data,
16 | })
17 | }
18 | // 仅获取活动信息
19 | export function getActivityInfo(data) {
20 | return request({
21 | url: `/adminApi/limitPromotions/${data.id}`,
22 | method: 'get',
23 | data,
24 | })
25 | }
26 | // 获取活动全部信息(包含商品及SKU)
27 | export function getAllActivityInfo(data) {
28 | return request({
29 | url: `/adminApi/limitPromotions/${data.id}/all`,
30 | method: 'get',
31 | data,
32 | })
33 | }
34 | // 添加活动
35 | export function addActivity(data) {
36 | return request({
37 | url: `/adminApi/limitPromotions`,
38 | method: 'post',
39 | data,
40 | })
41 | }
42 | // 修改活动信息
43 | export function editActivityInfo(data) {
44 | return request({
45 | url: `/adminApi/limitPromotions/${data.id}`,
46 | method: 'put',
47 | data,
48 | })
49 | }
50 | // 活动启用/禁用管理
51 | export function banState(data) {
52 | return request({
53 | url: `/adminApi/limitPromotions/${data.id}/banOrEnable`,
54 | method: 'put',
55 | data,
56 | })
57 | }
58 | // 活动销售数据统计
59 | export function getStatistics(data) {
60 | return request({
61 | url: `/adminApi/limitPromotions/${data.id}/statistics`,
62 | method: 'get',
63 | data,
64 | })
65 | }
66 | // 活动关联订单明细
67 | export function getOrders(data) {
68 | return request({
69 | url: `/adminApi/limitPromotions/${data.id}/orders`,
70 | method: 'get',
71 | data,
72 | })
73 | }
74 | // 活动商品明细
75 | export function getItems(data) {
76 | return request({
77 | url: `/adminApi/limitPromotions/${data.id}/items`,
78 | method: 'get',
79 | data,
80 | })
81 | }
82 |
--------------------------------------------------------------------------------
/src/icons/svg/afterSales.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layout/components/Sidebar/index.vue:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
67 |
--------------------------------------------------------------------------------
/src/utils/scroll-to.js:
--------------------------------------------------------------------------------
1 | Math.easeInOutQuad = function(t, b, c, d) {
2 | t /= d / 2
3 | if (t < 1) {
4 | return c / 2 * t * t + b
5 | }
6 | t--
7 | return -c / 2 * (t * (t - 2) - 1) + b
8 | }
9 |
10 | // requestAnimationFrame for Smart Animating http://goo.gl/sx5sts
11 | var requestAnimFrame = (function() {
12 | return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60) }
13 | })()
14 |
15 | /**
16 | * Because it's so fucking difficult to detect the scrolling element, just move them all
17 | * @param {number} amount
18 | */
19 | function move(amount) {
20 | document.documentElement.scrollTop = amount
21 | document.body.parentNode.scrollTop = amount
22 | document.body.scrollTop = amount
23 | }
24 |
25 | function position() {
26 | return document.documentElement.scrollTop || document.body.parentNode.scrollTop || document.body.scrollTop
27 | }
28 |
29 | /**
30 | * @param {number} to
31 | * @param {number} duration
32 | * @param {Function} callback
33 | */
34 | export function scrollTo(to, duration, callback) {
35 | const start = position()
36 | const change = to - start
37 | const increment = 20
38 | let currentTime = 0
39 | duration = (typeof (duration) === 'undefined') ? 500 : duration
40 | var animateScroll = function() {
41 | // increment the time
42 | currentTime += increment
43 | // find the value with the quadratic in-out easing function
44 | var val = Math.easeInOutQuad(currentTime, start, change, duration)
45 | // move the document.body
46 | move(val)
47 | // do the animation unless its over
48 | if (currentTime < duration) {
49 | requestAnimFrame(animateScroll)
50 | } else {
51 | if (callback && typeof (callback) === 'function') {
52 | // the animation is done so lets callback
53 | callback()
54 | }
55 | }
56 | }
57 | animateScroll()
58 | }
59 |
--------------------------------------------------------------------------------
/src/icons/svg/cat.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-admin-template",
3 | "version": "4.3.0",
4 | "description": "A vue admin",
5 | "author": "Pan",
6 | "license": "MIT",
7 | "scripts": {
8 | "dev": "vue-cli-service serve",
9 | "prod": "vue-cli-service build",
10 | "stage": "vue-cli-service build --mode staging",
11 | "preview": "node build/index.js --preview",
12 | "lint": "eslint --ext .js,.vue src",
13 | "test:unit": "jest --clearCache && vue-cli-service test:unit",
14 | "test:ci": "npm run lint && npm run test:unit",
15 | "svgo": "svgo -f src/icons/svg --config=src/icons/svgo.yml"
16 | },
17 | "dependencies": {
18 | "axios": "0.18.1",
19 | "element-ui": "2.13.0",
20 | "js-cookie": "2.2.0",
21 | "moment": "^2.26.0",
22 | "normalize.css": "7.0.0",
23 | "nprogress": "0.2.0",
24 | "path-to-regexp": "2.4.0",
25 | "vue": "2.6.10",
26 | "vue-cropper": "^0.5.4",
27 | "vue-router": "3.0.6",
28 | "vuedraggable": "^2.24.1",
29 | "vuex": "3.1.0",
30 | "vuex-persistedstate": "^3.0.1"
31 | },
32 | "devDependencies": {
33 | "@babel/core": "7.0.0",
34 | "@babel/register": "7.0.0",
35 | "@vue/cli-plugin-babel": "3.6.0",
36 | "@vue/cli-plugin-eslint": "^3.9.1",
37 | "@vue/cli-plugin-unit-jest": "3.6.3",
38 | "@vue/cli-service": "3.6.0",
39 | "@vue/test-utils": "1.0.0-beta.29",
40 | "autoprefixer": "^9.5.1",
41 | "babel-core": "7.0.0-bridge.0",
42 | "babel-eslint": "10.0.1",
43 | "babel-jest": "23.6.0",
44 | "chalk": "2.4.2",
45 | "connect": "3.6.6",
46 | "eslint": "5.15.3",
47 | "eslint-plugin-vue": "5.2.2",
48 | "html-webpack-plugin": "3.2.0",
49 | "mockjs": "1.0.1-beta3",
50 | "runjs": "^4.3.2",
51 | "sass": "^1.26.8",
52 | "sass-loader": "^7.1.0",
53 | "script-ext-html-webpack-plugin": "2.1.3",
54 | "serve-static": "^1.13.2",
55 | "svg-sprite-loader": "4.1.3",
56 | "svgo": "1.2.2",
57 | "vue-template-compiler": "2.6.10"
58 | },
59 | "engines": {
60 | "node": ">=8.9",
61 | "npm": ">= 3.0.0"
62 | },
63 | "browserslist": [
64 | "> 1%",
65 | "last 2 versions"
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/src/layout/components/Sidebar/Logo.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
21 |
22 |
23 |
40 |
41 |
90 |
--------------------------------------------------------------------------------
/src/icons/svg/recommends.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/styles/index.scss:
--------------------------------------------------------------------------------
1 | @import './variables.scss';
2 | @import './mixin.scss';
3 | @import './transition.scss';
4 | @import './element-ui.scss';
5 | @import './sidebar.scss';
6 |
7 | body {
8 | height: 100%;
9 | -moz-osx-font-smoothing: grayscale;
10 | -webkit-font-smoothing: antialiased;
11 | text-rendering: optimizeLegibility;
12 | font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial,
13 | sans-serif;
14 | }
15 |
16 | label {
17 | // font-weight: 700;
18 | }
19 |
20 | html {
21 | height: 100%;
22 | box-sizing: border-box;
23 | }
24 |
25 | #app {
26 | height: 100%;
27 | }
28 |
29 | *,
30 | *:before,
31 | *:after {
32 | box-sizing: inherit;
33 | }
34 |
35 | a:focus,
36 | a:active {
37 | outline: none;
38 | }
39 |
40 | a,
41 | a:focus,
42 | a:hover {
43 | cursor: pointer;
44 | color: inherit;
45 | text-decoration: none;
46 | }
47 |
48 | div:focus {
49 | outline: none;
50 | }
51 |
52 | .clearfix {
53 | &:after {
54 | visibility: hidden;
55 | display: block;
56 | font-size: 0;
57 | content: ' ';
58 | clear: both;
59 | height: 0;
60 | }
61 | }
62 |
63 | // main-container global css
64 | .app-container {
65 | padding: 20px;
66 | }
67 | .el-submenu__title i {
68 | font-weight: bold !important;
69 | font-size: 14px !important;
70 | }
71 | .el-form-item {
72 | // width: 400px;
73 | }
74 | .el-input__inner {
75 | // width: auto;
76 | }
77 | .el-textarea__inner {
78 | width: auto;
79 | }
80 | .el-textarea .el-textarea__inner {
81 | width: 100%;
82 | }
83 | .el-button--primary {
84 | background-color: #1890ff;
85 | border-color: #1890ff;
86 | }
87 | .dialogStyle {
88 | min-width: 700px;
89 | }
90 | .activityDetail .el-card__body {
91 | height: 200px;
92 | box-sizing: content-box;
93 | }
94 | .flex-row .el-form-item__label,
95 | .userLimitAmount .el-form-item__label {
96 | text-align: left;
97 | }
98 | .refundSause_dynamic .el-form-item__error {
99 | right: 0;
100 | }
101 | .newUploadForm .el-form-item__content {
102 | display: flex;
103 | align-items: center;
104 | }
105 | .el-checkbox__input.is-checked + .el-checkbox__label {
106 | color: unset;
107 | }
108 | .el-tooltip__popper {
109 | max-width: 500px;
110 | }
111 |
--------------------------------------------------------------------------------
/src/icons/svg/dashboard.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/form.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/icons/svg/coupon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/layout/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
19 |
20 |
21 |
59 |
60 |
101 |
--------------------------------------------------------------------------------
/src/store/modules/app.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-06-20 18:55:32
4 | * @LastEditors: xin.chen
5 | * @LastEditTime: 2020-09-21 16:19:10
6 | * @Descripttion:
7 | */
8 |
9 | import Cookies from 'js-cookie';
10 | import { login, logout, getInfo } from '@/api/user';
11 | import { getToken } from '@/api/upload';
12 |
13 | const state = {
14 | sidebar: {
15 | opened: Cookies.get('sidebarStatus') ? !!+Cookies.get('sidebarStatus') : true,
16 | withoutAnimation: false,
17 | },
18 |
19 | device: 'desktop',
20 | upLoadToken: null,
21 |
22 | // 自定义表单类型
23 | field: [
24 | { key: '', name: '单行文本', type: 'text', placeholder: '', disabled: false },
25 | { key: '', name: '选择器', type: 'select', placeholder: '' },
26 | { key: '', name: '级联选择器', type: 'cascader', placeholder: '', disabled: false },
27 | {
28 | key: '',
29 | name: '日期时间选择器',
30 | type: 'datetimepicker',
31 | placeholder: '',
32 | disabled: false,
33 | },
34 | {
35 | key: '',
36 | name: '创建条目选择器',
37 | type: 'allowcreate',
38 | placeholder: '',
39 | disabled: false,
40 | },
41 | ],
42 | };
43 |
44 | const mutations = {
45 | TOGGLE_SIDEBAR: state => {
46 | state.sidebar.opened = !state.sidebar.opened;
47 | state.sidebar.withoutAnimation = false;
48 | if (state.sidebar.opened) {
49 | Cookies.set('sidebarStatus', 1);
50 | } else {
51 | Cookies.set('sidebarStatus', 0);
52 | }
53 | },
54 | CLOSE_SIDEBAR: (state, withoutAnimation) => {
55 | Cookies.set('sidebarStatus', 0);
56 | state.sidebar.opened = false;
57 | state.sidebar.withoutAnimation = withoutAnimation;
58 | },
59 | TOGGLE_DEVICE: (state, device) => {
60 | state.device = device;
61 | },
62 | SET_UPLOAD_TOKEN: (state, token) => {
63 | console.log('set token', token);
64 | state.upLoadToken = token;
65 | },
66 | // 编辑自定义表单类型
67 | SET_UPLOAD_FIELD: (state, field) => {
68 | state.field = field;
69 | },
70 | };
71 |
72 | const actions = {
73 | toggleSideBar({ commit }) {
74 | commit('TOGGLE_SIDEBAR');
75 | },
76 | closeSideBar({ commit }, { withoutAnimation }) {
77 | commit('CLOSE_SIDEBAR', withoutAnimation);
78 | },
79 | toggleDevice({ commit }, device) {
80 | commit('TOGGLE_DEVICE', device);
81 | },
82 | setUpLoadToken({ commit }, token) {
83 | commit('SET_UPLOAD_TOKEN', token);
84 | },
85 | // 编辑自定义表单类型
86 | setuploadfield({ commit }, field) {
87 | commit('SET_UPLOAD_FIELD', field);
88 | },
89 | };
90 |
91 | export default {
92 | namespaced: true,
93 | state,
94 | mutations,
95 | actions,
96 | };
97 |
--------------------------------------------------------------------------------
/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import { login, logout, getInfo } from '@/api/user'
2 | import { getToken, setToken, removeToken } from '@/utils/auth'
3 | import { resetRouter } from '@/router'
4 |
5 | const getDefaultState = () => {
6 | return {
7 | token: '',
8 | name: '',
9 | avatar: '',
10 | }
11 | }
12 |
13 | const state = getDefaultState()
14 |
15 | const mutations = {
16 | RESET_STATE: state => {
17 | Object.assign(state, getDefaultState())
18 | },
19 | SET_TOKEN: (state, token) => {
20 | state.token = token
21 | },
22 | SET_NAME: (state, name) => {
23 | state.name = name
24 | },
25 | SET_AVATAR: (state, avatar) => {
26 | state.avatar = avatar
27 | },
28 | }
29 |
30 | const actions = {
31 | // user login
32 | login({ commit }, userInfo) {
33 | const { phone, passWord } = userInfo
34 | return new Promise((resolve, reject) => {
35 | login({ phone, passWord })
36 | .then(response => {
37 | const { data } = response
38 | commit('SET_TOKEN', data.token)
39 | commit('SET_NAME', data.admin.name)
40 | commit('SET_AVATAR', data.admin.avatar)
41 | setToken(data.token)
42 | sessionStorage.setItem('token', data.token)
43 | resolve()
44 | })
45 | .catch(error => {
46 | reject(error)
47 | })
48 | })
49 | },
50 |
51 | // get user info
52 | getInfo({ commit, state }) {
53 | return new Promise((resolve, reject) => {
54 | getInfo(state.token)
55 | .then(response => {
56 | const { data } = response
57 |
58 | if (!data) {
59 | reject('Verification failed, please Login again.')
60 | }
61 |
62 | const { name, avatar } = data.admin
63 |
64 | commit('SET_NAME', name)
65 | commit('SET_AVATAR', avatar)
66 | resolve(data)
67 | })
68 | .catch(error => {
69 | reject(error)
70 | })
71 | })
72 | },
73 |
74 | // user logout
75 | logout({ commit, state }) {
76 | return new Promise((resolve, reject) => {
77 | // logout(state.token)
78 | // .then(() => {
79 | removeToken() // must remove token first
80 | resetRouter()
81 | commit('RESET_STATE')
82 | resolve()
83 | // })
84 | // .catch(error => {
85 | // reject(error)
86 | // })
87 | })
88 | },
89 |
90 | // remove token
91 | resetToken({ commit }) {
92 | return new Promise(resolve => {
93 | removeToken() // must remove token first
94 | commit('RESET_STATE')
95 | resolve()
96 | })
97 | },
98 | }
99 |
100 | export default {
101 | namespaced: true,
102 | state,
103 | mutations,
104 | actions,
105 | }
106 |
--------------------------------------------------------------------------------
/src/components/Breadcrumb/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 | {{ item.meta.title }}
16 | {{ item.meta.title }}
17 |
18 |
19 |
20 |
21 |
22 |
81 |
82 |
95 |
--------------------------------------------------------------------------------
/src/icons/svg/supperlier.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/password/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
22 |
29 |
36 |
37 |
38 |
39 |
40 |
116 |
117 |
122 |
--------------------------------------------------------------------------------
/src/utils/guid.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: wei.chen
3 | * @Date: 2020-06-15 15:53:48
4 | * @LastEditors: wei.chen
5 | * @LastEditTime: 2020-06-15 16:08:27
6 | * @Descripttion: 全局生成一个唯一的id
7 | */
8 | function GUID() {
9 | this.date = new Date();
10 |
11 | /* 判断是否初始化过,如果初始化过以下代码,则以下代码将不再执行,实际中只执行一次 */
12 | if (typeof this.newGUID != 'function') {
13 | /* 生成GUID码 */
14 | GUID.prototype.newGUID = function() {
15 | this.date = new Date();
16 | var guidStr = '';
17 | let sexadecimalDate = this.hexadecimal(this.getGUIDDate(), 16);
18 | let sexadecimalTime = this.hexadecimal(this.getGUIDTime(), 16);
19 | for (var i = 0; i < 9; i++) {
20 | guidStr += Math.floor(Math.random() * 16).toString(16);
21 | }
22 | guidStr += sexadecimalDate;
23 | guidStr += sexadecimalTime;
24 | while (guidStr.length < 32) {
25 | guidStr += Math.floor(Math.random() * 16).toString(16);
26 | }
27 | return `qn${guidStr}`;
28 | };
29 | /*
30 | * 功能:获取当前日期的GUID格式,即8位数的日期:19700101
31 | * 返回值:返回GUID日期格式的字条串
32 | */
33 | GUID.prototype.getGUIDDate = function() {
34 | return (
35 | this.date.getFullYear() +
36 | this.addZero(this.date.getMonth() + 1) +
37 | this.addZero(this.date.getDay())
38 | );
39 | };
40 |
41 | /*
42 | * 功能:获取当前时间的GUID格式,即8位数的时间,包括毫秒,毫秒为2位数:12300933
43 | * 返回值:返回GUID日期格式的字条串
44 | */
45 | GUID.prototype.getGUIDTime = function() {
46 | return (
47 | this.addZero(this.date.getHours()) +
48 | this.addZero(this.date.getMinutes()) +
49 | this.addZero(this.date.getSeconds()) +
50 | this.addZero(parseInt(this.date.getMilliseconds() / 10))
51 | );
52 | };
53 |
54 | /*
55 | * 功能: 为一位数的正整数前面添加0,如果是可以转成非NaN数字的字符串也可以实现
56 | * 参数: 参数表示准备再前面添加0的数字或可以转换成数字的字符串
57 | * 返回值: 如果符合条件,返回添加0后的字条串类型,否则返回自身的字符串
58 | */
59 | GUID.prototype.addZero = function(num) {
60 | if (Number(num).toString() != 'NaN' && num >= 0 && num < 10) {
61 | return '0' + Math.floor(num);
62 | } else {
63 | return num.toString();
64 | }
65 | };
66 |
67 | /*
68 | * 功能:将y进制的数值,转换为x进制的数值
69 | * 参数:第1个参数表示欲转换的数值;第2个参数表示欲转换的进制;第3个参数可选,表示当前的进制数,如不写则为10
70 | * 返回值:返回转换后的字符串
71 | */
72 | GUID.prototype.hexadecimal = function(num, x, y) {
73 | if (y != undefined) {
74 | return parseInt(num.toString(), y).toString(x);
75 | } else {
76 | return parseInt(num.toString()).toString(x);
77 | }
78 | };
79 | /*
80 | * 功能:格式化32位的字符串为GUID模式的字符串
81 | * 参数:第1个参数表示32位的字符串
82 | * 返回值:标准GUID格式的字符串
83 | */
84 | GUID.prototype.formatGUID = function(guidStr) {
85 | var str1 = guidStr.slice(0, 8) + '-',
86 | str2 = guidStr.slice(8, 12) + '-',
87 | str3 = guidStr.slice(12, 16) + '-',
88 | str4 = guidStr.slice(16, 20) + '-',
89 | str5 = guidStr.slice(20);
90 | return str1 + str2 + str3 + str4 + str5;
91 | };
92 | }
93 | }
94 |
95 | export { GUID };
96 |
--------------------------------------------------------------------------------
/src/utils/request.js:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 刘家辰
3 | * @Date: 2020-06-11 11:46:00
4 | * @LastEditTime: 2020-07-31 14:41:06
5 | * @LastEditors: wei.chen
6 | * @Description:
7 | */
8 |
9 | import axios from 'axios'
10 | import { MessageBox, Message } from 'element-ui'
11 | import store from '@/store'
12 | import { getToken } from '@/utils/auth'
13 |
14 | // create an axios instance
15 | const service = axios.create({
16 | baseURL: process.env.VUE_APP_BASE_API, // url = base url + request url
17 | // withCredentials: true, // send cookies when cross-domain requests
18 | timeout: 5000, // request timeout
19 | })
20 |
21 | // request interceptor
22 | service.interceptors.request.use(
23 | config => {
24 | // do something before request is sent
25 |
26 | if (store.getters.token) {
27 | // let each request carry token
28 | // ['X-Token'] is a custom headers key
29 | // please modify it according to the actual situation
30 | config.headers['Authorization'] = getToken()
31 | }
32 | return config
33 | },
34 | error => {
35 | // do something with request error
36 | console.log(error) // for debug
37 | return Promise.reject(error)
38 | }
39 | )
40 |
41 | // response interceptor
42 | service.interceptors.response.use(
43 | response => {
44 | const res = response.data
45 | // debugger
46 | // if the custom code is not 20000, it is judged as an error.
47 | if (res.code !== 200) {
48 | switch (res.code) {
49 | case 10005:
50 | res.msg = '值不可以重复!'
51 | break
52 | }
53 | Message({
54 | message: res.msg || 'Error',
55 | type: 'error',
56 | duration: 5 * 1000,
57 | })
58 | if (res.code === 402) {
59 | // token 过期了
60 | // 1、重置用户的状态
61 | // 2、用户跳转到 登录界面
62 | }
63 | // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
64 | if (res.code === 50008 || res.code === 50012 || res.code === 50014) {
65 | // to re-login
66 | MessageBox.confirm('登录状态失效,请重新登录', '确认退出', {
67 | confirmButtonText: '重新登录',
68 | cancelButtonText: 'Cancel',
69 | type: 'warning',
70 | }).then(() => {
71 | store.dispatch('user/resetToken').then(() => {
72 | location.reload()
73 | })
74 | })
75 | }
76 | return Promise.reject(new Error(res.msg || 'Error'))
77 | } else {
78 | return res
79 | }
80 | },
81 | error => {
82 | Message({
83 | message: '服务开了小差,请稍后重试',
84 | type: 'error',
85 | duration: 3 * 1000,
86 | })
87 | return Promise.reject(error)
88 | }
89 | )
90 | // get请求封装
91 | const request = val => {
92 | let { data, url, method } = val
93 | if (method == 'get' || method == 'GET') {
94 | return service({ url, method, params: data })
95 | } else {
96 | return service({ url, method, data })
97 | }
98 | }
99 |
100 | export default request
101 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 | const path = require('path')
3 | const defaultSettings = require('./src/settings.js')
4 |
5 | function resolve(dir) {
6 | return path.join(__dirname, dir)
7 | }
8 |
9 | const name = defaultSettings.title || 'Admin'
10 |
11 | // dev port
12 | const port = process.env.port || process.env.npm_config_port || 9000
13 |
14 | module.exports = {
15 | publicPath: '/',
16 | outputDir: 'dist',
17 | assetsDir: 'static',
18 | lintOnSave: false,
19 | productionSourceMap: false,
20 | devServer: {
21 | port: port,
22 | open: true,
23 | overlay: {
24 | warnings: false,
25 | errors: true,
26 | },
27 | // before: require('./mock/mock-server.js'),
28 | },
29 | configureWebpack: {
30 | name: name,
31 | resolve: {
32 | alias: {
33 | '@': resolve('src'),
34 | },
35 | },
36 | },
37 | chainWebpack(config) {
38 | config.plugins.delete('preload')
39 | config.plugins.delete('prefetch') // TODO: need test
40 |
41 | // set svg-sprite-loader
42 | config.module
43 | .rule('svg')
44 | .exclude.add(resolve('src/icons'))
45 | .end()
46 | config.module
47 | .rule('icons')
48 | .test(/\.svg$/)
49 | .include.add(resolve('src/icons'))
50 | .end()
51 | .use('svg-sprite-loader')
52 | .loader('svg-sprite-loader')
53 | .options({
54 | symbolId: 'icon-[name]',
55 | })
56 | .end()
57 |
58 | // set preserveWhitespace
59 | config.module
60 | .rule('vue')
61 | .use('vue-loader')
62 | .loader('vue-loader')
63 | .tap(options => {
64 | options.compilerOptions.preserveWhitespace = true
65 | return options
66 | })
67 | .end()
68 |
69 | config.when(process.env.NODE_ENV !== 'development', config => {
70 | config
71 | .plugin('ScriptExtHtmlWebpackPlugin')
72 | .after('html')
73 | .use('script-ext-html-webpack-plugin', [
74 | {
75 | // `runtime` must same as runtimeChunk name. default is `runtime`
76 | inline: /runtime\..*\.js$/,
77 | },
78 | ])
79 | .end()
80 | config.optimization.splitChunks({
81 | chunks: 'all',
82 | cacheGroups: {
83 | libs: {
84 | name: 'chunk-libs',
85 | test: /[\\/]node_modules[\\/]/,
86 | priority: 10,
87 | chunks: 'initial', // only package third parties that are initially dependent
88 | },
89 | elementUI: {
90 | name: 'chunk-elementUI', // split elementUI into a single package
91 | priority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or app
92 | test: /[\\/]node_modules[\\/]_?element-ui(.*)/, // in order to adapt to cnpm
93 | },
94 | commons: {
95 | name: 'chunk-commons',
96 | test: resolve('src/components'), // can customize your rules
97 | minChunks: 3, // minimum common number
98 | priority: 5,
99 | reuseExistingChunk: true,
100 | },
101 | },
102 | })
103 | config.optimization.runtimeChunk('single')
104 | })
105 | },
106 | }
107 |
--------------------------------------------------------------------------------
/src/layout/components/Sidebar/SidebarItem.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
23 |
24 |
25 |
26 |
27 |
96 |
--------------------------------------------------------------------------------
/src/views/worksheet/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
17 | {{ item.name }}
18 |
19 |
20 |
21 |
22 |
23 |
24 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
95 |
96 |
140 |
--------------------------------------------------------------------------------
/src/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue';
2 | import Router from 'vue-router';
3 | import { getToken } from '@/utils/auth';
4 | import { getPageTitle } from '@/utils/getPageTitle';
5 |
6 | Vue.use(Router);
7 |
8 | /* Layout */
9 | import Layout from '@/layout';
10 |
11 | /**
12 | * Note: sub-menu only appear when route children.length >= 1
13 | * Detail see: https://panjiachen.github.io/vue-element-admin-site/guide/essentials/router-and-nav.html
14 | *
15 | * hidden: true 当设置 true 的时候该路由不会在侧边栏出现 如401,login等页面,或者如一些编辑页面/edit/1
16 | * alwaysShow: true 当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
17 | *
18 | // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
19 | // 只有一个时,会将那个子路由当做根路由显示在侧边栏--如引导页面
20 | // 若你想不管路由下面的 children 声明的个数都显示你的根路由
21 | // 你可以设置 alwaysShow: true,这样它就会忽略之前定义的规则,一直显示根路由
22 | * redirect: noRedirect
23 | * name:'router-name' 设定路由的名字,一定要填写不然使用时会出现各种问题
24 | * meta : {
25 | title: 'title' 设置该路由在侧边栏和面包屑中展示的名字
26 | icon: 'svg-name' // 设置该路由的图标,支持 svg-class,也支持 el-icon-x element-ui 的 icon
27 | noCache 如果设置为true,则不会被 缓存(默认 false)
28 | breadcrumb: false 如果设置为false,则不会在breadcrumb面包屑中显示(默认 true)
29 | // 当路由设置了该属性,则会高亮相对应的侧边栏。
30 | // 这在某些场景非常有用,比如:一个文章的列表页路由为:/article/list
31 | // 点击文章进入文章详情页,这时候路由为/article/1,但你想在侧边栏高亮文章列表的路由,就可以进行如下设置
32 | activeMenu: '/example/list'
33 | }
34 | */
35 |
36 | /**
37 | * constantRoutes
38 | * a base page that does not have permission requirements
39 | * all roles can be accessed
40 | */
41 | export const constantRoutes = [
42 | {
43 | path: '/login',
44 | component: () => import('@/views/login/index'),
45 | hidden: true,
46 | },
47 |
48 | {
49 | path: '/404',
50 | component: () => import('@/views/404'),
51 | hidden: true,
52 | },
53 | {
54 | path: '/',
55 | component: Layout,
56 | redirect: '/dashboard',
57 | children: [
58 | {
59 | path: 'dashboard',
60 | name: '首页',
61 | component: () => import('@/views/dashboard/index'),
62 | meta: { title: '首页', icon: 'dashboard' },
63 | },
64 | ],
65 | },
66 | {
67 | path: '/worksheet',
68 | component: Layout,
69 | redirect: '/worksheet',
70 | children: [
71 | {
72 | path: 'worksheet',
73 | name: '自定义表单',
74 | component: () => import('@/views/worksheet/index'),
75 | meta: { title: '自定义表单', icon: 'dashboard' },
76 | },
77 | ],
78 | },
79 |
80 | // 404 page must be placed at the end !!!
81 | { path: '*', redirect: '/404', hidden: true },
82 | ];
83 | const createRouter = () =>
84 | new Router({
85 | // mode: 'history', // require service support
86 | scrollBehavior: () => ({ y: 0 }),
87 | routes: constantRoutes,
88 | });
89 |
90 | const router = createRouter();
91 |
92 | // router.beforeEach(async (to, from, next) => {
93 | // // start progress bar
94 | // // NProgress.start()
95 |
96 | // // set page title
97 | // document.title = getPageTitle(to.meta.title)
98 |
99 | // // determine whether the user has logged in
100 | // const hasToken = getToken()
101 | // // 判断是否有token
102 | // if (hasToken) {
103 | // // 如果有token,直接进入登录界面强制调整到首页
104 | // if (to.path === '/login') {
105 | // // if is logged in, redirect to the home page
106 | // next({ path: '/' })
107 | // }
108 | // } else {
109 | // /* 没有 token 的值 */
110 | // next(`/login?redirect=${to.path}`)
111 | // }
112 | // })
113 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
114 | export function resetRouter() {
115 | const newRouter = createRouter();
116 | router.matcher = newRouter.matcher; // reset router
117 | }
118 |
119 | export default router;
120 |
--------------------------------------------------------------------------------
/src/layout/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
29 |
30 |
31 |
32 |
56 |
57 |
145 |
--------------------------------------------------------------------------------
/src/styles/sidebar.scss:
--------------------------------------------------------------------------------
1 | #app {
2 | .main-container {
3 | min-height: 100%;
4 | transition: margin-left 0.28s;
5 | margin-left: $sideBarWidth;
6 | position: relative;
7 | background-color: #f0f2f5;
8 | }
9 |
10 | .sidebar-container {
11 | transition: width 0.28s;
12 | width: $sideBarWidth !important;
13 | background-color: $menuHover;
14 | height: 100%;
15 | position: fixed;
16 | font-size: 0px;
17 | top: 0;
18 | bottom: 0;
19 | left: 0;
20 | z-index: 1001;
21 | overflow: hidden;
22 |
23 | // reset element-ui css
24 | .horizontal-collapse-transition {
25 | transition: 0s width ease-in-out, 0s padding-left ease-in-out,
26 | 0s padding-right ease-in-out;
27 | }
28 |
29 | .scrollbar-wrapper {
30 | overflow-x: hidden !important;
31 | }
32 |
33 | .el-scrollbar__bar.is-vertical {
34 | right: 0px;
35 | }
36 |
37 | .el-scrollbar {
38 | height: 100%;
39 | }
40 |
41 | &.has-logo {
42 | .el-scrollbar {
43 | height: calc(100% - 50px);
44 | }
45 | }
46 |
47 | .is-horizontal {
48 | display: none;
49 | }
50 |
51 | a {
52 | display: inline-block;
53 | width: 100%;
54 | overflow: hidden;
55 | }
56 |
57 | .svg-icon {
58 | margin-right: 16px;
59 | }
60 |
61 | .el-menu {
62 | border: none;
63 | height: 100%;
64 | width: 100% !important;
65 | }
66 |
67 | // menu hover
68 | .submenu-title-noDropdown,
69 | .el-submenu__title {
70 | color: $navTextColor !important;
71 | &:hover {
72 | background-color: $menuHover !important;
73 | }
74 | }
75 |
76 | .is-active > .el-submenu__title {
77 | color: $navTextColor !important;
78 | }
79 |
80 | & .nest-menu .el-submenu > .el-submenu__title,
81 | & .el-submenu .el-menu-item {
82 | min-width: $sideBarWidth !important;
83 | background-color: $subMenuBg !important;
84 | &:hover {
85 | background-color: $subMenuHover !important;
86 | }
87 | }
88 | }
89 |
90 | .hideSidebar {
91 | .sidebar-container {
92 | width: 54px !important;
93 | }
94 |
95 | .main-container {
96 | margin-left: 54px;
97 | }
98 |
99 | .submenu-title-noDropdown {
100 | padding: 0 !important;
101 | position: relative;
102 |
103 | .el-tooltip {
104 | padding: 0 !important;
105 |
106 | .svg-icon {
107 | margin-left: 20px;
108 | }
109 | }
110 | }
111 |
112 | .el-submenu {
113 | overflow: hidden;
114 |
115 | & > .el-submenu__title {
116 | padding: 0 !important;
117 |
118 | .svg-icon {
119 | margin-left: 20px;
120 | }
121 |
122 | .el-submenu__icon-arrow {
123 | display: none;
124 | }
125 | }
126 | }
127 |
128 | .el-menu--collapse {
129 | .el-submenu {
130 | & > .el-submenu__title {
131 | & > span {
132 | height: 0;
133 | width: 0;
134 | overflow: hidden;
135 | visibility: hidden;
136 | display: inline-block;
137 | }
138 | }
139 | }
140 | }
141 | }
142 |
143 | .el-menu--collapse .el-menu .el-submenu {
144 | min-width: $sideBarWidth !important;
145 | }
146 |
147 | // mobile responsive
148 | .mobile {
149 | .main-container {
150 | margin-left: 0px;
151 | }
152 |
153 | .sidebar-container {
154 | transition: transform 0.28s;
155 | width: $sideBarWidth !important;
156 | }
157 |
158 | &.hideSidebar {
159 | .sidebar-container {
160 | pointer-events: none;
161 | transition-duration: 0.3s;
162 | transform: translate3d(-$sideBarWidth, 0, 0);
163 | }
164 | }
165 | }
166 |
167 | .withoutAnimation {
168 | .main-container,
169 | .sidebar-container {
170 | transition: none;
171 | }
172 | }
173 | }
174 |
175 | // when menu collapsed
176 | .el-menu--vertical {
177 | & > .el-menu {
178 | .svg-icon {
179 | margin-right: 16px;
180 | }
181 | }
182 |
183 | .nest-menu .el-submenu > .el-submenu__title,
184 | .el-menu-item {
185 | &:hover {
186 | // you can use $subMenuHover
187 | background-color: $menuHover !important;
188 | }
189 | }
190 |
191 | // the scroll bar appears when the subMenu is too long
192 | > .el-menu--popup {
193 | max-height: 100vh;
194 | overflow-y: auto;
195 |
196 | &::-webkit-scrollbar-track-piece {
197 | background: #d3dce6;
198 | }
199 |
200 | &::-webkit-scrollbar {
201 | width: 6px;
202 | }
203 |
204 | &::-webkit-scrollbar-thumb {
205 | background: #99a9bf;
206 | border-radius: 20px;
207 | }
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/src/utils/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by PanJiaChen on 16/11/18.
3 | */
4 |
5 | import chalk from 'chalk';
6 |
7 | /**
8 | * Parse the time to string
9 | * @param {(Object|string|number)} time
10 | * @param {string} cFormat
11 | * @returns {string | null}
12 | */
13 | export function parseTime(time, cFormat) {
14 | if (arguments.length === 0 || !time) {
15 | return null;
16 | }
17 | const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}';
18 | let date;
19 | if (typeof time === 'object') {
20 | date = time;
21 | } else {
22 | if (typeof time === 'string') {
23 | if (/^[0-9]+$/.test(time)) {
24 | // support "1548221490638"
25 | time = parseInt(time);
26 | } else {
27 | // support safari
28 | // https://stackoverflow.com/questions/4310953/invalid-date-in-safari
29 | time = time.replace(new RegExp(/-/gm), '/');
30 | }
31 | }
32 |
33 | if (typeof time === 'number' && time.toString().length === 10) {
34 | time = time * 1000;
35 | }
36 | date = new Date(time);
37 | }
38 | const formatObj = {
39 | y: date.getFullYear(),
40 | m: date.getMonth() + 1,
41 | d: date.getDate(),
42 | h: date.getHours(),
43 | i: date.getMinutes(),
44 | s: date.getSeconds(),
45 | a: date.getDay(),
46 | };
47 | const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
48 | const value = formatObj[key];
49 | // Note: getDay() returns 0 on Sunday
50 | if (key === 'a') {
51 | return ['日', '一', '二', '三', '四', '五', '六'][value];
52 | }
53 | return value.toString().padStart(2, '0');
54 | });
55 | return time_str;
56 | }
57 |
58 | /**
59 | * @param {number} time
60 | * @param {string} option
61 | * @returns {string}
62 | */
63 | export function formatTime(time, option) {
64 | if (('' + time).length === 10) {
65 | time = parseInt(time) * 1000;
66 | } else {
67 | time = +time;
68 | }
69 | const d = new Date(time);
70 | const now = Date.now();
71 |
72 | const diff = (now - d) / 1000;
73 |
74 | if (diff < 30) {
75 | return '刚刚';
76 | } else if (diff < 3600) {
77 | // less 1 hour
78 | return Math.ceil(diff / 60) + '分钟前';
79 | } else if (diff < 3600 * 24) {
80 | return Math.ceil(diff / 3600) + '小时前';
81 | } else if (diff < 3600 * 24 * 2) {
82 | return '1天前';
83 | }
84 | if (option) {
85 | return parseTime(time, option);
86 | } else {
87 | return (
88 | d.getMonth() +
89 | 1 +
90 | '月' +
91 | d.getDate() +
92 | '日' +
93 | d.getHours() +
94 | '时' +
95 | d.getMinutes() +
96 | '分'
97 | );
98 | }
99 | }
100 |
101 | /**
102 | * @param {string} url
103 | * @returns {Object}
104 | */
105 | export function param2Obj(url) {
106 | const search = url.split('?')[1];
107 | if (!search) {
108 | return {};
109 | }
110 | return JSON.parse(
111 | '{"' +
112 | decodeURIComponent(search)
113 | .replace(/"/g, '\\"')
114 | .replace(/&/g, '","')
115 | .replace(/=/g, '":"')
116 | .replace(/\+/g, ' ') +
117 | '"}'
118 | );
119 | }
120 |
121 | /**
122 | * @name:
123 | * @description: sku解析 [{properties:"材质:羊毛;尺寸:M;颜色:黑色;"},{properties:"材质:羊毛;尺寸:M;颜色:黑色;"}]
124 | * @param {Array} val 包含properties值数组
125 | * @return: names:属性名 sku 属性值
126 | */
127 | export function getSku(val) {
128 | if (!val) {
129 | return false;
130 | }
131 | let properties = [];
132 | let names = [];
133 | let sku = [];
134 | let arr2 = [];
135 | let obj = {};
136 | let handle = [];
137 | for (let i = 0; i < val.length; i++) {
138 | properties.push(val[i].properties);
139 | }
140 | for (let i = 0; i < properties.length; i++) {
141 | let item = properties[i].split(';');
142 |
143 | let arr1 = [];
144 | for (let j = 0; j < item.length; j++) {
145 | let name = item[j].split(':');
146 | handle.push(name[0], name[1]);
147 | arr1.push(name[0]);
148 | arr2.push(name[1]);
149 | obj[name[0]] = [];
150 | Object.keys(obj).forEach(item => {
151 | if (item == name[0]) {
152 | // console.log(name[1], obj[item])
153 | obj[item].push(name[1]);
154 | }
155 | });
156 | }
157 | // 由于拼接时候最后一个会有空,需要删除
158 | arr1.splice(arr1.length - 1, 1);
159 | arr2.splice(arr2.length - 1, 1);
160 | names = arr1;
161 | sku = Array.from(new Set(arr2));
162 | }
163 | // 模拟用户操作
164 | handle = handle.filter(item => {
165 | return item;
166 | });
167 | return { names, sku, handle };
168 | }
169 |
170 | /**
171 | * @name:
172 | * @description: 复制内容到粘贴板
173 | * @param {str} content : 需要复制的内容
174 | * @param {str} message : 复制完后的提示,不传则默认提示"复制成功"
175 | * @return:
176 | */
177 |
178 | export function copyToClip(content, message) {
179 | var aux = document.createElement('input');
180 | aux.setAttribute('value', content);
181 | document.body.appendChild(aux);
182 | aux.select();
183 | document.execCommand('copy');
184 | document.body.removeChild(aux);
185 | if (message == null) {
186 | confirm('复制成功');
187 | } else {
188 | confirm(message);
189 | }
190 | }
191 |
192 | /**
193 | *文件下载
194 | *引入后直接掉用 (此方法可能会引起跨域的问题)
195 | *传入参数 ulr地址 name文件名
196 | */
197 | export const downloadMp3 = (filePath, fileName = 'yuying.wav') => {
198 | fetch(filePath)
199 | .then(res => res.blob())
200 | .then(blob => {
201 | const a = document.createElement('a');
202 | document.body.appendChild(a);
203 | a.style.display = 'none';
204 | // 使用获取到的blob对象创建的url
205 | const url = window.URL.createObjectURL(blob);
206 | a.href = url;
207 | // 指定下载的文件名
208 | a.download = fileName;
209 | a.click();
210 | document.body.removeChild(a);
211 | // 移除blob对象的url
212 | window.URL.revokeObjectURL(url);
213 | });
214 | };
215 |
--------------------------------------------------------------------------------
/src/views/404.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
OOPS!
12 |
15 |
{{ message }}
16 |
Please check that the URL you entered is correct, or click the button below to return to the homepage.
17 |
Back to home
18 |
19 |
20 |
21 |
22 |
23 |
34 |
35 |
229 |
--------------------------------------------------------------------------------
/src/icons/svg/service.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/views/login/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
享得多后台管理系统
13 |
14 |
15 |
16 |
17 |
18 |
19 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
48 |
49 |
50 |
51 |
52 |
53 | 登录
59 |
60 |
61 |
62 |
63 |
153 |
154 |
200 |
201 |
264 |
--------------------------------------------------------------------------------
/src/components/Table/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
19 |
25 |
26 |
34 |
35 |
36 |
37 |
38 |
39 |
48 |
54 | {{
55 | typeof active.name === 'function'
56 | ? active.name(scope.row)
57 | : active.name
58 | }}
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
76 |
77 |
78 |
79 |
80 |
263 |
264 |
289 |
--------------------------------------------------------------------------------
/src/components/upload/index.vue:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
![]()
30 |
31 |
32 |
33 |
34 |
35 |
44 |
45 |
46 |
47 |
270 |
359 |
--------------------------------------------------------------------------------
/src/views/worksheet/worksheetcom.vue:
--------------------------------------------------------------------------------
1 |
8 | /** * @name: 自定义表单 * @description: 基于vue2.0 element mobx二次封装自定义表单组件 *
9 | @formData:当前自定义表单所有类型集合 * @formData:当前自定义表单所有类型 */
10 |
11 |
12 |
13 |
19 |
26 |
27 |
34 |
35 |
42 |
48 |
49 |
50 |
58 |
59 |
60 |
66 |
67 |
68 |
78 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
418 |
419 |
420 |
424 |
--------------------------------------------------------------------------------
/src/components/cropperImg/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
34 |
38 |
39 |
48 |
49 |
50 |
59 |
81 |
85 |
86 |
87 |
88 |
377 |
465 |
--------------------------------------------------------------------------------
/src/views/Admin/index.vue:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
18 |
19 |
20 |
26 |
27 |
28 | 查询
29 | 重置
30 | 添加账号
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
114 |
115 |
116 |
125 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
174 |
175 |
176 |
185 |
192 |
193 |
194 |
195 |
201 |
202 |
203 |
204 |
205 |
211 |
212 |
213 |
214 |
215 |
219 |
220 |
221 |
222 |
223 |
497 |
498 |
504 |
--------------------------------------------------------------------------------