├── src ├── vuex │ ├── modules │ │ ├── permission.js │ │ ├── merchant.js │ │ ├── user.js │ │ ├── app.js │ │ ├── goods.js │ │ ├── admin-user.js │ │ └── auth-config.js │ ├── getters.js │ ├── mutations.js │ ├── actions.js │ ├── store.js │ └── mutation-types.js ├── utils │ ├── serverConfig.js │ ├── storageUtils.js │ ├── tools.js │ ├── dateUtils.js │ └── request.js ├── assets │ ├── logo.png │ ├── fonts │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.woff2 │ │ ├── iconfont.json │ │ ├── iconfont.css │ │ └── demo.css │ ├── fontss │ │ ├── iconfont.eot │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ ├── iconfont.woff2 │ │ ├── iconfont.json │ │ ├── iconfont.css │ │ ├── iconfont.svg │ │ └── iconfont.js │ └── css │ │ └── reset.css ├── views │ ├── auth │ │ ├── Auth.vue │ │ ├── RoleList.vue │ │ └── UserList.vue │ ├── order │ │ └── Order.vue │ ├── activity │ │ ├── Activity.vue │ │ └── ActivityList.vue │ ├── comment │ │ └── Comment.vue │ ├── goods │ │ ├── Goods.vue │ │ ├── GoodsList.vue │ │ ├── Detail.vue │ │ └── GoodsService.vue │ ├── merchant │ │ └── Merchant.vue │ ├── banner │ │ ├── Banner.vue │ │ └── BannerList.vue │ ├── category │ │ ├── Category.vue │ │ ├── CategoryList.vue │ │ └── AddCategory.vue │ ├── home │ │ └── Home.vue │ └── login │ │ └── Login.vue ├── plugins │ └── element.js ├── styles │ ├── filter-area.scss │ ├── dialog.scss │ ├── header.scss │ ├── index.scss │ └── siderbar.scss ├── App.vue ├── api │ ├── activity.js │ ├── comment.js │ ├── merchant.js │ ├── order.js │ ├── banner.js │ ├── category.js │ ├── index.js │ ├── goods.js │ └── user.js ├── main-prod.js ├── main-dev.js ├── components │ ├── MyBread │ │ └── MyBread.vue │ ├── Aside │ │ ├── Item.vue │ │ └── Aside.vue │ └── Header │ │ └── Header.vue ├── router │ ├── index.js │ └── routes.js ├── mixins │ └── ResizeHandle.js └── config │ └── menuConfig.js ├── .browserslistrc ├── public ├── favicon.ico ├── static │ └── images │ │ └── weather │ │ ├── lei.gif │ │ ├── qing.gif │ │ ├── wu.gif │ │ ├── xue.gif │ │ ├── yin.gif │ │ ├── yu.gif │ │ ├── yun.gif │ │ ├── bingbao.gif │ │ └── shachen.gif └── index.html ├── babel.config.js ├── .gitignore ├── .eslintrc.js ├── README.md ├── package.json.bak ├── package.json ├── vue.config.js └── vue.config.js.bak /src/vuex/modules/permission.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /src/utils/serverConfig.js: -------------------------------------------------------------------------------- 1 | export const serverURL = 'http://dev.shubuzuo.top/' // 开发环境 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/views/auth/Auth.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/views/order/Order.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/assets/fonts/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fonts/iconfont.eot -------------------------------------------------------------------------------- /src/assets/fonts/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fonts/iconfont.ttf -------------------------------------------------------------------------------- /src/views/activity/Activity.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/comment/Comment.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/views/goods/Goods.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/merchant/Merchant.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/assets/fonts/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fonts/iconfont.woff -------------------------------------------------------------------------------- /src/assets/fonts/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fonts/iconfont.woff2 -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fontss/iconfont.eot -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fontss/iconfont.ttf -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fontss/iconfont.woff -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/src/assets/fontss/iconfont.woff2 -------------------------------------------------------------------------------- /src/views/banner/Banner.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/category/Category.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/static/images/weather/lei.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/lei.gif -------------------------------------------------------------------------------- /public/static/images/weather/qing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/qing.gif -------------------------------------------------------------------------------- /public/static/images/weather/wu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/wu.gif -------------------------------------------------------------------------------- /public/static/images/weather/xue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/xue.gif -------------------------------------------------------------------------------- /public/static/images/weather/yin.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/yin.gif -------------------------------------------------------------------------------- /public/static/images/weather/yu.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/yu.gif -------------------------------------------------------------------------------- /public/static/images/weather/yun.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/yun.gif -------------------------------------------------------------------------------- /public/static/images/weather/bingbao.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/bingbao.gif -------------------------------------------------------------------------------- /public/static/images/weather/shachen.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder-xc/mini-boss/HEAD/public/static/images/weather/shachen.gif -------------------------------------------------------------------------------- /src/vuex/getters.js: -------------------------------------------------------------------------------- 1 | export default { 2 | isCollapseMenu: state => state.isCollapseMenu, 3 | sidebar: state => state.app.sidebar, 4 | } -------------------------------------------------------------------------------- /src/plugins/element.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import 'element-ui/lib/theme-chalk/index.css' 4 | 5 | Vue.use(Element) 6 | -------------------------------------------------------------------------------- /src/styles/filter-area.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | .filter { 3 | .input { 4 | margin-bottom: 10px; 5 | margin-right: 10px; 6 | } 7 | .search, .add { 8 | margin-bottom: 10px; 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /src/styles/dialog.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | .mobile { 3 | .el-dialog { 4 | width: 350px; 5 | } 6 | } 7 | .el-dialog { 8 | width: 650px; 9 | .pre-img { 10 | width: 100%; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 包含n个用于直接更新状态数据的方法的对象 3 | */ 4 | 5 | import { TOGGLE_COLLAPSE } from './mutation-types' 6 | 7 | export default { 8 | [TOGGLE_COLLAPSE](state) { // 总state 9 | state.isCollapseMenu = !state.isCollapseMenu; 10 | } 11 | } -------------------------------------------------------------------------------- /src/vuex/modules/merchant.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | 3 | } 4 | 5 | const mutations = { 6 | 7 | } 8 | 9 | const actions = { 10 | 11 | } 12 | 13 | const getters = {} 14 | 15 | export default { 16 | state, 17 | mutations, 18 | actions, 19 | getters 20 | } -------------------------------------------------------------------------------- /src/vuex/modules/user.js: -------------------------------------------------------------------------------- 1 | const state = { 2 | 3 | } 4 | 5 | const mutations = { 6 | 7 | } 8 | 9 | const actions = { 10 | 11 | } 12 | 13 | const getters = {} 14 | 15 | export default { 16 | state, 17 | mutations, 18 | actions, 19 | getters 20 | } -------------------------------------------------------------------------------- /src/vuex/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 包含n个用于间接更新状态数据的方法的对象 3 | * 可以包含异步/逻辑操作代码 4 | */ 5 | import { TOGGLE_COLLAPSE } from './mutation-types' 6 | export default { 7 | toggleCollapse({ commit }) { // state是根/总state 8 | commit(TOGGLE_COLLAPSE) 9 | // commit('xxx') // 找到所有同名匹配的mutation执行 10 | } 11 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | 'eslint:recommended' 9 | ], 10 | rules: { 11 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'no-unused-vars': process.env.NODE_ENV === 'production' ? 'error' : 'off' 14 | }, 15 | parserOptions: { 16 | parser: 'babel-eslint' 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/storageUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | 操作loacl数据的工具函数模块 3 | */ 4 | import store from 'store' 5 | 6 | 7 | const TOKEN = 'token' 8 | 9 | export default { 10 | // 保存token 11 | saveToken(token) { 12 | store.set(TOKEN, token) 13 | }, 14 | 15 | 16 | // 需要返回一个 token,如果没有,返回空字符串 17 | getToken() { 18 | return store.get(TOKEN) || "" 19 | }, 20 | 21 | // 删除保存的user 22 | removeToken() { 23 | // localStorage.removeItem(USER_KEY) 24 | store.remove(TOKEN) 25 | }, 26 | } -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /src/styles/header.scss: -------------------------------------------------------------------------------- 1 | #app { 2 | .mobile { 3 | .el-header { 4 | .el-row--flex { 5 | display: block; 6 | } 7 | .time-weather-toggle { 8 | display: flex; 9 | align-items: center; 10 | font-size: 14px; 11 | padding: 0 20px; 12 | margin-top: 10px; 13 | } 14 | .userinfo { 15 | margin-top: 10px; 16 | } 17 | } 18 | .welcome { 19 | display: flex; 20 | height: 100%; 21 | justify-content: center; 22 | align-items: center; 23 | font-size: 20px; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/api/activity.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * 获取活动列表 5 | */ 6 | export const reqActiveList = (query) => request({ 7 | url: `/actions?query=${JSON.stringify(query)}` 8 | }) 9 | 10 | /** 11 | * 添加/修改活动 12 | */ 13 | export const addUpdateActive = ({ title, icon, _id }) => request({ 14 | url: `/actions${_id ? `/${_id}` : ''}`, 15 | method: `${_id ? 'PUT' : 'POST'}`, 16 | data: { 17 | title, 18 | icon 19 | } 20 | }) 21 | 22 | /** 23 | * 删除活动 24 | */ 25 | export const delActive = ({ _id }) => request({ 26 | url: `/actions/${_id}`, 27 | method: 'DELETE' 28 | }) -------------------------------------------------------------------------------- /src/api/comment.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * 获取评论 5 | */ 6 | export const reqComment = (query) => request({ 7 | url: `/comments?query=${JSON.stringify(query)}` 8 | }) 9 | 10 | /** 11 | * 添加/修改评论 12 | */ 13 | export const reqAddUpdateComment = ({ order, message, images, _id }) => request({ 14 | url: `/comments${_id ? `/${_id}` : ""}`, 15 | method: `${_id ? "PUT" : "POST"}`, 16 | data: { 17 | order, 18 | message, 19 | images 20 | } 21 | }) 22 | 23 | /** 24 | * 删除评论 25 | */ 26 | export const reqDelComment = ({ _id }) => request({ 27 | url: `/comments/${_id}`, 28 | method: 'DELETE' 29 | }) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 此项目为小程序商城后台管理系统 2 | 1. 该项目使用的技术栈为Vue全家桶 + ES6 + Element UI + Axios 3 | 2. 包含了商品分类,商品管理,商家管理,订单管理和用户管理等子模块 4 | 3. 采用模块化、组件化、工程化的模式开发(模块化、组件化和工程化详解戳:[谈谈前端工程化、组件化、模块化以及自动化](https://www.xcio.cn/index.php/archives/62/).) 5 | 6 | ## 项目依赖安装 7 | ``` 8 | npm install 9 | 10 | cnpm install 11 | 12 | yarn install 13 | ``` 14 | 15 | ### 启动项目(开发环境) 16 | ``` 17 | npm run serve 18 | ``` 19 | 20 | ### 项目打包(生产环境打包) 21 | ``` 22 | npm run build 23 | ``` 24 | 25 | ### 生产环境运行 26 | ``` 27 | serve -s dist 28 | ``` 29 | 30 | ### Lints and fixes files 31 | ``` 32 | yarn lint 33 | 34 | ``` 35 | 36 | ### Customize configuration 37 | See [Configuration Reference](https://cli.vuejs.org/config/). 38 | -------------------------------------------------------------------------------- /src/utils/tools.js: -------------------------------------------------------------------------------- 1 | /* 工具方法模块 */ 2 | 3 | 4 | /** 5 | * 合并数组并去重 6 | * @param {Array} origin 要被合并的数组 7 | * @param {Array} newArr 合并到origin的数组 8 | * @returns 返回一个新的数组(已经合并+去重) 9 | */ 10 | export function merge(origin, newArr) { 11 | let tempObj = {}; 12 | const resultArr = [...origin]; 13 | if (!newArr || newArr.length === 0) { 14 | return resultArr; 15 | } 16 | resultArr.forEach(item => { 17 | if(!tempObj[item._id]) { 18 | tempObj[item._id] = true; 19 | } 20 | }) 21 | return newArr.reduce((pre, item) => { 22 | if(!tempObj[item._id]) { 23 | tempObj[item._id] = true; 24 | pre.push(item) 25 | } 26 | return pre; 27 | }, resultArr); 28 | } -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import "@/styles/siderbar.scss"; 2 | @import "@/styles/header.scss"; 3 | @import "@/styles/filter-area.scss"; 4 | @import "@/styles/dialog.scss"; 5 | 6 | /* 全局公共样式 */ 7 | #app, 8 | body, 9 | html { 10 | color: rgba(0, 0, 0, 0.65); 11 | } 12 | 13 | .el-card { 14 | margin-top: 15px; 15 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15) !important; 16 | } 17 | 18 | .el-pagination, 19 | .el-table { 20 | margin-top: 15px; 21 | } 22 | 23 | .el-table { 24 | font-size: 12px; 25 | } 26 | 27 | .ql-editor { 28 | min-height: 300px; 29 | } 30 | 31 | .el-upload-list__item { 32 | transition: none !important; 33 | } 34 | 35 | .el-message-box { 36 | max-width: 100%; 37 | } 38 | -------------------------------------------------------------------------------- /src/main-prod.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import MyBread from 'components/MyBread/MyBread.vue' 4 | import router from './router' 5 | import store from './vuex/store' 6 | import './styles/index.scss' 7 | import './assets/css/reset.css'; 8 | import './assets/fonts/iconfont.css' 9 | 10 | 11 | 12 | 13 | // 引入富文本编辑器 14 | import VueQuillEditor from 'vue-quill-editor' 15 | 16 | // require styles 17 | import 'quill/dist/quill.core.css' 18 | import 'quill/dist/quill.snow.css' 19 | import 'quill/dist/quill.bubble.css' 20 | 21 | Vue.config.productionTip = false 22 | 23 | Vue.use(VueQuillEditor) 24 | Vue.component(MyBread.name, MyBread) 25 | 26 | 27 | new Vue({ 28 | router, 29 | render: h => h(App), 30 | store 31 | }).$mount('#app') -------------------------------------------------------------------------------- /src/utils/dateUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 包含n个日期时间处理的工具函数模块 3 | */ 4 | 5 | /** 6 | * 格式化日期 7 | */ 8 | export function formateDate(time) { 9 | if (!time) return '' 10 | const date = new Date(time) 11 | let month = date.getMonth() + 1; 12 | let day = date.getDate(); 13 | let hour = date.getHours(); 14 | let min = date.getMinutes(); 15 | let seconds = date.getSeconds(); 16 | 17 | if (seconds < 10) { 18 | seconds = `0${seconds}`; 19 | } 20 | if (min < 10) { 21 | min = `0${min}`; 22 | } 23 | if (hour < 10) { 24 | hour = `0${hour}`; 25 | } 26 | if (month < 10) { 27 | month = `0${month}`; 28 | } 29 | if (day < 10) { 30 | day = `0${day}`; 31 | } 32 | return `${date.getFullYear()}-${month}-${day} ${hour}:${min}:${seconds}`; 33 | } -------------------------------------------------------------------------------- /src/main-dev.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import MyBread from 'components/MyBread/MyBread.vue' 4 | import router from './router' 5 | import store from './vuex/store' 6 | // 全局css 7 | import './styles/index.scss' 8 | // 基本样式 9 | import './assets/css/reset.css'; 10 | // iconfont图标库 11 | import './assets/fonts/iconfont.css' 12 | // 引入富文本编辑器 13 | import VueQuillEditor from 'vue-quill-editor' 14 | // 引入富文本编辑器 css 样式 15 | import 'quill/dist/quill.core.css' 16 | import 'quill/dist/quill.snow.css' 17 | import 'quill/dist/quill.bubble.css' 18 | 19 | Vue.config.productionTip = false 20 | 21 | Vue.use(VueQuillEditor) 22 | Vue.component(MyBread.name, MyBread) 23 | 24 | 25 | new Vue({ 26 | router, 27 | render: h => h(App), 28 | store 29 | }).$mount('#app') -------------------------------------------------------------------------------- /src/api/merchant.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | 4 | /** 5 | * 获取店铺列表 6 | */ 7 | export const reqMerchants = (query) => request(`/selles?query=${JSON.stringify(query)}`); 8 | 9 | /** 10 | * 添加/修改店铺 11 | */ 12 | export const reqAddUpdateMerchant = ({ name, logo, commodities, salesVolume, commoditiesCount, appraise, collectCount, _id }) => request({ 13 | url: `/selles${_id ? `/${_id}` : ""}`, 14 | method: `${_id ? "PUT" : "POST"}`, 15 | data: { 16 | name, 17 | logo, 18 | commodities, 19 | salesVolume, 20 | commoditiesCount, 21 | appraise, 22 | collectCount 23 | } 24 | }) 25 | 26 | /** 27 | * 删除店铺 28 | */ 29 | export const reqDelMerchant = ({ _id }) => request({ 30 | url: `/selles/${_id}`, 31 | method: 'DELETE' 32 | }) 33 | -------------------------------------------------------------------------------- /src/api/order.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * 获取订单列表 5 | */ 6 | export const reqOrders = (query) => { 7 | const params = JSON.parse(JSON.stringify(query)) 8 | // if(!params.where.orderNum.$regex) { 9 | // delete params.where 10 | // } 11 | return request({ 12 | url: `/orders?query=${JSON.stringify(params)}` 13 | }) 14 | } 15 | 16 | /** 17 | * 添加/修改订单 18 | */ 19 | export const reqAddUpdateOrder = ({ commoditis, user, _id }) => request({ 20 | url: `/orders${_id ? `/${_id}` : ""}`, 21 | method: `${_id ? 'PUT' : 'POST'}`, 22 | data: { 23 | commoditis, 24 | user 25 | } 26 | }) 27 | 28 | /** 29 | * 删除订单 30 | */ 31 | export const reqDelOrder = ({ _id }) => request({ 32 | url: `/orders/${_id}`, 33 | method: 'DELETE' 34 | }) -------------------------------------------------------------------------------- /src/api/banner.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * 获取轮播图 5 | */ 6 | export const reqBanner = (query) => { 7 | // debugger 8 | return request({ 9 | url: `/banners?query=${JSON.stringify(query)}`, 10 | headers: { 11 | checkToken: true 12 | } 13 | }) 14 | } 15 | 16 | /** 17 | * 添加或修改轮播图 18 | */ 19 | export const addUpdateBanner = ({ title, url, index, _id }) => request({ 20 | url: `/banners${_id ? `/${_id}` : ''}`, 21 | method: `${_id ? 'PUT' : 'POST'}`, 22 | data: { 23 | title, 24 | url, 25 | index 26 | }, 27 | headers: { 28 | checkToken: true 29 | } 30 | }) 31 | 32 | /** 33 | * 删除轮播图 34 | */ 35 | export const delBanner = (_id) => request({ 36 | url: `/banners/${_id}`, 37 | method: 'DELETE', 38 | headers: { 39 | checkToken: true 40 | } 41 | }) -------------------------------------------------------------------------------- /package.json.bak: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-boss", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.4.4", 12 | "element-ui": "^2.4.5", 13 | "sass-loader": "^8.0.2", 14 | "vue": "^2.6.10", 15 | "vue-router": "^3.1.3" 16 | }, 17 | "devDependencies": { 18 | "@vue/cli-plugin-babel": "^4.1.0", 19 | "@vue/cli-plugin-eslint": "^4.1.0", 20 | "@vue/cli-plugin-router": "^4.1.0", 21 | "@vue/cli-service": "^4.1.0", 22 | "babel-eslint": "^10.0.3", 23 | "eslint": "^5.16.0", 24 | "eslint-plugin-vue": "^5.0.0", 25 | "vue-cli-plugin-element": "^1.0.1", 26 | "vue-template-compiler": "^2.6.10" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/vuex/store.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | import mutations from './mutations'; 5 | import actions from './actions'; 6 | import getters from './getters' 7 | 8 | import goods from './modules/goods'; 9 | import merchant from './modules/merchant'; 10 | import adminUser from './modules/admin-user'; 11 | import user from './modules/user'; 12 | import app from './modules/app' 13 | // import permission from './modules/permission' 14 | import authConfig from './modules/auth-config' 15 | 16 | Vue.use(Vuex); 17 | 18 | export default new Vuex.Store({ 19 | state: { 20 | isCollapseMenu: false, 21 | }, 22 | mutations, 23 | actions, 24 | getters, 25 | modules: { 26 | goods, 27 | merchant, 28 | adminUser, 29 | user, 30 | app, 31 | // permission, 32 | authConfig 33 | } 34 | }) -------------------------------------------------------------------------------- /src/api/category.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * 获取分类列表 5 | */ 6 | export const reqCategories = (query) => request({ 7 | url: `/categories?query=${JSON.stringify(query)}`, 8 | headers: { 9 | checkToken: true 10 | } 11 | }) 12 | 13 | /** 14 | * 添加或修改分类 15 | */ 16 | export const reqAddUpdateCategory = ({ parent = '', categoryName: name, url = '', attributes, _id }) => request({ 17 | url: `/categories${_id ? `/${_id}` : ''}`, 18 | method: `${_id ? 'PUT' : 'POST'}`, 19 | data: { 20 | parent, 21 | name, 22 | url, 23 | attributes 24 | }, 25 | headers: { 26 | checkToken: true 27 | } 28 | }) 29 | 30 | /** 31 | * 删除分类 32 | */ 33 | export const deleteCategory = (_id) => request({ 34 | url: `/categories/${_id}`, 35 | method: 'DELETE', 36 | headers: { 37 | checkToken: true 38 | } 39 | }) -------------------------------------------------------------------------------- /src/components/MyBread/MyBread.vue: -------------------------------------------------------------------------------- 1 | 8 | 38 | -------------------------------------------------------------------------------- /src/vuex/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const RECEIVE_TOKEN = 'receive_token'; 2 | export const RECEIVE_ADMIN_USER = 'receive_admin_user'; 3 | export const LOGOUT = 'logout'; 4 | export const RECEIVE_USER_LIST = 'receive_user_list'; 5 | export const DELETE_USER = 'delete_user' 6 | export const RECEIVE_ACTIVE_LIST = 'receive_active_list'; 7 | export const RECEIVE_GOODS_LIST = 'receive_goods_list' 8 | export const RECEIVE_GOODS = 'receive_goods' 9 | export const TOGGLE_COLLAPSE = 'toggle_collapse' 10 | export const RECEIVE_GOODS_SERVICE = 'receive_goods_service'; 11 | export const RECEIVE_CATEGORIES = 'receive_categorise'; 12 | export const RECEIVE_MERCHANTS = 'receive_merchants'; 13 | export const RECEIVE_ORDERS = 'receive_orders'; 14 | export const RECEIVE_BACK_ROUTE_PATH = 'receive_back_route_path' 15 | export const RECEIVE_COMMENTS = 'receive_comments'; 16 | export const RECEIVE_ROLE_LIST = 'receive_role_list'; 17 | export const PERMISSION_MAPPING_VIEW = 'permission_mapping_view'; 18 | export const CLEAR_CACHE = 'clear_cache'; -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import jsonp from 'jsonp' 3 | import { Message } from 'element-ui' 4 | 5 | // 发送 jsonp 请求得到天气信息 6 | export const reqWeather = () => { 7 | return new Promise((resolve) => { 8 | // 执行器函数:内部去执行异步任务, 9 | // 成功了调用 resolve(), 失败了不调用 reject(),直接提示错误 10 | const url = `https://tianqiapi.com/api?version=v6&appid=81661511&appsecret=0oTDWd9T&vue=1` 11 | jsonp(url, {}, (err, data) => { 12 | if (!err) { 13 | const { wea_img, wea } = data; 14 | resolve({ wea_img, wea }); 15 | } else { 16 | Message.error('获取天气信息失败'); 17 | } 18 | }) 19 | }) 20 | } 21 | 22 | /** 23 | * 上传图片 24 | */ 25 | export const uploadImg = (file) => { 26 | let param = new FormData() // 创建form对象 27 | param.append('file', file) // 通过append向form对象添加数据 28 | let config = { 29 | headers: { 30 | 'Content-Type': 'multipart/form-data', 31 | checkToken: true 32 | } 33 | } 34 | return request.post('/upload', param, config) 35 | } -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import routes from './routes' 4 | import store from '@/vuex/store' 5 | 6 | Vue.use(VueRouter) 7 | 8 | // 修改路由push方法,阻止重复点击报错 9 | const originalPush = VueRouter.prototype.push; 10 | VueRouter.prototype.push = function push(location) { 11 | return originalPush.call(this, location).catch(err => err); 12 | }; 13 | 14 | // 修改路由replace方法,阻止重复点击报错 15 | const originalReplace = VueRouter.prototype.replace; 16 | VueRouter.prototype.replace = function replace(location) { 17 | return originalReplace.call(this, location).catch(err => err); 18 | }; 19 | 20 | const router = new VueRouter({ 21 | mode: 'history', 22 | routes, 23 | // base: '/shopadmin/', // 生产环境打包设置 24 | }) 25 | 26 | router.beforeEach((to, from, next) => { 27 | const token = store.state.adminUser.token; 28 | if (to.path === '/login') { 29 | if (token) { 30 | next('/home') 31 | } else { 32 | next() 33 | } 34 | } 35 | next() 36 | }) 37 | 38 | export default router 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mini-boss", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "axios": "^0.19.0", 12 | "core-js": "^3.4.4", 13 | "element-ui": "^2.4.5", 14 | "jsonp": "^0.2.1", 15 | "lodash": "^4.17.15", 16 | "store": "^2.0.12", 17 | "vue": "^2.6.10", 18 | "vue-quill-editor": "^3.0.6", 19 | "vue-router": "^3.1.3", 20 | "vuex": "^3.1.2" 21 | }, 22 | "devDependencies": { 23 | "@vue/cli-plugin-babel": "^4.1.0", 24 | "@vue/cli-plugin-eslint": "^4.1.0", 25 | "@vue/cli-plugin-router": "^4.1.0", 26 | "@vue/cli-service": "^4.1.0", 27 | "babel-eslint": "^10.0.3", 28 | "eslint": "^5.16.0", 29 | "eslint-plugin-vue": "^5.0.0", 30 | "node-sass": "^4.13.1", 31 | "sass-loader": "^8.0.2", 32 | "vue-cli-plugin-element": "^1.0.1", 33 | "vue-template-compiler": "^2.6.10" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | const path = require('path') 3 | 4 | /* 根据指定目录名得到根目录的绝对路径 */ 5 | function resolve(dir) { 6 | return path.join(__dirname, dir) 7 | } 8 | 9 | module.exports = { 10 | /* 编写webpack支持的配置 */ 11 | configureWebpack: { 12 | resolve: { 13 | extensions: ['.js', '.vue', '.json'], 14 | alias: { 15 | '@': resolve('src'), 16 | 'components': resolve('src/components'), 17 | 'views': resolve('src/views'), 18 | 'api': resolve('src/api') 19 | } 20 | }, 21 | externals: { 22 | 'vue': 'Vue', 23 | 'element-ui': 'ELEMENT', 24 | 'vue-quill-editor': "VueQuillEditor" 25 | }, 26 | }, 27 | chainWebpack: config => { //修改webpack打包的入口文件。需要在根目录建两个对应入口js文件 28 | config.when(process.env.NODE_ENV === 'production', config => { 29 | config.entry('app').clear().add('./src/main-prod.js') //生产环境 30 | }) 31 | config.when(process.env.NODE_ENV === 'development', config => { 32 | config.entry('app').clear().add('./src/main-dev.js') //开发环境 33 | }) 34 | }, 35 | productionSourceMap: false, 36 | } -------------------------------------------------------------------------------- /src/mixins/ResizeHandle.js: -------------------------------------------------------------------------------- 1 | import store from '@/vuex/store' 2 | 3 | const { body } = document 4 | const WIDTH = 992 // refer to Bootstrap's responsive design 5 | 6 | export default { 7 | beforeMount() { 8 | window.addEventListener('resize', this.$_resizeHandler) 9 | }, 10 | beforeDestroy() { 11 | window.removeEventListener('resize', this.$_resizeHandler) 12 | }, 13 | mounted() { 14 | 15 | const isMobile = this.$_isMobile() 16 | if (isMobile) { 17 | store.dispatch('app/toggleDevice', 'mobile') 18 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 19 | } 20 | }, 21 | methods: { 22 | $_isMobile() { 23 | const rect = body.getBoundingClientRect() 24 | return rect.width - 1 < WIDTH 25 | }, 26 | $_resizeHandler() { 27 | if (!document.hidden) { 28 | const isMobile = this.$_isMobile() 29 | store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') 30 | if (isMobile) { 31 | store.dispatch('app/closeSideBar', { withoutAnimation: true }) 32 | } 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /vue.config.js.bak: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | const path = require('path') 3 | 4 | /* 根据指定目录名得到根目录的绝对路径 */ 5 | function resolve(dir) { 6 | return path.join(__dirname, dir) 7 | } 8 | 9 | module.exports = { 10 | // publicPath: '/shopadmin', // 生产环境打包设置 11 | /* 编写webpack支持的配置 */ 12 | configureWebpack: { 13 | resolve: { 14 | extensions: ['.js', '.vue', '.json'], 15 | alias: { 16 | '@': resolve('src'), 17 | 'components': resolve('src/components'), 18 | 'views': resolve('src/views'), 19 | 'api': resolve('src/api') 20 | } 21 | }, 22 | externals: { 23 | 'vue': 'Vue', 24 | 'element-ui': 'ELEMENT', 25 | 'vue-quill-editor': "VueQuillEditor" 26 | }, 27 | }, 28 | chainWebpack: config => { //修改webpack打包的入口文件。需要在根目录建两个对应入口js文件 29 | config.when(process.env.NODE_ENV === 'production', config => { 30 | config.entry('app').clear().add('./src/main-prod.js') //生产环境 31 | }) 32 | config.when(process.env.NODE_ENV === 'development', config => { 33 | config.entry('app').clear().add('./src/main-dev.js') //开发环境 34 | }) 35 | }, 36 | productionSourceMap: false, 37 | } -------------------------------------------------------------------------------- /src/vuex/modules/app.js: -------------------------------------------------------------------------------- 1 | 2 | const state = { 3 | sidebar: { 4 | opened: true, 5 | withoutAnimation: false 6 | }, 7 | device: 'desktop', 8 | // size: 'medium' 9 | } 10 | 11 | const mutations = { 12 | TOGGLE_SIDEBAR: state => { 13 | state.sidebar.opened = !state.sidebar.opened 14 | state.sidebar.withoutAnimation = false 15 | }, 16 | CLOSE_SIDEBAR: (state, withoutAnimation) => { 17 | state.sidebar.opened = false 18 | state.sidebar.withoutAnimation = withoutAnimation 19 | }, 20 | TOGGLE_DEVICE: (state, device) => { 21 | state.device = device 22 | }, 23 | // SET_SIZE: (state, size) => { 24 | // state.size = size 25 | // } 26 | } 27 | 28 | const actions = { 29 | toggleSideBar({ commit }) { 30 | commit('TOGGLE_SIDEBAR') 31 | }, 32 | closeSideBar({ commit }, { withoutAnimation }) { 33 | commit('CLOSE_SIDEBAR', withoutAnimation) 34 | }, 35 | toggleDevice({ commit }, device) { 36 | commit('TOGGLE_DEVICE', device) 37 | }, 38 | // setSize({ commit }, size) { 39 | // commit('SET_SIZE', size) 40 | // } 41 | } 42 | 43 | export default { 44 | namespaced: true, 45 | state, 46 | mutations, 47 | actions 48 | } 49 | -------------------------------------------------------------------------------- /src/api/goods.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | /** 4 | * 获取商品 5 | */ 6 | export const reqGoods = (query) => request({ 7 | url: `/commodities?query=${JSON.stringify(query)}`, 8 | }) 9 | 10 | /** 11 | * 添加/修改商品 12 | */ 13 | export const reqAddUpdateGoods = (params) => { 14 | const { _id } = params; 15 | return request({ 16 | url: `/commodities${_id ? `/${_id}` : ''}`, 17 | method: `${_id ? 'PUT' : 'POST'}`, 18 | data: params 19 | }) 20 | } 21 | 22 | /** 23 | * 删除商品 24 | */ 25 | export const reqDelGoods = ({ _id }) => request({ 26 | url: `/commodities/${_id}`, 27 | method: 'DELETE' 28 | }) 29 | 30 | /** 31 | * 获取商品服务 32 | */ 33 | export const reqGoodsServices = (query) => request(`/services?query=${JSON.stringify(query)}`) 34 | 35 | /** 36 | * 添加/修改商品服务 37 | */ 38 | export const reqAddUpdateGoodsService = ({ name, message, icon, _id }) => request({ 39 | url: `/services${_id ? `/${_id}` : ''}`, 40 | method: `${_id ? 'PUT' : 'POST'}`, 41 | data: { 42 | name, 43 | message, 44 | icon 45 | }, 46 | headers: { 47 | checkToken: true 48 | } 49 | }) 50 | 51 | /** 52 | * 删除商品服务 53 | */ 54 | export const reqDelGoodsService = ({ _id }) => request({ 55 | url: `/services/${_id}`, 56 | method: 'DELETE' 57 | }) -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 小程序商城后台管理系统 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "name": "", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon-", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "182088", 10 | "name": "商家", 11 | "font_class": "shangjia", 12 | "unicode": "e6bd", 13 | "unicode_decimal": 59069 14 | }, 15 | { 16 | "icon_id": "479390", 17 | "name": "订单管理", 18 | "font_class": "dingdanguanli", 19 | "unicode": "e60e", 20 | "unicode_decimal": 58894 21 | }, 22 | { 23 | "icon_id": "689262", 24 | "name": "类目 品类 分类 类别", 25 | "font_class": "leimupinleifenleileibie", 26 | "unicode": "e7f9", 27 | "unicode_decimal": 59385 28 | }, 29 | { 30 | "icon_id": "1567679", 31 | "name": "user", 32 | "font_class": "user", 33 | "unicode": "e6b2", 34 | "unicode_decimal": 59058 35 | }, 36 | { 37 | "icon_id": "2848763", 38 | "name": "权限管理", 39 | "font_class": "quanxianguanli", 40 | "unicode": "e641", 41 | "unicode_decimal": 58945 42 | }, 43 | { 44 | "icon_id": "7155561", 45 | "name": "密码", 46 | "font_class": "mima", 47 | "unicode": "e658", 48 | "unicode_decimal": 58968 49 | }, 50 | { 51 | "icon_id": "9851598", 52 | "name": "活动", 53 | "font_class": "huodong", 54 | "unicode": "e611", 55 | "unicode_decimal": 58897 56 | }, 57 | { 58 | "icon_id": "11903650", 59 | "name": "toggle-left", 60 | "font_class": "toggle-left", 61 | "unicode": "e6f5", 62 | "unicode_decimal": 59125 63 | }, 64 | { 65 | "icon_id": "11903653", 66 | "name": "toggle-right", 67 | "font_class": "toggle-right", 68 | "unicode": "e6f6", 69 | "unicode_decimal": 59126 70 | } 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /src/styles/siderbar.scss: -------------------------------------------------------------------------------- 1 | // mobile responsive 2 | #app { 3 | .el-menu--collapse .el-menu .el-submenu { 4 | min-width: 64px !important; 5 | } 6 | .sidebar-container { 7 | // transition: width 0.28s; 8 | width: 256px !important; 9 | height: 100%; 10 | position: fixed; 11 | font-size: 0px; 12 | top: 0; 13 | bottom: 0; 14 | left: 0; 15 | z-index: 1001; 16 | // overflow: hidden; 17 | & .el-submenu .el-menu-item { 18 | min-width: 64px !important; 19 | } 20 | } 21 | // .withoutAnimation { 22 | // .sidebar-container { 23 | // transition: none !important; 24 | // } 25 | // } 26 | .main-container { 27 | min-height: 100%; 28 | transition: margin-left 0.28s; 29 | margin-left: 256px; 30 | position: relative; 31 | } 32 | .hideSidebar { 33 | .sidebar-container { 34 | width: 64px !important; 35 | } 36 | .main-container { 37 | margin-left: 64px; 38 | } 39 | } 40 | .mobile { 41 | .main-container { 42 | margin-left: 0px; 43 | } 44 | .sidebar-container { 45 | transition: transform 0.28s; 46 | width: 256px !important; 47 | } 48 | 49 | &.hideSidebar { 50 | .sidebar-container { 51 | pointer-events: none; 52 | // transition-duration: 0.3s; 53 | transform: translate3d(-256px, 0, 0); 54 | } 55 | } 56 | } 57 | 58 | .withoutAnimation { 59 | .main-container, 60 | .sidebar-container { 61 | transition: none; 62 | } 63 | } 64 | 65 | .hideSidebar { 66 | .sidebar-container { 67 | .el-menu-item.is-active { 68 | color: rgb(64, 158, 255) !important; 69 | background-color: rgb(0, 21, 41) !important; 70 | } 71 | } 72 | } 73 | 74 | .openSidebar { 75 | .el-menu-item.is-active { 76 | background-color: #409eff !important; 77 | color: #fff !important; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/assets/fonts/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "", 3 | "name": "", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon-", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "182088", 10 | "name": "商家", 11 | "font_class": "shangjia", 12 | "unicode": "e6bd", 13 | "unicode_decimal": 59069 14 | }, 15 | { 16 | "icon_id": "479390", 17 | "name": "订单管理", 18 | "font_class": "dingdanguanli", 19 | "unicode": "e60e", 20 | "unicode_decimal": 58894 21 | }, 22 | { 23 | "icon_id": "689262", 24 | "name": "类目 品类 分类 类别", 25 | "font_class": "leimupinleifenleileibie", 26 | "unicode": "e7f9", 27 | "unicode_decimal": 59385 28 | }, 29 | { 30 | "icon_id": "1567679", 31 | "name": "user", 32 | "font_class": "user", 33 | "unicode": "e6b2", 34 | "unicode_decimal": 59058 35 | }, 36 | { 37 | "icon_id": "2848763", 38 | "name": "权限管理", 39 | "font_class": "quanxianguanli", 40 | "unicode": "e641", 41 | "unicode_decimal": 58945 42 | }, 43 | { 44 | "icon_id": "7155561", 45 | "name": "密码", 46 | "font_class": "mima", 47 | "unicode": "e658", 48 | "unicode_decimal": 58968 49 | }, 50 | { 51 | "icon_id": "9851598", 52 | "name": "活动", 53 | "font_class": "huodong", 54 | "unicode": "e611", 55 | "unicode_decimal": 58897 56 | }, 57 | { 58 | "icon_id": "11683673", 59 | "name": "评论", 60 | "font_class": "ziyuan", 61 | "unicode": "e608", 62 | "unicode_decimal": 58888 63 | }, 64 | { 65 | "icon_id": "11903650", 66 | "name": "toggle-left", 67 | "font_class": "toggle-left", 68 | "unicode": "e6f5", 69 | "unicode_decimal": 59125 70 | }, 71 | { 72 | "icon_id": "11903653", 73 | "name": "toggle-right", 74 | "font_class": "toggle-right", 75 | "unicode": "e6f6", 76 | "unicode_decimal": 59126 77 | } 78 | ] 79 | } 80 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | 4 | /** 5 | * 登录接口 6 | */ 7 | export const login = ({ username, password }) => request({ 8 | url: '/auth/login', 9 | method: 'POST', 10 | data: { 11 | username, 12 | password 13 | } 14 | }) 15 | 16 | /** 17 | * 根据token获取用户信息 18 | */ 19 | export const getUserInfo = () => request({ 20 | url: '/auth/user', 21 | headers: { 22 | checkToken: true 23 | } 24 | }) 25 | 26 | /** 27 | * 获取所有后台系统的用户 28 | */ 29 | export const getAdminUser = (query) => request({ 30 | url: `/auth/users?query=${JSON.stringify(query)}`, 31 | headers: { 32 | checkToken: true 33 | } 34 | }) 35 | 36 | /** 37 | * 添加用户 38 | */ 39 | export const addUser = ({ username, password, roles, permission }) => request({ 40 | url: '/auth/register', 41 | method: 'POST', 42 | data: { 43 | username, 44 | password, 45 | roles, 46 | permission 47 | }, 48 | }) 49 | 50 | /** 51 | * 修改用户 52 | */ 53 | export const updateUser = ({ username, password, roles, permission, _id }) => request({ 54 | url: `/auth/users/${_id}`, 55 | method: 'PUT', 56 | data: { 57 | username, 58 | password, 59 | roles, 60 | permission 61 | }, 62 | headers: { 63 | checkToken: true 64 | } 65 | }) 66 | 67 | /** 68 | * 删除用户 69 | */ 70 | export const delUser = ({ _id }) => request({ 71 | url: `/auth/users/${_id}`, 72 | method: 'DELETE', 73 | headers: { 74 | checkToken: true 75 | } 76 | }) 77 | 78 | /** 79 | * 获取角色列表 80 | */ 81 | export const reqRoles = (query) => request({ 82 | url: `/roles?query=${JSON.stringify(query)}` 83 | }) 84 | 85 | /** 86 | * 添加角色 87 | */ 88 | export const reqAddRole = ({ name, description, status, permission, _id }) => request({ 89 | url: `/roles${_id ? `/${_id}` : ''}`, 90 | method: `${_id ? 'PUT' : 'POST'}`, 91 | data: { 92 | name, 93 | description, 94 | status, 95 | permission 96 | } 97 | }) 98 | 99 | /** 100 | * 删除角色 101 | */ 102 | export const reqDelRole = ({ _id }) => request({ 103 | url: `/roles/${_id}`, 104 | method: 'DELETE', 105 | }) -------------------------------------------------------------------------------- /src/components/Aside/Item.vue: -------------------------------------------------------------------------------- 1 | 24 | 82 | -------------------------------------------------------------------------------- /src/views/home/Home.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /src/utils/request.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 封装axios的模块 3 | */ 4 | import Vue from 'vue' 5 | import axios from 'axios'; 6 | import router from '@/router' 7 | import store from '@/vuex/store' 8 | import { serverURL } from '@/utils/serverConfig' 9 | import { Message } from 'element-ui' 10 | 11 | const instance = axios.create({ 12 | baseURL: serverURL 13 | }) 14 | 15 | // 存放axios的cancel对象,用于取消请求 16 | Vue.prototype._axiosPromiseArr = [] 17 | 18 | /** 19 | * 添加请求拦截器, 处理请求参数问题以及token问题 20 | */ 21 | instance.interceptors.request.use((config) => { 22 | // 处理请求参数 23 | // debugger 24 | if (config.data && (config.data instanceof FormData) === false) { 25 | config.data = JSON.parse(JSON.stringify(config.data)) 26 | for (let item in config.data) { 27 | if (config.data[item] === "") { 28 | delete config.data[item] 29 | } 30 | } 31 | } 32 | 33 | /** 34 | * new一个Cancel对象,并push到Vue.prototype._axiosPromiseArr中 35 | */ 36 | config.cancelToken = new axios.CancelToken(cancel => { 37 | Vue.prototype._axiosPromiseArr.push(cancel) 38 | }) 39 | 40 | // 处理token 41 | const token = store.state.adminUser.token 42 | if (token) { 43 | config.headers['Authorization'] = token 44 | } else { 45 | if (config.headers.checkToken) { 46 | throw new Error('授权失败,请重新登录') 47 | } 48 | } 49 | return config 50 | }) 51 | /** 52 | * 添加响应拦截器 53 | * 成功回调: 成功的结果不再是response, 而是response.data 54 | * 失败回调: 统一处理请求异常 55 | */ 56 | instance.interceptors.response.use( 57 | response => { 58 | // debugger 59 | return response.data 60 | }, 61 | error => { 62 | // debugger 63 | // 1. 没有token直接发请求的错误 64 | if (!error.response) { 65 | // 取消请求特殊处理 66 | if (error.message.code === 600) { 67 | Message.warning(error.message.msg) 68 | return; 69 | } 70 | // 提示错误, 并且跳转到登录页 71 | Message.error('没有授权, 请重新登录', error.message) 72 | store.dispatch('logout') 73 | router.replace('/login') 74 | } else { 75 | // 2. 发了请求, 但账号密码错误 76 | if (error.response.status === 400) return Promise.reject(error) // 返回一个reject状态的promise 77 | if (error.response.status === 401) { 78 | // 3. 发了请求, 但token失效了 79 | store.dispatch('logout') 80 | Message.error('授权失败,请重新登录,错误码:' + error.response.status) 81 | router.replace('/login') 82 | } else if (error.response.status === 404) { 83 | // 4. 发了请求, 资源不存在404 84 | Message.error('您请求的资源不存在,错误码:' + error.response.status) 85 | } else if (error.response.status === 500) { 86 | Message.error('服务器内部错误,错误码:' + error.response.status) 87 | } else { 88 | Message.error('请求错误,请检查网络后重试,错误码:' + error.response.status) 89 | } 90 | } 91 | return new Promise(() => { }) // 返回一个pedding状态的promise 92 | } 93 | ) 94 | 95 | export default instance; -------------------------------------------------------------------------------- /src/assets/css/reset.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Eric Meyer's Reset CSS v2.0 (http://meyerweb.com/eric/tools/css/reset/) 3 | * http://cssreset.com 4 | */ 5 | html, 6 | body, 7 | div, 8 | span, 9 | applet, 10 | object, 11 | iframe, 12 | h1, 13 | h2, 14 | h3, 15 | h4, 16 | h5, 17 | h6, 18 | p, 19 | blockquote, 20 | pre, 21 | a, 22 | abbr, 23 | acronym, 24 | address, 25 | big, 26 | cite, 27 | code, 28 | del, 29 | dfn, 30 | em, 31 | img, 32 | ins, 33 | kbd, 34 | q, 35 | s, 36 | samp, 37 | small, 38 | strike, 39 | strong, 40 | sub, 41 | sup, 42 | tt, 43 | var, 44 | b, 45 | u, 46 | i, 47 | center, 48 | dl, 49 | dt, 50 | dd, 51 | ol, 52 | ul, 53 | li, 54 | fieldset, 55 | form, 56 | label, 57 | legend, 58 | table, 59 | caption, 60 | tbody, 61 | tfoot, 62 | thead, 63 | tr, 64 | th, 65 | td, 66 | article, 67 | aside, 68 | canvas, 69 | details, 70 | embed, 71 | figure, 72 | figcaption, 73 | footer, 74 | header, 75 | menu, 76 | nav, 77 | output, 78 | ruby, 79 | section, 80 | summary, 81 | time, 82 | mark, 83 | audio, 84 | video, 85 | input { 86 | margin: 0; 87 | padding: 0; 88 | border: 0; 89 | font-size: 100%; 90 | font-weight: normal; 91 | vertical-align: baseline; 92 | } 93 | 94 | /* HTML5 display-role reset for older browsers */ 95 | article, 96 | aside, 97 | details, 98 | figcaption, 99 | figure, 100 | footer, 101 | header, 102 | menu, 103 | nav, 104 | section { 105 | display: block; 106 | } 107 | 108 | body { 109 | line-height: 1; 110 | } 111 | 112 | blockquote, 113 | q { 114 | quotes: none; 115 | } 116 | 117 | blockquote:before, 118 | blockquote:after, 119 | q:before, 120 | q:after { 121 | content: none; 122 | } 123 | 124 | table { 125 | border-collapse: collapse; 126 | border-spacing: 0; 127 | } 128 | 129 | /* custom */ 130 | a { 131 | color: #7e8c8d; 132 | text-decoration: none; 133 | -webkit-backface-visibility: hidden; 134 | } 135 | 136 | li { 137 | list-style: none; 138 | } 139 | 140 | ::-webkit-scrollbar { 141 | width: 5px; 142 | height: 5px; 143 | } 144 | 145 | ::-webkit-scrollbar-track-piece { 146 | background-color: rgba(0, 0, 0, 0.2); 147 | -webkit-border-radius: 6px; 148 | } 149 | 150 | ::-webkit-scrollbar-thumb:vertical { 151 | height: 5px; 152 | background-color: rgba(125, 125, 125, 0.7); 153 | -webkit-border-radius: 6px; 154 | } 155 | 156 | ::-webkit-scrollbar-thumb:horizontal { 157 | width: 5px; 158 | background-color: rgba(125, 125, 125, 0.7); 159 | -webkit-border-radius: 6px; 160 | } 161 | 162 | #app, 163 | html, 164 | body { 165 | width: 100%; 166 | height: 100%; 167 | /* min-width: 1024px; */ 168 | } 169 | 170 | body { 171 | -webkit-text-size-adjust: none; 172 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 173 | } 174 | 175 | 176 | 177 | /*显示省略号*/ 178 | .ellipsis { 179 | overflow: hidden; 180 | text-overflow: ellipsis; 181 | white-space: nowrap; 182 | } 183 | -------------------------------------------------------------------------------- /src/config/menuConfig.js: -------------------------------------------------------------------------------- 1 | const menuList = [ 2 | // { 3 | // title: '首页', // 菜单标题名称 4 | // key: '/home', // 对应的path 5 | // icon: 'el-icon-s-home', // 图标名称 6 | // public: true, // 公开的 7 | // }, 8 | { 9 | title: '轮播图', 10 | key: '/banner', 11 | icon: 'el-icon-picture', 12 | children: [ // 子菜单列表 13 | { 14 | title: '首页轮播图', 15 | key: '/banner/bannerlist', 16 | icon: 'el-icon-picture' 17 | }, 18 | ] 19 | }, 20 | { 21 | title: '分类管理', 22 | key: '/category', 23 | icon: 'el-icon- iconfont icon-leimupinleifenleileibie', 24 | children: [ // 子菜单列表 25 | { 26 | title: '分类列表', 27 | key: '/category/categorylist', 28 | icon: '' 29 | }, 30 | ] 31 | }, 32 | { 33 | title: '商品管理', 34 | key: '/goods', 35 | icon: 'el-icon-goods', 36 | children: [ // 子菜单列表 37 | { 38 | title: '商品列表', 39 | key: '/goods/goodslist', 40 | icon: '' 41 | }, 42 | { 43 | title: '商品服务', 44 | key: '/goods/goodsservice', 45 | icon: '' 46 | }, 47 | ] 48 | }, 49 | { 50 | title: '商家管理', 51 | key: '/merchant', 52 | icon: 'el-icon- iconfont icon-shangjia', 53 | children: [ // 子菜单列表 54 | { 55 | title: '店铺列表', 56 | key: '/metchant/merchantlist', 57 | icon: '' 58 | }, 59 | ] 60 | }, 61 | { 62 | title: '权限管理', 63 | key: '/auth', 64 | icon: 'el-icon- iconfont icon-quanxianguanli', 65 | children: [ // 子菜单列表 66 | { 67 | title: '用户管理', 68 | key: '/auth/userlist', 69 | icon: '' 70 | }, 71 | ] 72 | }, 73 | { 74 | title: '活动管理', 75 | key: '/active', 76 | icon: 'el-icon- iconfont icon-huodong', 77 | children: [ // 子菜单列表 78 | { 79 | title: '用户管理', 80 | key: '/active/activelist', 81 | icon: '' 82 | }, 83 | ] 84 | }, 85 | { 86 | title: '订单管理', 87 | key: '/order', 88 | icon: 'el-icon- iconfont icon-dingdanguanli', 89 | children: [ // 子菜单列表 90 | { 91 | title: '订单列表', 92 | key: '/order/orderlist', 93 | icon: '' 94 | }, 95 | ] 96 | }, 97 | { 98 | title: '评论管理', 99 | key: '/comment', 100 | icon: 'el-icon- iconfont icon-ziyuan', 101 | children: [ // 子菜单列表 102 | { 103 | title: '评论列表', 104 | key: '/comment/commentlist', 105 | icon: '' 106 | }, 107 | ] 108 | }, 109 | ] 110 | 111 | export default menuList -------------------------------------------------------------------------------- /src/vuex/modules/goods.js: -------------------------------------------------------------------------------- 1 | import { reqActiveList } from 'api/activity'; 2 | import { reqGoods, reqGoodsServices } from 'api/goods'; 3 | import { reqCategories } from 'api/category'; 4 | import { reqMerchants } from 'api/merchant'; 5 | import { reqOrders } from 'api/order'; 6 | import { reqComment } from 'api/comment'; 7 | 8 | import { 9 | RECEIVE_ACTIVE_LIST, 10 | RECEIVE_GOODS_LIST, 11 | RECEIVE_GOODS, 12 | RECEIVE_GOODS_SERVICE, 13 | RECEIVE_CATEGORIES, 14 | RECEIVE_MERCHANTS, 15 | RECEIVE_ORDERS, 16 | RECEIVE_BACK_ROUTE_PATH, 17 | RECEIVE_COMMENTS 18 | } from '../mutation-types'; 19 | 20 | const state = { 21 | activeList: [], 22 | activeTotal: 0, 23 | goodsList: null, 24 | goodsTotal: 0, 25 | currentGoods: JSON.parse(sessionStorage.getItem('goods') || '{}'), 26 | goodsService: null, 27 | servicesTotal: 0, 28 | categoryList: [], 29 | categoryTotal: 0, 30 | merchantList: [], 31 | merchantTotal: 0, 32 | orderList: null, 33 | orderTotal: 0, 34 | backRoutePath: sessionStorage.getItem('backRoutePath') || "", 35 | commentList: null, 36 | commentTotal: 0 37 | }; 38 | 39 | 40 | const actions = { 41 | async getActiveList({ commit }, query) { 42 | const result = await reqActiveList(query); 43 | commit(RECEIVE_ACTIVE_LIST, result); 44 | }, 45 | async getGoods({ commit }, query) { 46 | const result = await reqGoods(query) 47 | commit(RECEIVE_GOODS_LIST, result); 48 | }, 49 | saveGoods({ commit }, goods) { 50 | commit(RECEIVE_GOODS, goods); 51 | }, 52 | async getGoodsService({ commit }, query) { 53 | const result = await reqGoodsServices(query); 54 | commit(RECEIVE_GOODS_SERVICE, result); 55 | }, 56 | async getCategory({ commit }, query) { 57 | const result = await reqCategories(query); 58 | commit(RECEIVE_CATEGORIES, result) 59 | }, 60 | async getMerchants({ commit }, query) { 61 | const result = await reqMerchants(query); 62 | commit(RECEIVE_MERCHANTS, result); 63 | }, 64 | async getOrders({ commit }, query) { 65 | const result = await reqOrders(query); 66 | commit(RECEIVE_ORDERS, result); 67 | }, 68 | async getComments({ commit }, query) { 69 | const result = await reqComment(query); 70 | commit(RECEIVE_COMMENTS, result); 71 | }, 72 | 73 | }; 74 | 75 | const mutations = { 76 | [RECEIVE_ACTIVE_LIST](state, { total, data }) { 77 | state.activeTotal = total; 78 | state.activeList = data; 79 | }, 80 | [RECEIVE_GOODS_LIST](state, { total, data }) { 81 | state.goodsList = data; 82 | state.goodsTotal = total; 83 | }, 84 | [RECEIVE_GOODS](state, goods) { 85 | sessionStorage.removeItem('goods') 86 | state.currentGoods = goods; 87 | sessionStorage.setItem('goods', JSON.stringify(goods)); 88 | }, 89 | [RECEIVE_GOODS_SERVICE](state, { data, total }) { 90 | state.goodsService = data; 91 | state.servicesTotal = total; 92 | }, 93 | [RECEIVE_CATEGORIES](state, { data, total }) { 94 | state.categoryList = data; 95 | state.categoryTotal = total; 96 | }, 97 | [RECEIVE_MERCHANTS](state, { data, total }) { 98 | state.merchantList = data; 99 | state.merchantTotal = total; 100 | }, 101 | [RECEIVE_ORDERS](state, { data, total }) { 102 | state.orderList = data; 103 | state.orderTotal = total; 104 | }, 105 | [RECEIVE_BACK_ROUTE_PATH](state, path) { 106 | state.backRoutePath = path; 107 | sessionStorage.setItem('backRoutePath', path); 108 | }, 109 | [RECEIVE_COMMENTS](state, { data, total }) { 110 | state.commentList = data; 111 | state.commentTotal = total; 112 | } 113 | }; 114 | 115 | const getters = {}; 116 | 117 | export default { 118 | state, 119 | mutations, 120 | actions, 121 | getters 122 | }; -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1583412488775'); /* IE9 */ 3 | src: url('iconfont.eot?t=1583412488775#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1583412488775') format('woff'), 6 | url('iconfont.ttf?t=1583412488775') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1583412488775#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-shangjia:before { 19 | content: "\e6bd"; 20 | } 21 | 22 | .icon-dingdanguanli:before { 23 | content: "\e60e"; 24 | } 25 | 26 | .icon-leimupinleifenleileibie:before { 27 | content: "\e7f9"; 28 | } 29 | 30 | .icon-user:before { 31 | content: "\e6b2"; 32 | } 33 | 34 | .icon-quanxianguanli:before { 35 | content: "\e641"; 36 | } 37 | 38 | .icon-mima:before { 39 | content: "\e658"; 40 | } 41 | 42 | .icon-huodong:before { 43 | content: "\e611"; 44 | } 45 | 46 | .icon-toggle-left:before { 47 | content: "\e6f5"; 48 | } 49 | 50 | .icon-toggle-right:before { 51 | content: "\e6f6"; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /src/vuex/modules/admin-user.js: -------------------------------------------------------------------------------- 1 | import { 2 | RECEIVE_ADMIN_USER, 3 | RECEIVE_TOKEN, 4 | LOGOUT, 5 | RECEIVE_USER_LIST, 6 | RECEIVE_ROLE_LIST 7 | } from '../mutation-types' 8 | 9 | import { 10 | getAdminUser, 11 | getUserInfo, 12 | reqRoles 13 | } from 'api/user' 14 | 15 | import storageUtils from '@/utils/storageUtils' 16 | 17 | const state = { 18 | token: storageUtils.getToken(), // 登录token标识, 19 | adminUser: {}, 20 | adminUserList: null, 21 | adminUserListTotal: 0, 22 | menus: [], 23 | roleList: null, 24 | roleTotal: 0 25 | } 26 | /** 27 | * 包含同步action和异步action 28 | */ 29 | const actions = { 30 | saveToken({ commit }, token) { 31 | storageUtils.saveToken(token) 32 | commit(RECEIVE_TOKEN, { token }) 33 | }, 34 | async saveUser({ commit }) { 35 | const user = await getUserInfo(); 36 | const menus = []; 37 | user.permission.forEach(item => { 38 | switch (item.path) { 39 | case 'banners': 40 | if (!menus.find(item => item === '/banner')) { 41 | menus.push('/banner') 42 | menus.push('/banner/bannerlist') 43 | } 44 | break; 45 | case 'actions': 46 | if (!menus.find(item => item === '/active')) { 47 | menus.push('/active') 48 | menus.push('/active/activelist') 49 | } 50 | 51 | break; 52 | case 'categories': 53 | if (!menus.find(item => item === '/category')) { 54 | menus.push('/category') 55 | menus.push('/category/categorylist') 56 | } 57 | 58 | break; 59 | case 'comments': 60 | if (!menus.find(item => item === '/comment')) { 61 | menus.push('/comment') 62 | menus.push('/comment/commentlist') 63 | } 64 | break; 65 | case 'commodities': 66 | if (!menus.find(item => item === '/goods')) { 67 | menus.push('/goods') 68 | menus.push('/goods/goodslist') 69 | } 70 | break; 71 | case 'orders': 72 | if (!menus.find(item => item === '/order')) { 73 | menus.push('/order') 74 | menus.push('/order/orderlist') 75 | } 76 | break; 77 | case 'selles': 78 | if (!menus.find(item => item === '/merchant')) { 79 | menus.push('/merchant') 80 | menus.push('/merchant/merchantlist') 81 | } 82 | break; 83 | case 'services': 84 | if (!menus.find(item => item === '/goods/goodsservice')) { 85 | menus.push('/goods/goodsservice') 86 | } 87 | break; 88 | case 'roles': 89 | if (!menus.find(item => item === '/auth')) { 90 | menus.push('/auth') 91 | menus.push('/auth/rolelist') 92 | } 93 | break; 94 | default: 95 | break; 96 | } 97 | }) 98 | commit(RECEIVE_ADMIN_USER, { user, menus }) 99 | }, 100 | logout({ commit }) { 101 | storageUtils.removeToken() 102 | commit(LOGOUT) 103 | }, 104 | async getUserList({ commit }, query) { 105 | const result = await getAdminUser(query) 106 | commit(RECEIVE_USER_LIST, result) 107 | }, 108 | async getRoleList({ commit }, query) { 109 | const result = await reqRoles(query) 110 | commit(RECEIVE_ROLE_LIST, result) 111 | } 112 | } 113 | 114 | const mutations = { 115 | [RECEIVE_TOKEN](state, { token }) { 116 | state.token = token 117 | }, 118 | [RECEIVE_ADMIN_USER](state, { user, menus }) { 119 | state.menus = menus 120 | state.adminUser = user 121 | }, 122 | [LOGOUT](state) { 123 | state.token = '' 124 | state.adminUser = {} 125 | }, 126 | [RECEIVE_USER_LIST](state, { data, total }) { 127 | state.adminUserList = data; 128 | state.adminUserListTotal = total; 129 | }, 130 | [RECEIVE_ROLE_LIST](state, { data, total }) { 131 | state.roleList = data; 132 | state.roleTotal = total; 133 | } 134 | } 135 | 136 | 137 | 138 | const getters = {} 139 | 140 | export default { 141 | state, 142 | mutations, 143 | actions, 144 | getters 145 | } -------------------------------------------------------------------------------- /src/assets/fonts/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: "iconfont"; 2 | src: url('iconfont.eot?t=1583474376955'); /* IE9 */ 3 | src: url('iconfont.eot?t=1583474376955#iefix') format('embedded-opentype'), /* IE6-IE8 */ 4 | url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2'), 5 | url('iconfont.woff?t=1583474376955') format('woff'), 6 | url('iconfont.ttf?t=1583474376955') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */ 7 | url('iconfont.svg?t=1583474376955#iconfont') format('svg'); /* iOS 4.1- */ 8 | } 9 | 10 | .iconfont { 11 | font-family: "iconfont" !important; 12 | font-size: 16px; 13 | font-style: normal; 14 | -webkit-font-smoothing: antialiased; 15 | -moz-osx-font-smoothing: grayscale; 16 | } 17 | 18 | .icon-shangjia:before { 19 | content: "\e6bd"; 20 | } 21 | 22 | .icon-dingdanguanli:before { 23 | content: "\e60e"; 24 | } 25 | 26 | .icon-leimupinleifenleileibie:before { 27 | content: "\e7f9"; 28 | } 29 | 30 | .icon-user:before { 31 | content: "\e6b2"; 32 | } 33 | 34 | .icon-quanxianguanli:before { 35 | content: "\e641"; 36 | } 37 | 38 | .icon-mima:before { 39 | content: "\e658"; 40 | } 41 | 42 | .icon-huodong:before { 43 | content: "\e611"; 44 | } 45 | 46 | .icon-ziyuan:before { 47 | content: "\e608"; 48 | } 49 | 50 | .icon-toggle-left:before { 51 | content: "\e6f5"; 52 | } 53 | 54 | .icon-toggle-right:before { 55 | content: "\e6f6"; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /src/components/Header/Header.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 99 | 100 | -------------------------------------------------------------------------------- /src/views/login/Login.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 111 | 112 | 162 | -------------------------------------------------------------------------------- /src/components/Aside/Aside.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 96 | 97 | -------------------------------------------------------------------------------- /src/views/goods/GoodsList.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 152 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | const Home = () => import('views/home/Home'); 2 | const Banner = () => import('views/banner/Banner'); 3 | const BannerList = () => import('views/banner/BannerList'); 4 | const Login = () => import('views/login/Login'); 5 | const Category = () => import('views/category/Category'); 6 | const CategoryList = () => import('views/category/CategoryList'); 7 | const Goods = () => import('views/goods/Goods'); 8 | const Acitve = () => import('views/activity/Activity'); 9 | const AcitveList = () => import('views/activity/ActivityList'); 10 | const Auth = () => import('views/auth/Auth'); 11 | const UserList = () => import('views/auth/UserList'); 12 | const RoleList = () => import('views/auth/RoleList'); 13 | const AddCategory = () => import('views/category/AddCategory'); 14 | const GoodsList = () => import('views/goods/GoodsList'); 15 | const Detail = () => import('views/goods/Detail'); 16 | const GoodsService = () => import("views/goods/GoodsService"); 17 | const AddUpdateGoods = () => import('views/goods/AddUpdateGoods'); 18 | const Merchant = () => import('views/merchant/Merchant'); 19 | const MerchantList = () => import('views/merchant/MerchantList'); 20 | const Order = () => import('views/order/Order') 21 | const OrderList = () => import('views/order/OrderList') 22 | const Comment = () => import('views/comment/Comment') 23 | const CommentList = () => import('views/comment/CommentList') 24 | 25 | export default [ 26 | { path: '/', redirect: '/login' }, 27 | { path: '/login', component: Login }, 28 | { 29 | path: '/home', 30 | name: 'home', 31 | component: Home, 32 | meta: { title: '首页' }, 33 | children: [ 34 | { 35 | path: '/banner', 36 | component: Banner, 37 | redirect: '/banner/bannerlist', 38 | meta: { title: '轮播图' }, 39 | children: [ 40 | { 41 | path: '/banner/bannerlist', 42 | component: BannerList, 43 | meta: { title: '首页轮播图' }, 44 | } 45 | ] 46 | }, 47 | { 48 | path: '/category', 49 | component: Category, 50 | redirect: '/category/categorylist', 51 | meta: { title: '分类管理' }, 52 | children: [ 53 | { 54 | path: '/category/categorylist', 55 | component: CategoryList, 56 | meta: { 57 | title: '分类列表', 58 | activeMenu: '/categorylist', 59 | activeChildMenu: '/category/addcategory' 60 | } 61 | }, 62 | { 63 | path: '/category/addcategory', 64 | name: 'addCategory', 65 | component: AddCategory, 66 | meta: { 67 | title: '添加分类', 68 | activeMenu: '/category', 69 | activeChildMenu: '/category/addcategory' 70 | } 71 | }, 72 | ] 73 | }, 74 | { 75 | path: '/auth', 76 | component: Auth, 77 | redirect: '/auth/userlist', 78 | meta: { title: '权限管理' }, 79 | children: [ 80 | { 81 | path: '/auth/userlist', 82 | component: UserList, 83 | meta: { title: '用户管理' }, 84 | }, 85 | { 86 | path: '/auth/rolelist', 87 | component: RoleList, 88 | meta: { title: '角色管理' }, 89 | }, 90 | 91 | ] 92 | }, 93 | { 94 | path: '/active', 95 | component: Acitve, 96 | redirect: '/active/activelist', 97 | meta: { title: '活动管理' }, 98 | children: [{ 99 | path: '/active/activelist', 100 | component: AcitveList, 101 | meta: { title: '首页活动' }, 102 | }] 103 | 104 | }, 105 | { 106 | path: '/goods', 107 | component: Goods, 108 | redirect: '/goods/goodslist', 109 | meta: { title: '商品管理' }, 110 | children: [ 111 | { 112 | path: '/goods/goodslist', 113 | component: GoodsList, 114 | meta: { title: '商品列表' }, 115 | }, 116 | { 117 | path: '/goods/detail', 118 | name: 'detail', 119 | component: Detail, 120 | meta: { title: '商品详情' }, 121 | }, 122 | { 123 | path: '/goods/goodsservice', 124 | component: GoodsService, 125 | meta: { title: '商品服务' }, 126 | }, 127 | { 128 | path: '/goods/addupdategoods', 129 | name: 'addUpdateGoods', 130 | component: AddUpdateGoods, 131 | }, 132 | ] 133 | }, 134 | 135 | { 136 | path: '/merchant', 137 | component: Merchant, 138 | redirect: '/merchant/merchantlist', 139 | meta: { title: '商家管理' }, 140 | children: [ 141 | { 142 | path: '/merchant/merchantlist', 143 | component: MerchantList, 144 | meta: { title: '店铺列表' }, 145 | }, 146 | ] 147 | }, 148 | { 149 | path: '/order', 150 | component: Order, 151 | redirect: '/order/orderlist', 152 | meta: { title: '订单管理' }, 153 | children: [ 154 | { 155 | path: '/order/orderlist', 156 | component: OrderList, 157 | meta: { title: '订单列表' }, 158 | }, 159 | ] 160 | }, 161 | { 162 | path: '/comment', 163 | component: Comment, 164 | redirect: '/comment/commentlist', 165 | meta: { title: '评论管理' }, 166 | children: [ 167 | { 168 | path: '/comment/commentlist', 169 | component: CommentList, 170 | meta: { title: '评论列表' }, 171 | }, 172 | ] 173 | } 174 | ] 175 | }, 176 | ] -------------------------------------------------------------------------------- /src/views/category/CategoryList.vue: -------------------------------------------------------------------------------- 1 | 81 | 82 | -------------------------------------------------------------------------------- /src/views/goods/Detail.vue: -------------------------------------------------------------------------------- 1 | 115 | 116 | 117 | 158 | 159 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /src/vuex/modules/auth-config.js: -------------------------------------------------------------------------------- 1 | import { PERMISSION_MAPPING_VIEW } from '../mutation-types' 2 | import { CLEAR_CACHE } from '../mutation-types' 3 | import { getUserInfo } from 'api/user' 4 | 5 | const state = { 6 | cache: false, 7 | cacheData: {}, 8 | mandatoryRefresh: false, 9 | menuList: [], 10 | activity: {}, 11 | banner: {}, 12 | category: {}, 13 | comment: {}, 14 | goods: {}, 15 | order: {}, 16 | merchant: {}, 17 | services: {}, 18 | role: {}, 19 | auth: {}, 20 | } 21 | 22 | //权限 -> 对应视图是否渲染 23 | /** 24 | * 25 | * data 26 | * menuList : [] 27 | * banner : {} 28 | * category : {} 29 | * goods : {} 30 | * service : {} 31 | * order : {} 32 | * merchant : {} 33 | * comment : {} 34 | * auth : {} 35 | * activity : {} 36 | * role : null 37 | */ 38 | const permissionMappingView = (data) => { 39 | 40 | let mapObj = { 41 | actions: 'activity', 42 | banners: 'banner', 43 | categories: 'category', 44 | comments: 'comment', 45 | commodities: 'goods', 46 | orders: 'order', 47 | selles: 'merchant', 48 | services: 'services', 49 | roles: 'role' 50 | } 51 | 52 | //合并 去重 53 | data.roles.forEach(role => { 54 | role.permission.forEach(permission => { 55 | var has = data.permission.some(item => { 56 | return item._id == permission._id 57 | }) 58 | if (!has) 59 | data.permission.push(permission) 60 | }) 61 | }) 62 | 63 | 64 | data.permission.forEach(item => { 65 | switch (item.method) { 66 | case 'get': state[mapObj[item.path]].find = true 67 | break 68 | case 'post': state[mapObj[item.path]].create = true 69 | break 70 | case 'put': state[mapObj[item.path]].update = true 71 | break 72 | case 'delete': state[mapObj[item.path]].delete = true 73 | break 74 | } 75 | // console.log('重求', state) 76 | }) 77 | 78 | let menuListMapObj = { 79 | auth: { 80 | title: '权限管理', 81 | key: '/auth', 82 | icon: 'el-icon- iconfont icon-quanxianguanli', 83 | children: [ // 子菜单列表 84 | { 85 | title: '用户管理', 86 | key: '/auth/userlist', 87 | icon: '' 88 | }, 89 | ] 90 | }, 91 | activity: { 92 | title: '活动管理', 93 | key: '/active', 94 | icon: 'el-icon- iconfont icon-huodong', 95 | children: [ // 子菜单列表 96 | { 97 | title: '活动管理', 98 | key: '/active/activelist', 99 | icon: '' 100 | }, 101 | ] 102 | }, 103 | banner: { 104 | title: '轮播图', 105 | key: '/banner', 106 | icon: 'el-icon-picture', 107 | children: [ // 子菜单列表 108 | { 109 | title: '首页轮播图', 110 | key: '/banner/bannerlist', 111 | icon: 'el-icon-picture' 112 | }, 113 | ] 114 | }, 115 | category: { 116 | title: '分类管理', 117 | key: '/category', 118 | icon: 'el-icon- iconfont icon-leimupinleifenleileibie', 119 | children: [ // 子菜单列表 120 | { 121 | title: '分类列表', 122 | key: '/category/categorylist', 123 | icon: '' 124 | }, 125 | ] 126 | }, 127 | comment: { 128 | title: '评论管理', 129 | key: '/comment', 130 | icon: 'el-icon- iconfont icon-ziyuan', 131 | children: [ // 子菜单列表 132 | { 133 | title: '评论列表', 134 | key: '/comment/commentlist', 135 | icon: '' 136 | }, 137 | ] 138 | }, 139 | goodsAndServices: { 140 | title: '商品管理', 141 | key: '/goods', 142 | icon: 'el-icon-goods', 143 | children: [ // 子菜单列表 144 | ] 145 | }, 146 | goods: { 147 | title: '商品列表', 148 | key: '/goods/goodslist', 149 | icon: '' 150 | }, 151 | order: { 152 | title: '订单管理', 153 | key: '/order', 154 | icon: 'el-icon- iconfont icon-dingdanguanli', 155 | children: [ // 子菜单列表 156 | { 157 | title: '订单列表', 158 | key: '/order/orderlist', 159 | icon: '' 160 | }, 161 | ] 162 | }, 163 | merchant: { 164 | title: '商家管理', 165 | key: '/merchant', 166 | icon: 'el-icon- iconfont icon-shangjia', 167 | children: [ // 子菜单列表 168 | { 169 | title: '店铺列表', 170 | key: '/merchant/merchantlist', 171 | icon: '' 172 | }, 173 | ] 174 | }, 175 | services: { 176 | title: '商品服务', 177 | key: '/goods/goodsservice', 178 | icon: '' 179 | }, 180 | role: { 181 | title: '角色管理', 182 | key: '/auth/rolelist', 183 | icon: '' 184 | } 185 | } 186 | 187 | let flag = true 188 | 189 | if (state.menuList.length != 0) { 190 | return 191 | } 192 | 193 | for (const key in menuListMapObj) { 194 | // console.log(state[key].find) 195 | const standard = key == 'goods' || key == 'services' || key == 'goodsAndServices' 196 | if (standard && flag) { 197 | if (state.goods.find || state.services.find) { 198 | if (state.goods.find) { 199 | menuListMapObj.goodsAndServices.children.push(menuListMapObj.goods) 200 | } 201 | if (state.services.find) { 202 | menuListMapObj.goodsAndServices.children.push(menuListMapObj.services) 203 | } 204 | state.menuList.push(menuListMapObj.goodsAndServices) 205 | flag = false 206 | } else { 207 | state.menuList.push(menuListMapObj.goodsAndServices) 208 | flag = false 209 | } 210 | } else if (!standard) { 211 | //模块 中 只有一项 而且不是 其他模块子项 212 | //权限菜单 213 | if (key == 'role') { 214 | if (state['role']['find']) { 215 | menuListMapObj.auth.children.push(menuListMapObj.role) 216 | state.menuList.push(menuListMapObj.auth) 217 | } else { 218 | state.menuList.push(menuListMapObj.auth) 219 | } 220 | } else if (state[key].hasOwnProperty('find') && state[key]['find']) { 221 | state.menuList.push(menuListMapObj[key]) 222 | } 223 | } 224 | } 225 | // console.log(state.menuList, state) 226 | } 227 | 228 | const mutations = { 229 | [PERMISSION_MAPPING_VIEW](state, data) { 230 | // console.log(func) 231 | permissionMappingView(data) 232 | }, 233 | [CLEAR_CACHE](state) { 234 | state.cache = false 235 | state.menuList = [] 236 | state.role = {} 237 | state.menuList = [] 238 | state.activity = {} 239 | state.banner = {} 240 | state.category = {} 241 | state.comment = {} 242 | state.goods = {} 243 | state.order = {} 244 | state.merchant = {} 245 | state.services = {} 246 | state.role = {} 247 | state.auth = {} 248 | } 249 | } 250 | 251 | const actions = { 252 | async getThisUserAuth({ commit }) { 253 | //缓存中没有数据 并且 不是强制刷新 重新请求数据 254 | if (!state.cache || state.mandatoryRefresh) { 255 | const result = await getUserInfo() 256 | state.cache = true 257 | state.cacheData = result 258 | commit(PERMISSION_MAPPING_VIEW, result, permissionMappingView) 259 | } else { 260 | //以前拿到过接口的数据 防止多次请求 增加接口压力 261 | commit(PERMISSION_MAPPING_VIEW, state.cacheData, permissionMappingView) 262 | } 263 | }, 264 | clearCache({ commit }) { 265 | commit(CLEAR_CACHE) 266 | } 267 | } 268 | 269 | const getters = {} 270 | 271 | export default { 272 | state, 273 | mutations, 274 | actions, 275 | getters 276 | }; -------------------------------------------------------------------------------- /src/views/activity/ActivityList.vue: -------------------------------------------------------------------------------- 1 | 110 | 111 | 112 | 277 | -------------------------------------------------------------------------------- /src/views/auth/RoleList.vue: -------------------------------------------------------------------------------- 1 | 98 | 292 | -------------------------------------------------------------------------------- /src/views/banner/BannerList.vue: -------------------------------------------------------------------------------- 1 | 124 | 125 | 322 | 323 | 324 | 326 | -------------------------------------------------------------------------------- /src/assets/fonts/demo.css: -------------------------------------------------------------------------------- 1 | /* Logo 字体 */ 2 | @font-face { 3 | font-family: "iconfont logo"; 4 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834'); 5 | src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'), 6 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'), 7 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'), 8 | url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); 9 | } 10 | 11 | .logo { 12 | font-family: "iconfont logo"; 13 | font-size: 160px; 14 | font-style: normal; 15 | -webkit-font-smoothing: antialiased; 16 | -moz-osx-font-smoothing: grayscale; 17 | } 18 | 19 | /* tabs */ 20 | .nav-tabs { 21 | position: relative; 22 | } 23 | 24 | .nav-tabs .nav-more { 25 | position: absolute; 26 | right: 0; 27 | bottom: 0; 28 | height: 42px; 29 | line-height: 42px; 30 | color: #666; 31 | } 32 | 33 | #tabs { 34 | border-bottom: 1px solid #eee; 35 | } 36 | 37 | #tabs li { 38 | cursor: pointer; 39 | width: 100px; 40 | height: 40px; 41 | line-height: 40px; 42 | text-align: center; 43 | font-size: 16px; 44 | border-bottom: 2px solid transparent; 45 | position: relative; 46 | z-index: 1; 47 | margin-bottom: -1px; 48 | color: #666; 49 | } 50 | 51 | 52 | #tabs .active { 53 | border-bottom-color: #f00; 54 | color: #222; 55 | } 56 | 57 | .tab-container .content { 58 | display: none; 59 | } 60 | 61 | /* 页面布局 */ 62 | .main { 63 | padding: 30px 100px; 64 | width: 960px; 65 | margin: 0 auto; 66 | } 67 | 68 | .main .logo { 69 | color: #333; 70 | text-align: left; 71 | margin-bottom: 30px; 72 | line-height: 1; 73 | height: 110px; 74 | margin-top: -50px; 75 | overflow: hidden; 76 | *zoom: 1; 77 | } 78 | 79 | .main .logo a { 80 | font-size: 160px; 81 | color: #333; 82 | } 83 | 84 | .helps { 85 | margin-top: 40px; 86 | } 87 | 88 | .helps pre { 89 | padding: 20px; 90 | margin: 10px 0; 91 | border: solid 1px #e7e1cd; 92 | background-color: #fffdef; 93 | overflow: auto; 94 | } 95 | 96 | .icon_lists { 97 | width: 100% !important; 98 | overflow: hidden; 99 | *zoom: 1; 100 | } 101 | 102 | .icon_lists li { 103 | width: 100px; 104 | margin-bottom: 10px; 105 | margin-right: 20px; 106 | text-align: center; 107 | list-style: none !important; 108 | cursor: default; 109 | } 110 | 111 | .icon_lists li .code-name { 112 | line-height: 1.2; 113 | } 114 | 115 | .icon_lists .icon { 116 | display: block; 117 | height: 100px; 118 | line-height: 100px; 119 | font-size: 42px; 120 | margin: 10px auto; 121 | color: #333; 122 | -webkit-transition: font-size 0.25s linear, width 0.25s linear; 123 | -moz-transition: font-size 0.25s linear, width 0.25s linear; 124 | transition: font-size 0.25s linear, width 0.25s linear; 125 | } 126 | 127 | .icon_lists .icon:hover { 128 | font-size: 100px; 129 | } 130 | 131 | .icon_lists .svg-icon { 132 | /* 通过设置 font-size 来改变图标大小 */ 133 | width: 1em; 134 | /* 图标和文字相邻时,垂直对齐 */ 135 | vertical-align: -0.15em; 136 | /* 通过设置 color 来改变 SVG 的颜色/fill */ 137 | fill: currentColor; 138 | /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示 139 | normalize.css 中也包含这行 */ 140 | overflow: hidden; 141 | } 142 | 143 | .icon_lists li .name, 144 | .icon_lists li .code-name { 145 | color: #666; 146 | } 147 | 148 | /* markdown 样式 */ 149 | .markdown { 150 | color: #666; 151 | font-size: 14px; 152 | line-height: 1.8; 153 | } 154 | 155 | .highlight { 156 | line-height: 1.5; 157 | } 158 | 159 | .markdown img { 160 | vertical-align: middle; 161 | max-width: 100%; 162 | } 163 | 164 | .markdown h1 { 165 | color: #404040; 166 | font-weight: 500; 167 | line-height: 40px; 168 | margin-bottom: 24px; 169 | } 170 | 171 | .markdown h2, 172 | .markdown h3, 173 | .markdown h4, 174 | .markdown h5, 175 | .markdown h6 { 176 | color: #404040; 177 | margin: 1.6em 0 0.6em 0; 178 | font-weight: 500; 179 | clear: both; 180 | } 181 | 182 | .markdown h1 { 183 | font-size: 28px; 184 | } 185 | 186 | .markdown h2 { 187 | font-size: 22px; 188 | } 189 | 190 | .markdown h3 { 191 | font-size: 16px; 192 | } 193 | 194 | .markdown h4 { 195 | font-size: 14px; 196 | } 197 | 198 | .markdown h5 { 199 | font-size: 12px; 200 | } 201 | 202 | .markdown h6 { 203 | font-size: 12px; 204 | } 205 | 206 | .markdown hr { 207 | height: 1px; 208 | border: 0; 209 | background: #e9e9e9; 210 | margin: 16px 0; 211 | clear: both; 212 | } 213 | 214 | .markdown p { 215 | margin: 1em 0; 216 | } 217 | 218 | .markdown>p, 219 | .markdown>blockquote, 220 | .markdown>.highlight, 221 | .markdown>ol, 222 | .markdown>ul { 223 | width: 80%; 224 | } 225 | 226 | .markdown ul>li { 227 | list-style: circle; 228 | } 229 | 230 | .markdown>ul li, 231 | .markdown blockquote ul>li { 232 | margin-left: 20px; 233 | padding-left: 4px; 234 | } 235 | 236 | .markdown>ul li p, 237 | .markdown>ol li p { 238 | margin: 0.6em 0; 239 | } 240 | 241 | .markdown ol>li { 242 | list-style: decimal; 243 | } 244 | 245 | .markdown>ol li, 246 | .markdown blockquote ol>li { 247 | margin-left: 20px; 248 | padding-left: 4px; 249 | } 250 | 251 | .markdown code { 252 | margin: 0 3px; 253 | padding: 0 5px; 254 | background: #eee; 255 | border-radius: 3px; 256 | } 257 | 258 | .markdown strong, 259 | .markdown b { 260 | font-weight: 600; 261 | } 262 | 263 | .markdown>table { 264 | border-collapse: collapse; 265 | border-spacing: 0px; 266 | empty-cells: show; 267 | border: 1px solid #e9e9e9; 268 | width: 95%; 269 | margin-bottom: 24px; 270 | } 271 | 272 | .markdown>table th { 273 | white-space: nowrap; 274 | color: #333; 275 | font-weight: 600; 276 | } 277 | 278 | .markdown>table th, 279 | .markdown>table td { 280 | border: 1px solid #e9e9e9; 281 | padding: 8px 16px; 282 | text-align: left; 283 | } 284 | 285 | .markdown>table th { 286 | background: #F7F7F7; 287 | } 288 | 289 | .markdown blockquote { 290 | font-size: 90%; 291 | color: #999; 292 | border-left: 4px solid #e9e9e9; 293 | padding-left: 0.8em; 294 | margin: 1em 0; 295 | } 296 | 297 | .markdown blockquote p { 298 | margin: 0; 299 | } 300 | 301 | .markdown .anchor { 302 | opacity: 0; 303 | transition: opacity 0.3s ease; 304 | margin-left: 8px; 305 | } 306 | 307 | .markdown .waiting { 308 | color: #ccc; 309 | } 310 | 311 | .markdown h1:hover .anchor, 312 | .markdown h2:hover .anchor, 313 | .markdown h3:hover .anchor, 314 | .markdown h4:hover .anchor, 315 | .markdown h5:hover .anchor, 316 | .markdown h6:hover .anchor { 317 | opacity: 1; 318 | display: inline-block; 319 | } 320 | 321 | .markdown>br, 322 | .markdown>p>br { 323 | clear: both; 324 | } 325 | 326 | 327 | .hljs { 328 | display: block; 329 | background: white; 330 | padding: 0.5em; 331 | color: #333333; 332 | overflow-x: auto; 333 | } 334 | 335 | .hljs-comment, 336 | .hljs-meta { 337 | color: #969896; 338 | } 339 | 340 | .hljs-string, 341 | .hljs-variable, 342 | .hljs-template-variable, 343 | .hljs-strong, 344 | .hljs-emphasis, 345 | .hljs-quote { 346 | color: #df5000; 347 | } 348 | 349 | .hljs-keyword, 350 | .hljs-selector-tag, 351 | .hljs-type { 352 | color: #a71d5d; 353 | } 354 | 355 | .hljs-literal, 356 | .hljs-symbol, 357 | .hljs-bullet, 358 | .hljs-attribute { 359 | color: #0086b3; 360 | } 361 | 362 | .hljs-section, 363 | .hljs-name { 364 | color: #63a35c; 365 | } 366 | 367 | .hljs-tag { 368 | color: #333333; 369 | } 370 | 371 | .hljs-title, 372 | .hljs-attr, 373 | .hljs-selector-id, 374 | .hljs-selector-class, 375 | .hljs-selector-attr, 376 | .hljs-selector-pseudo { 377 | color: #795da3; 378 | } 379 | 380 | .hljs-addition { 381 | color: #55a532; 382 | background-color: #eaffea; 383 | } 384 | 385 | .hljs-deletion { 386 | color: #bd2c00; 387 | background-color: #ffecec; 388 | } 389 | 390 | .hljs-link { 391 | text-decoration: underline; 392 | } 393 | 394 | /* 代码高亮 */ 395 | /* PrismJS 1.15.0 396 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 397 | /** 398 | * prism.js default theme for JavaScript, CSS and HTML 399 | * Based on dabblet (http://dabblet.com) 400 | * @author Lea Verou 401 | */ 402 | code[class*="language-"], 403 | pre[class*="language-"] { 404 | color: black; 405 | background: none; 406 | text-shadow: 0 1px white; 407 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 408 | text-align: left; 409 | white-space: pre; 410 | word-spacing: normal; 411 | word-break: normal; 412 | word-wrap: normal; 413 | line-height: 1.5; 414 | 415 | -moz-tab-size: 4; 416 | -o-tab-size: 4; 417 | tab-size: 4; 418 | 419 | -webkit-hyphens: none; 420 | -moz-hyphens: none; 421 | -ms-hyphens: none; 422 | hyphens: none; 423 | } 424 | 425 | pre[class*="language-"]::-moz-selection, 426 | pre[class*="language-"] ::-moz-selection, 427 | code[class*="language-"]::-moz-selection, 428 | code[class*="language-"] ::-moz-selection { 429 | text-shadow: none; 430 | background: #b3d4fc; 431 | } 432 | 433 | pre[class*="language-"]::selection, 434 | pre[class*="language-"] ::selection, 435 | code[class*="language-"]::selection, 436 | code[class*="language-"] ::selection { 437 | text-shadow: none; 438 | background: #b3d4fc; 439 | } 440 | 441 | @media print { 442 | 443 | code[class*="language-"], 444 | pre[class*="language-"] { 445 | text-shadow: none; 446 | } 447 | } 448 | 449 | /* Code blocks */ 450 | pre[class*="language-"] { 451 | padding: 1em; 452 | margin: .5em 0; 453 | overflow: auto; 454 | } 455 | 456 | :not(pre)>code[class*="language-"], 457 | pre[class*="language-"] { 458 | background: #f5f2f0; 459 | } 460 | 461 | /* Inline code */ 462 | :not(pre)>code[class*="language-"] { 463 | padding: .1em; 464 | border-radius: .3em; 465 | white-space: normal; 466 | } 467 | 468 | .token.comment, 469 | .token.prolog, 470 | .token.doctype, 471 | .token.cdata { 472 | color: slategray; 473 | } 474 | 475 | .token.punctuation { 476 | color: #999; 477 | } 478 | 479 | .namespace { 480 | opacity: .7; 481 | } 482 | 483 | .token.property, 484 | .token.tag, 485 | .token.boolean, 486 | .token.number, 487 | .token.constant, 488 | .token.symbol, 489 | .token.deleted { 490 | color: #905; 491 | } 492 | 493 | .token.selector, 494 | .token.attr-name, 495 | .token.string, 496 | .token.char, 497 | .token.builtin, 498 | .token.inserted { 499 | color: #690; 500 | } 501 | 502 | .token.operator, 503 | .token.entity, 504 | .token.url, 505 | .language-css .token.string, 506 | .style .token.string { 507 | color: #9a6e3a; 508 | background: hsla(0, 0%, 100%, .5); 509 | } 510 | 511 | .token.atrule, 512 | .token.attr-value, 513 | .token.keyword { 514 | color: #07a; 515 | } 516 | 517 | .token.function, 518 | .token.class-name { 519 | color: #DD4A68; 520 | } 521 | 522 | .token.regex, 523 | .token.important, 524 | .token.variable { 525 | color: #e90; 526 | } 527 | 528 | .token.important, 529 | .token.bold { 530 | font-weight: bold; 531 | } 532 | 533 | .token.italic { 534 | font-style: italic; 535 | } 536 | 537 | .token.entity { 538 | cursor: help; 539 | } 540 | -------------------------------------------------------------------------------- /src/views/auth/UserList.vue: -------------------------------------------------------------------------------- 1 | 115 | 342 | -------------------------------------------------------------------------------- /src/views/category/AddCategory.vue: -------------------------------------------------------------------------------- 1 | 160 | 161 | 328 | -------------------------------------------------------------------------------- /src/views/goods/GoodsService.vue: -------------------------------------------------------------------------------- 1 | 117 | 118 | 359 | -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | Created by iconfont 9 | 10 | 11 | 12 | 13 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/assets/fontss/iconfont.js: -------------------------------------------------------------------------------- 1 | !function(a){var c,n='',l=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss");if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(c){console&&console.log(c)}}!function(c){if(document.addEventListener)if(~["complete","loaded","interactive"].indexOf(document.readyState))setTimeout(c,0);else{var l=function(){document.removeEventListener("DOMContentLoaded",l,!1),c()};document.addEventListener("DOMContentLoaded",l,!1)}else document.attachEvent&&(t=c,i=a.document,o=!1,(n=function(){try{i.documentElement.doScroll("left")}catch(c){return void setTimeout(n,50)}e()})(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())});function e(){o||(o=!0,t())}var t,i,o,n}(function(){var c,l,e,t,i,o;(c=document.createElement("div")).innerHTML=n,n=null,(l=c.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",e=l,(t=document.body).firstChild?(i=e,(o=t.firstChild).parentNode.insertBefore(i,o)):t.appendChild(e))})}(window); --------------------------------------------------------------------------------