├── .gitattributes ├── .env ├── .env.preview ├── .browserslistrc ├── .env.development ├── src ├── cache │ ├── const.js │ ├── index.js │ └── user.js ├── components │ ├── MultiTab │ │ ├── events.js │ │ ├── index.less │ │ └── index.js │ ├── Trend │ │ ├── index.js │ │ ├── index.less │ │ ├── index.md │ │ └── Trend.vue │ ├── Ellipsis │ │ ├── index.js │ │ ├── index.md │ │ └── Ellipsis.vue │ ├── NoticeIcon │ │ └── index.js │ ├── NumberInfo │ │ ├── index.js │ │ ├── index.md │ │ ├── NumberInfo.vue │ │ └── index.less │ ├── IconSelector │ │ ├── index.js │ │ ├── README.md │ │ └── IconSelector.vue │ ├── SettingDrawer │ │ ├── index.js │ │ ├── SettingItem.vue │ │ ├── themeColor.js │ │ └── settingConfig.js │ ├── AvatarList │ │ ├── index.js │ │ ├── Item.vue │ │ └── index.less │ ├── StandardFormRow │ │ └── index.js │ ├── ArticleListContent │ │ ├── index.js │ │ └── ArticleListContent.vue │ ├── FooterToolbar │ │ ├── index.js │ │ ├── index.less │ │ ├── index.md │ │ └── FooterToolBar.vue │ ├── index.less │ ├── TextArea │ │ ├── style.less │ │ └── index.jsx │ ├── Charts │ │ ├── chart.less │ │ ├── smooth.area.less │ │ ├── MiniSmoothArea.vue │ │ ├── MiniBar.vue │ │ ├── MiniArea.vue │ │ ├── TransferBar.vue │ │ ├── Bar.vue │ │ ├── Liquid.vue │ │ ├── Radar.vue │ │ ├── MiniProgress.vue │ │ ├── RankList.vue │ │ └── Trend.vue │ ├── Reload │ │ └── Reload.vue │ ├── Tinymce │ │ ├── toolbar.js │ │ ├── plugins.js │ │ └── dynamicLoadScript.js │ ├── Search │ │ ├── index.less │ │ └── GlobalSearch.jsx │ ├── SelectLang │ │ ├── index.less │ │ └── index.jsx │ ├── ConfirmBack │ │ └── ConfirmBack.vue │ ├── GlobalFooter │ │ └── index.vue │ ├── _util │ │ └── util.js │ ├── InputDropdown │ │ └── index.less │ ├── TagSelect │ │ └── TagSelectOption.jsx │ ├── GlobalHeader │ │ ├── RightContent.vue │ │ └── AvatarDropdown.vue │ ├── index.js │ ├── Box │ │ └── Box.vue │ ├── NProgress │ │ └── nprogress.less │ └── UploadButton │ │ └── UploadButton.vue ├── assets │ ├── logo.png │ └── icons │ │ └── bx-analyse.svg ├── views │ ├── 404.vue │ ├── user │ │ └── Register.vue │ ├── dashboard │ │ └── Dashboard.vue │ ├── system │ │ ├── setting │ │ │ └── mixin.js │ │ ├── login_plugin │ │ │ └── Index.vue │ │ ├── storage_plugin │ │ │ └── Index.vue │ │ └── payment_plugin │ │ │ └── Index.vue │ ├── account │ │ └── settings │ │ │ ├── Binding.vue │ │ │ ├── Notification.vue │ │ │ ├── Security.vue │ │ │ └── Custom.vue │ ├── product │ │ ├── list │ │ │ └── components │ │ │ │ └── Introduction.vue │ │ └── tag │ │ │ └── upload-mixin.js │ ├── marketing │ │ ├── coupon │ │ │ ├── components │ │ │ │ ├── Add │ │ │ │ │ └── Introduction.vue │ │ │ │ └── Edit │ │ │ │ │ └── Introduction.vue │ │ │ └── Add.vue │ │ └── promotion │ │ │ ├── components │ │ │ ├── Add │ │ │ │ └── Introduction.vue │ │ │ └── Edit │ │ │ │ └── Introduction.vue │ │ │ └── Add.vue │ ├── exception │ │ ├── 403.vue │ │ ├── 404.vue │ │ └── 500.vue │ ├── order │ │ ├── returns │ │ │ ├── components │ │ │ │ └── ReturnsItems.vue │ │ │ └── Look.vue │ │ ├── shipping │ │ │ ├── components │ │ │ │ └── ShippingItems.vue │ │ │ └── Look.vue │ │ └── list │ │ │ ├── Edit.vue │ │ │ ├── components │ │ │ ├── LogsInfo.vue │ │ │ ├── ProductsInfo.vue │ │ │ └── ReturnsInfo.vue │ │ │ └── Look.vue │ └── member │ │ └── list │ │ ├── Add.vue │ │ └── components │ │ ├── Add │ │ └── Info.vue │ │ └── Edit │ │ └── Info.vue ├── layouts │ ├── PageView.vue │ ├── BlankLayout.vue │ ├── index.js │ ├── BasicLayout.less │ └── RouteView.vue ├── mixins │ ├── index.js │ ├── upload.js │ └── introduction.js ├── store │ ├── device-mixin.js │ ├── modules │ │ ├── sku.js │ │ ├── user.js │ │ ├── upload.js │ │ ├── member.js │ │ ├── permission.js │ │ ├── ad.js │ │ ├── parameter.js │ │ ├── specification.js │ │ ├── attribute.js │ │ ├── setting.js │ │ ├── area.js │ │ ├── tag.js │ │ ├── payment_method.js │ │ ├── order.js │ │ ├── brand.js │ │ ├── member_rank.js │ │ ├── delivery_corp.js │ │ ├── shipping_method.js │ │ ├── product_category.js │ │ ├── async-router.js │ │ ├── coupon.js │ │ └── promotion.js │ ├── i18n-mixin.js │ ├── mutation-types.js │ ├── app-mixin.js │ └── index.js ├── core │ ├── icons.js │ ├── directives │ │ └── action.js │ └── bootstrap.js ├── utils │ ├── helper │ │ ├── base64.js │ │ ├── permission.js │ │ └── image.js │ ├── domUtil.js │ ├── filter.js │ └── utils.less ├── config │ ├── axios.config.js │ └── defaultSettings.js ├── api │ ├── upload.js │ ├── permission.js │ ├── setting.js │ ├── product_notify.js │ ├── sku.js │ ├── user.js │ ├── point_log.js │ ├── deposit_log.js │ ├── payment.js │ ├── returns.js │ ├── message_config.js │ ├── shipping.js │ ├── stock.js │ ├── area.js │ ├── refunds.js │ ├── member.js │ └── config.js ├── locales │ ├── lang │ │ ├── zh-CN.js │ │ └── en-US.js │ └── index.js ├── App.vue ├── router │ └── index.js └── main.js ├── tests └── unit │ └── .eslintrc.js ├── .prettierrc ├── postcss.config.js ├── public ├── logo.png └── avatar2.jpg ├── webstorm.config.js ├── .travis.yml ├── jsconfig.json ├── .gitignore ├── jest.config.js ├── docs ├── multi-tabs.md ├── add-page-loading-animate.md ├── webpack-bundle-analyzer.md └── load-on-demand.md ├── babel.config.js ├── README.md ├── .editorconfig ├── LICENSE ├── .eslintrc.js └── package.json /.gitattributes: -------------------------------------------------------------------------------- 1 | public/* linguist-vendored -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PREVIEW=false -------------------------------------------------------------------------------- /.env.preview: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | VUE_APP_PREVIEW=true -------------------------------------------------------------------------------- /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 10 4 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | NODE_ENV=development 2 | VUE_APP_PREVIEW=true -------------------------------------------------------------------------------- /src/cache/const.js: -------------------------------------------------------------------------------- 1 | const PREFIX = '__admin' 2 | 3 | export { 4 | PREFIX 5 | } 6 | -------------------------------------------------------------------------------- /src/components/MultiTab/events.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | export default new Vue() 3 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "semi": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YangCheng0121/go-shop-b2c-admin/HEAD/public/logo.png -------------------------------------------------------------------------------- /src/components/Trend/index.js: -------------------------------------------------------------------------------- 1 | import Trend from './Trend.vue' 2 | 3 | export default Trend 4 | -------------------------------------------------------------------------------- /public/avatar2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YangCheng0121/go-shop-b2c-admin/HEAD/public/avatar2.jpg -------------------------------------------------------------------------------- /src/components/Ellipsis/index.js: -------------------------------------------------------------------------------- 1 | import Ellipsis from './Ellipsis' 2 | 3 | export default Ellipsis 4 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YangCheng0121/go-shop-b2c-admin/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/components/NoticeIcon/index.js: -------------------------------------------------------------------------------- 1 | import NoticeIcon from './NoticeIcon' 2 | export default NoticeIcon 3 | -------------------------------------------------------------------------------- /src/components/NumberInfo/index.js: -------------------------------------------------------------------------------- 1 | import NumberInfo from './NumberInfo' 2 | 3 | export default NumberInfo 4 | -------------------------------------------------------------------------------- /src/components/IconSelector/index.js: -------------------------------------------------------------------------------- 1 | import IconSelector from './IconSelector' 2 | export default IconSelector 3 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/index.js: -------------------------------------------------------------------------------- 1 | import SettingDrawer from './SettingDrawer' 2 | export default SettingDrawer 3 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.js: -------------------------------------------------------------------------------- 1 | import AvatarList from './List' 2 | import './index.less' 3 | 4 | export default AvatarList 5 | -------------------------------------------------------------------------------- /src/components/StandardFormRow/index.js: -------------------------------------------------------------------------------- 1 | import StandardFormRow from './StandardFormRow' 2 | 3 | export default StandardFormRow 4 | -------------------------------------------------------------------------------- /src/components/ArticleListContent/index.js: -------------------------------------------------------------------------------- 1 | import ArticleListContent from './ArticleListContent' 2 | 3 | export default ArticleListContent 4 | -------------------------------------------------------------------------------- /webstorm.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const webpackConfig = require('@vue/cli-service/webpack.config.js') 3 | module.exports = webpackConfig 4 | -------------------------------------------------------------------------------- /src/cache/index.js: -------------------------------------------------------------------------------- 1 | import { getUser, saveUser, clearUser } from './user' 2 | 3 | export { 4 | getUser, 5 | saveUser, 6 | clearUser 7 | } 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10.15.0 4 | cache: yarn 5 | script: 6 | - yarn 7 | - yarn run lint --no-fix && yarn run build 8 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.js: -------------------------------------------------------------------------------- 1 | import FooterToolBar from './FooterToolBar' 2 | import './index.less' 3 | 4 | export default FooterToolBar 5 | -------------------------------------------------------------------------------- /src/views/404.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 404 page 4 | 5 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /src/layouts/PageView.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | -------------------------------------------------------------------------------- /src/mixins/index.js: -------------------------------------------------------------------------------- 1 | import areaMixin from '@/mixins/area' 2 | import pluginMixin from '@/mixins/plugin' 3 | import pluginEdit from '@/mixins/plugin_edit' 4 | 5 | export { 6 | areaMixin, 7 | pluginMixin, 8 | pluginEdit 9 | } 10 | -------------------------------------------------------------------------------- /src/views/user/Register.vue: -------------------------------------------------------------------------------- 1 | 2 | $COMPONENT_NAME$ 3 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["src/*"] 7 | } 8 | }, 9 | "exclude": ["node_modules", "dist"], 10 | "include": ["src/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /src/components/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/lib/style/index"; 2 | 3 | // The prefix to use on all css classes from ant-pro. 4 | @ant-pro-prefix : ant-pro; 5 | @ant-global-sider-zindex : 106; 6 | @ant-global-header-zindex : 105; -------------------------------------------------------------------------------- /src/store/device-mixin.js: -------------------------------------------------------------------------------- 1 | import { mapState } from 'vuex' 2 | 3 | const deviceMixin = { 4 | computed: { 5 | ...mapState({ 6 | isMobile: state => state.app.isMobile 7 | }) 8 | } 9 | } 10 | 11 | export { deviceMixin } 12 | -------------------------------------------------------------------------------- /src/layouts/BlankLayout.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 13 | 14 | 17 | -------------------------------------------------------------------------------- /src/views/dashboard/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 2 | 欢迎来到BeeShop商城 3 | 4 | 5 | 10 | 11 | 14 | -------------------------------------------------------------------------------- /src/components/TextArea/style.less: -------------------------------------------------------------------------------- 1 | .ant-textarea-limit { 2 | position: relative; 3 | 4 | .limit { 5 | position: absolute; 6 | color: #909399; 7 | background: #fff; 8 | font-size: 12px; 9 | bottom: 5px; 10 | right: 10px; 11 | } 12 | } -------------------------------------------------------------------------------- /src/components/Charts/chart.less: -------------------------------------------------------------------------------- 1 | .antv-chart-mini { 2 | position: relative; 3 | width: 100%; 4 | 5 | .chart-wrapper { 6 | position: absolute; 7 | bottom: -28px; 8 | width: 100%; 9 | 10 | /* margin: 0 -5px; 11 | overflow: hidden;*/ 12 | } 13 | } -------------------------------------------------------------------------------- /src/layouts/index.js: -------------------------------------------------------------------------------- 1 | import UserLayout from './UserLayout' 2 | import BlankLayout from './BlankLayout' 3 | import BasicLayout from './BasicLayout' 4 | import RouteView from './RouteView' 5 | import PageView from './PageView' 6 | 7 | export { UserLayout, BasicLayout, BlankLayout, RouteView, PageView } 8 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/store/modules/sku.js: -------------------------------------------------------------------------------- 1 | const sku = { 2 | state: { 3 | sn: null 4 | }, 5 | mutations: { 6 | SET_SKU_SN: (state, sn) => { 7 | state.sn = sn 8 | } 9 | }, 10 | actions: { 11 | SetSkuSn({ commit }, sn) { 12 | commit('SET_SKU_SN', sn) 13 | } 14 | } 15 | } 16 | 17 | export default sku 18 | -------------------------------------------------------------------------------- /src/components/Charts/smooth.area.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @smoothArea-prefix-cls: ~"@{ant-pro-prefix}-smooth-area"; 4 | 5 | .@{smoothArea-prefix-cls} { 6 | position: relative; 7 | width: 100%; 8 | 9 | .chart-wrapper { 10 | position: absolute; 11 | bottom: -28px; 12 | width: 100%; 13 | } 14 | } -------------------------------------------------------------------------------- /src/store/i18n-mixin.js: -------------------------------------------------------------------------------- 1 | import { mapState } from 'vuex' 2 | 3 | const i18nMixin = { 4 | computed: { 5 | ...mapState({ 6 | currentLang: state => state.app.lang 7 | }) 8 | }, 9 | methods: { 10 | setLang (lang) { 11 | this.$store.dispatch('setLang', lang) 12 | } 13 | } 14 | } 15 | 16 | export default i18nMixin 17 | -------------------------------------------------------------------------------- /src/core/icons.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom icon list 3 | * All icons are loaded here for easy management 4 | * @see https://vue.ant.design/components/icon/#Custom-Font-Icon 5 | * 6 | * 自定义图标加载表 7 | * 所有图标均从这里加载,方便管理 8 | */ 9 | import bxAnaalyse from '@/assets/icons/bx-analyse.svg?inline' // path to your '*.svg?inline' file. 10 | 11 | export { bxAnaalyse } 12 | -------------------------------------------------------------------------------- /src/views/system/setting/mixin.js: -------------------------------------------------------------------------------- 1 | import { mapGetters } from 'vuex' 2 | 3 | const settingMixin = { 4 | computed: { 5 | ...mapGetters([ 6 | 'setting' 7 | ]) 8 | }, 9 | watch: { 10 | setting() { 11 | this.setFormFieldsValue() 12 | } 13 | }, 14 | mounted() { 15 | this.setFormFieldsValue() 16 | } 17 | } 18 | 19 | export default settingMixin 20 | -------------------------------------------------------------------------------- /src/views/account/settings/Binding.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /src/views/account/settings/Notification.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /src/utils/helper/base64.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 根据 File 获取Base64 3 | * @param file 4 | * @returns {Promise} 5 | */ 6 | export function getBase64(file) { 7 | return new Promise((resolve, reject) => { 8 | const reader = new FileReader() 9 | reader.readAsDataURL(file) 10 | reader.onload = () => resolve(reader.result) 11 | reader.onerror = error => reject(error) 12 | }) 13 | } 14 | -------------------------------------------------------------------------------- /src/config/axios.config.js: -------------------------------------------------------------------------------- 1 | const isProd = process.env.NODE_ENV === 'production' 2 | 3 | let BASE_URL 4 | let IMAGE_PREFIX 5 | if (isProd) { 6 | BASE_URL = 'http://106.54.82.67:8081/api/admin' // 生产地址 7 | IMAGE_PREFIX = 'http://106.54.82.67' // 生产地址 8 | } else { 9 | BASE_URL = 'http://localhost:8080/api/admin' 10 | IMAGE_PREFIX = 'http://localhost:8080' 11 | } 12 | 13 | export { 14 | BASE_URL, 15 | IMAGE_PREFIX 16 | } 17 | -------------------------------------------------------------------------------- /src/api/upload.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | 3 | /** 4 | * 上传图片 5 | * @returns {Promise} 6 | * @param file 7 | */ 8 | export function uploadImage(file) { 9 | const url = '/upload' 10 | 11 | return axios({ 12 | url: url, 13 | method: 'POST', 14 | headers: Object.assign({}, HEADERS.FromData), 15 | data: file 16 | }).then((res) => { 17 | return Promise.resolve(res.data) 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Reload/Reload.vue: -------------------------------------------------------------------------------- 1 | 2 | 刷新 3 | 4 | 5 | 17 | 18 | 21 | -------------------------------------------------------------------------------- /src/views/product/list/components/Introduction.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /src/views/marketing/coupon/components/Add/Introduction.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /src/views/marketing/coupon/components/Edit/Introduction.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /src/views/exception/403.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Back Home 6 | 7 | 8 | 9 | 10 | 11 | 21 | -------------------------------------------------------------------------------- /src/views/exception/404.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Back Home 6 | 7 | 8 | 9 | 10 | 11 | 21 | -------------------------------------------------------------------------------- /src/views/marketing/promotion/components/Add/Introduction.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /src/views/marketing/promotion/components/Edit/Introduction.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /src/views/exception/500.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Back Home 6 | 7 | 8 | 9 | 10 | 11 | 21 | -------------------------------------------------------------------------------- /src/components/Tinymce/toolbar.js: -------------------------------------------------------------------------------- 1 | // Here is a list of the toolbar 2 | // Detail list see https://www.tinymce.com/docs/advanced/editor-control-identifiers/#toolbarcontrols 3 | 4 | const toolbar = ['searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 'hr bullist numlist link image charmap preview anchor pagebreak insertdatetime media table emoticons forecolor backcolor fullscreen'] 5 | 6 | export default toolbar 7 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @footer-toolbar-prefix-cls: ~"@{ant-pro-prefix}-footer-toolbar"; 4 | 5 | .@{footer-toolbar-prefix-cls} { 6 | position: fixed; 7 | width: 100%; 8 | bottom: 0; 9 | right: 0; 10 | height: 56px; 11 | line-height: 56px; 12 | box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.03); 13 | background: #fff; 14 | border-top: 1px solid #e8e8e8; 15 | padding: 0 24px; 16 | z-index: 9; 17 | 18 | &:after { 19 | content: ""; 20 | display: block; 21 | clear: both; 22 | } 23 | } -------------------------------------------------------------------------------- /src/locales/lang/zh-CN.js: -------------------------------------------------------------------------------- 1 | import antd from 'ant-design-vue/es/locale-provider/zh_CN' 2 | import momentCN from 'moment/locale/zh-cn' 3 | 4 | const components = { 5 | antLocale: antd, 6 | momentName: 'zh-cn', 7 | momentLocale: momentCN 8 | } 9 | 10 | const locale = { 11 | 'message': '-', 12 | 'menu.home': '主页', 13 | 'menu.dashboard': '仪表盘', 14 | 'menu.dashboard.analysis': '分析页', 15 | 'menu.dashboard.monitor': '监控页', 16 | 'menu.dashboard.workplace': '工作台' 17 | } 18 | 19 | export default { 20 | ...components, 21 | ...locale 22 | } 23 | -------------------------------------------------------------------------------- /src/store/modules/user.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import { clearUser } from '@/cache' 3 | import { resetRouter } from '@/router' 4 | 5 | const user = { 6 | state: { 7 | role: {} 8 | }, 9 | mutations: { 10 | SET_ROLE: (state, role) => { 11 | state.role = role 12 | } 13 | }, 14 | actions: { 15 | Logout() { 16 | return new Promise(resolve => { 17 | clearUser() 18 | Vue.$cookies.remove('admin_login') 19 | resetRouter() 20 | resolve() 21 | }) 22 | } 23 | } 24 | } 25 | 26 | export default user 27 | -------------------------------------------------------------------------------- /src/cache/user.js: -------------------------------------------------------------------------------- 1 | import storage from 'store' 2 | import { PREFIX } from './const' 3 | 4 | const USER_KEY = `${PREFIX}__user__` 5 | 6 | /** 7 | * 保存 User 8 | * @param user 9 | */ 10 | function saveUser(user) { 11 | storage.set(USER_KEY, user) 12 | } 13 | 14 | /** 15 | * 获取 User 16 | * @returns {*} 17 | */ 18 | function getUser() { 19 | return storage.get(USER_KEY) 20 | } 21 | 22 | /** 23 | * 清除 User 24 | * @returns {*} 25 | */ 26 | function clearUser() { 27 | storage.remove(USER_KEY) 28 | } 29 | 30 | export { 31 | saveUser, 32 | getUser, 33 | clearUser 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Search/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/es/style/themes/default"; 2 | 3 | .global-search-wrapper { 4 | position: fixed; 5 | top: 0; 6 | left: 0; 7 | right: 0; 8 | bottom: 0; 9 | z-index: @zindex-modal-mask; 10 | background: @modal-mask-bg; 11 | 12 | .global-search-box { 13 | position: absolute; 14 | top: 20%; 15 | left: 50%; 16 | width: 450px; 17 | transform: translate(-50%, -50%); 18 | 19 | .global-search-tips { 20 | color: @white; 21 | font-size: @font-size-lg; 22 | text-align: right; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /src/utils/helper/permission.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 根据 module 获取权限 3 | * @param module 4 | * @param permissions 5 | * @returns {*} 6 | */ 7 | export function getPermissionsByModule(module, permissions) { 8 | return permissions.filter((value) => { 9 | return value.module === module 10 | }) 11 | } 12 | 13 | /** 14 | * 根据 module 获取选中的权限 15 | * @param module 16 | * @param permissions 17 | * @returns {*} 18 | */ 19 | export function getCheckedPermissionsByModule(module, permissions) { 20 | return getPermissionsByModule(module, permissions).map((item) => { 21 | return item.id 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue' 7 | ], 8 | transform: { 9 | '^.+\\.vue$': 'vue-jest', 10 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 11 | '^.+\\.jsx?$': 'babel-jest' 12 | }, 13 | moduleNameMapper: { 14 | '^@/(.*)$': '/src/$1' 15 | }, 16 | snapshotSerializers: [ 17 | 'jest-serializer-vue' 18 | ], 19 | testMatch: [ 20 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 21 | ], 22 | testURL: 'http://localhost/' 23 | } 24 | -------------------------------------------------------------------------------- /src/components/Tinymce/plugins.js: -------------------------------------------------------------------------------- 1 | // Any plugins you want to use has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | 5 | const plugins = ['advlist anchor autolink autosave code codesample colorpicker colorpicker contextmenu directionality emoticons fullscreen hr image imagetools insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus table template textcolor textpattern visualblocks visualchars wordcount autoresize'] 6 | 7 | export default plugins 8 | -------------------------------------------------------------------------------- /src/api/permission.js: -------------------------------------------------------------------------------- 1 | import axios from '@/api/config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 获取权限列表 6 | * @param query 7 | * @param sortby 8 | * @param order 9 | * @returns {Promise} 10 | */ 11 | export function getPermissionList({ query, sortby, order }) { 12 | const url = '/permission' 13 | 14 | return axios({ 15 | url: url, 16 | method: 'GET', 17 | params: { 18 | query: getObjectKeyValueToString(query), 19 | sortby: sortby, 20 | order: order 21 | } 22 | }).then((res) => { 23 | return Promise.resolve(res.data) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 25 | -------------------------------------------------------------------------------- /src/components/MultiTab/index.less: -------------------------------------------------------------------------------- 1 | @import '../index'; 2 | 3 | @multi-tab-prefix-cls: ~"@{ant-pro-prefix}-multi-tab"; 4 | @multi-tab-wrapper-prefix-cls: ~"@{ant-pro-prefix}-multi-tab-wrapper"; 5 | 6 | /* 7 | .topmenu .@{multi-tab-prefix-cls} { 8 | max-width: 1200px; 9 | margin: -23px auto 24px auto; 10 | } 11 | */ 12 | .@{multi-tab-prefix-cls} { 13 | margin: -23px -24px 24px -24px; 14 | background: #fff; 15 | } 16 | 17 | .topmenu .@{multi-tab-wrapper-prefix-cls} { 18 | max-width: 1200px; 19 | margin: 0 auto; 20 | } 21 | 22 | .topmenu.content-width-Fluid .@{multi-tab-wrapper-prefix-cls} { 23 | max-width: 100%; 24 | margin: 0 auto; 25 | } 26 | -------------------------------------------------------------------------------- /docs/multi-tabs.md: -------------------------------------------------------------------------------- 1 | 多(页签)标签 模式 2 | ==== 3 | 4 | 5 | ## 让框架支持打开的页面增加多标签,可随时切换 6 | 7 | ### 关于如何移除该功能 组件 8 | 1. 移除 `/src/layouts/BasicLayout.vue` L44, L69, L80 9 | ```vue 10 | // L44 11 | 12 | 13 | // L69 14 | import MultiTab from '@/components/MultiTab' 15 | 16 | // L80 17 | MultiTab, 18 | ``` 19 | 2. 移除 `/src/config/defaultSettings.js` L25 20 | 21 | 3. 移除 `src/store/modules/app.js` L27, L76-L79, L118-L120 22 | 23 | 4. 移除 `src/utils/product-upload.js` L21 24 | 25 | 5. 删除组件目录 `src/components/MultiTab` 26 | 27 | > 以上 `L x` 均代表行N ,如 L3 = 行3 28 | 29 | -------------------------------------------------------------------------------- /src/utils/domUtil.js: -------------------------------------------------------------------------------- 1 | import config from '@/config/defaultSettings' 2 | 3 | export const setDocumentTitle = function (title) { 4 | document.title = title 5 | const ua = navigator.userAgent 6 | // eslint-disable-next-line 7 | const regex = /\bMicroMessenger\/([\d\.]+)/ 8 | if (regex.test(ua) && /ip(hone|od|ad)/i.test(ua)) { 9 | const i = document.createElement('iframe') 10 | i.src = '/favicon.ico' 11 | i.style.display = 'none' 12 | i.onload = function () { 13 | setTimeout(function () { 14 | i.remove() 15 | }, 9) 16 | } 17 | document.body.appendChild(i) 18 | } 19 | } 20 | 21 | export const domTitle = config.title 22 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const IS_PROD = ['production', 'prod'].includes(process.env.NODE_ENV) 2 | 3 | const plugins = [] 4 | if (IS_PROD) { 5 | plugins.push('transform-remove-console') 6 | } 7 | 8 | // lazy load ant-design-vue 9 | // if your use import on Demand, Use this code 10 | plugins.push(['import', { 11 | 'libraryName': 'ant-design-vue', 12 | 'libraryDirectory': 'es', 13 | 'style': true // `style: true` 会加载 less 文件 14 | }]) 15 | 16 | module.exports = { 17 | presets: [ 18 | '@vue/cli-plugin-babel/preset', 19 | [ 20 | '@babel/preset-env', 21 | { 22 | 'useBuiltIns': 'entry', 23 | 'corejs': 3 24 | } 25 | ] 26 | ], 27 | plugins 28 | } 29 | -------------------------------------------------------------------------------- /src/utils/helper/image.js: -------------------------------------------------------------------------------- 1 | import { IMAGE_PREFIX } from '@/config/axios.config.js' 2 | import { deepClone } from '@/utils/util' 3 | 4 | /** 5 | * 设置 图片的前缀 6 | * @param images 7 | * @returns {*} 8 | */ 9 | export function setImagePrefix(images) { 10 | const clone = deepClone(images) 11 | 12 | clone.forEach((image) => { 13 | image['_url'] = image['url'] 14 | image['url'] = IMAGE_PREFIX + image['url'] 15 | image['large'] = IMAGE_PREFIX + image['large'] 16 | image['medium'] = IMAGE_PREFIX + image['medium'] 17 | image['thumbUrl'] = IMAGE_PREFIX + image['thumbUrl'] 18 | image['source'] = IMAGE_PREFIX + image['source'] 19 | }) 20 | 21 | return clone 22 | } 23 | -------------------------------------------------------------------------------- /src/views/product/tag/upload-mixin.js: -------------------------------------------------------------------------------- 1 | const uploadMixin = { 2 | data() { 3 | return { 4 | fileList: [] 5 | } 6 | }, 7 | methods: { 8 | /** 9 | * 上传图片处理 10 | */ 11 | handleUpload(e) { 12 | const file = e.file 13 | this.$store.dispatch('UploadImage', file).then((fileList) => { 14 | this.fileList = fileList 15 | this.form.setFieldsValue({ 16 | icon: fileList[0]._url 17 | }) 18 | }).catch((message) => { 19 | this.fileList = [] 20 | this.form.setFieldsValue({ 21 | icon: '' 22 | }) 23 | this.$message.error(message) 24 | }) 25 | } 26 | } 27 | } 28 | 29 | export default uploadMixin 30 | -------------------------------------------------------------------------------- /src/components/SelectLang/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/es/style/themes/default"; 2 | 3 | @header-menu-prefix-cls: ~'@{ant-prefix}-pro-header-menu'; 4 | @header-drop-down-prefix-cls: ~'@{ant-prefix}-pro-drop-down'; 5 | 6 | .@{header-menu-prefix-cls} { 7 | 8 | .anticon { 9 | margin-right: 8px; 10 | } 11 | .ant-dropdown-menu-item { 12 | min-width: 160px; 13 | } 14 | } 15 | 16 | .@{header-drop-down-prefix-cls} { 17 | 18 | line-height: @layout-header-height; 19 | vertical-align: top; 20 | cursor: pointer; 21 | 22 | > i { 23 | font-size: 16px !important; 24 | transform: none !important; 25 | 26 | svg { 27 | position: relative; 28 | top: -1px; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/api/setting.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | 3 | /** 4 | * 更新系统设置 5 | * @param body 6 | * @returns {*} 7 | */ 8 | export function updateSetting(body) { 9 | const url = '/setting/update' 10 | 11 | return axios({ 12 | url: url, 13 | method: 'PUT', 14 | headers: Object.assign({}, HEADERS.PUT), 15 | data: { 16 | body: body 17 | } 18 | }).then((res) => { 19 | return Promise.resolve(res.data) 20 | }) 21 | } 22 | 23 | /** 24 | * 获取系统设置 25 | * @returns {*} 26 | */ 27 | export function getSettingList() { 28 | const url = '/setting' 29 | 30 | return axios({ 31 | url: url, 32 | method: 'GET' 33 | }).then((res) => { 34 | return Promise.resolve(res.data) 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /docs/add-page-loading-animate.md: -------------------------------------------------------------------------------- 1 | 为首屏增加 加载动画 2 | ==== 3 | 4 | 5 | 6 | ## 需求 7 | 8 | > 为了缓解用户第一次访问时,加载 JS 过大所导致用户等待白屏时间过长导致的用户体验不好,进行的一个优化动效。 9 | 10 | 11 | 12 | ## 实现方案 13 | 14 | 1. 将 动画加载 dom 元素放在 #app 内,Vue 生命周期开始时,会自动清掉 #app 下的所有元素。 15 | 2. 将 动画加载 dom 元素放在 body 下,Vue 生命周期开始时 App.vue (created, mounted) 调用 `@/utils/utll` 下的 removeLoadingAnimate(#id, timeout) 则会移除加载动画 16 | 17 | 最后一步: 18 | 将样式插入到 `public/index.html` 文件的 `` 最好写成内联 `` 19 | 20 | 21 | 22 | ---- 23 | 24 | 目前提供有两个样式,均在 `public/loading` 文件夹内。且 pro 已经默认使用了一套 loading 动画方案,可以直接参考 `public/index.html` 25 | 26 | 27 | ## 写在最后 28 | 29 | 目前 pro 有页面 overflow 显示出浏览器滚动条时,页面会抖动一下的问题。 30 | 31 | 欢迎各位提供能解决的方案和实现 demo。如果在条件允许的情况下,建议请直接使用 pro 进行改造,也欢迎直接 PR 到 pro 的仓库 32 | -------------------------------------------------------------------------------- /src/api/product_notify.js: -------------------------------------------------------------------------------- 1 | import axios from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 获取到货通知列表 6 | * @param query 7 | * @param sortby 8 | * @param order 9 | * @param pageNumber 10 | * @param pageSize 11 | * @returns {*} 12 | */ 13 | export function getProductNotifyList({ query, sortby, order, pageNumber, pageSize }) { 14 | const url = '/product_notify' 15 | 16 | return axios({ 17 | url: url, 18 | method: 'GET', 19 | params: { 20 | query: getObjectKeyValueToString(query), 21 | sortby: sortby, 22 | order: order, 23 | pageNumber: pageNumber, 24 | pageSize: pageSize 25 | } 26 | }).then((res) => { 27 | return Promise.resolve(res.data) 28 | }) 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bee-shop-b2c-admin 2 | 一个用Vue写的B2C商城管理端 3 | 4 | 技术栈: Vue2、ant-design-vue、Vuex、Vue Router、axios、 5 | 6 | ## web端和管理端源代码 7 | * [go-shop-b2c](https://github.com/CleverBamboo/go-shop-b2c) 8 | * [go-shop-b2c-web](https://github.com/CleverBamboo/go-shop-b2c-web) 9 | 10 | ## 安装依赖 11 | 建议使用 yarn,因为项目包含 yarn.lock 12 | ``` 13 | yarn install 14 | ``` 15 | 16 | ### 运行项目 17 | ``` 18 | yarn serve 19 | ``` 20 | 21 | ### 编译项目 22 | ``` 23 | yarn build 24 | ``` 25 | 26 | ## 效果图 27 | ### 管理端 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/components/ConfirmBack/ConfirmBack.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 确定 5 | 6 | 7 | 返回 8 | 9 | 10 | 11 | 12 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /src/api/sku.js: -------------------------------------------------------------------------------- 1 | import axios from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 获取 SKU 6 | * @param sn 7 | * @returns {Promise} 8 | */ 9 | export function getSku(sn) { 10 | const url = `/sku/${sn}` 11 | 12 | return axios({ 13 | url: url, 14 | method: 'GET' 15 | }).then((res) => { 16 | return Promise.resolve(res.data) 17 | }) 18 | } 19 | 20 | /** 21 | * 获取 SKU 列表 22 | * @param query 23 | * @returns {*} 24 | */ 25 | export function getSkuList(query) { 26 | const url = '/sku' 27 | 28 | return axios({ 29 | url: url, 30 | method: 'GET', 31 | params: { 32 | query: getObjectKeyValueToString(query) 33 | } 34 | }).then((res) => { 35 | return Promise.resolve(res.data) 36 | }) 37 | } 38 | -------------------------------------------------------------------------------- /src/store/modules/upload.js: -------------------------------------------------------------------------------- 1 | import { uploadImage } from '@/api/upload' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const upload = { 5 | state: {}, 6 | mutations: {}, 7 | actions: { 8 | /** 9 | * 上传图片 10 | * @param commit 11 | * @param file 12 | * @private 13 | */ 14 | UploadImage({ commit }, file) { 15 | const formData = new FormData() 16 | formData.append('upload', file, file.name) 17 | 18 | return new Promise((resolve, reject) => { 19 | uploadImage(formData).then((res) => { 20 | if (res.err_code === ERR_OK) { 21 | resolve(res.data) 22 | } else { 23 | reject(res.data) 24 | } 25 | }) 26 | }) 27 | } 28 | } 29 | } 30 | 31 | export default upload 32 | -------------------------------------------------------------------------------- /src/store/modules/member.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 会员 3 | */ 4 | import { getMember } from '@/api/member' 5 | import { ERR_OK } from '@/api/config' 6 | 7 | const member = { 8 | state: { 9 | member: {} 10 | }, 11 | mutations: { 12 | SET_MEMBER: (state, member) => { 13 | state.member = member 14 | } 15 | }, 16 | actions: { 17 | /** 18 | * 获取会员 19 | * @param commit 20 | * @param id 21 | * @constructor 22 | */ 23 | GetMember({ commit }, id) { 24 | return new Promise(resolve => { 25 | getMember(id).then((res) => { 26 | if (res.err_code === ERR_OK) { 27 | commit('SET_MEMBER', res.data) 28 | resolve(res.data) 29 | } 30 | }) 31 | }) 32 | } 33 | } 34 | } 35 | 36 | export default member 37 | -------------------------------------------------------------------------------- /src/store/modules/permission.js: -------------------------------------------------------------------------------- 1 | import { getPermissionList } from '@/api/permission' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const permission = { 5 | state: { 6 | permissions: [] 7 | }, 8 | mutations: { 9 | SET_PERMISSIONS: (state, list) => { 10 | state.permissions = list 11 | } 12 | }, 13 | actions: { 14 | /** 15 | * 获取权限列表 16 | * @param commit 17 | * @constructor 18 | */ 19 | GetPermissions({ commit }) { 20 | return new Promise(resolve => { 21 | getPermissionList({}).then((res) => { 22 | if (res.err_code === ERR_OK) { 23 | commit('SET_PERMISSIONS', res.data) 24 | resolve(res.data) 25 | } 26 | }) 27 | }) 28 | } 29 | } 30 | } 31 | 32 | export default permission 33 | -------------------------------------------------------------------------------- /src/components/Trend/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @trend-prefix-cls: ~"@{ant-pro-prefix}-trend"; 4 | 5 | .@{trend-prefix-cls} { 6 | display: inline-block; 7 | font-size: @font-size-base; 8 | line-height: 22px; 9 | 10 | .up, 11 | .down { 12 | margin-left: 4px; 13 | position: relative; 14 | top: 1px; 15 | 16 | i { 17 | font-size: 12px; 18 | transform: scale(0.83); 19 | } 20 | } 21 | 22 | .item-text { 23 | display: inline-block; 24 | margin-left: 8px; 25 | color: rgba(0,0,0,.85); 26 | } 27 | 28 | .up { 29 | color: @red-6; 30 | } 31 | .down { 32 | color: @green-6; 33 | top: -1px; 34 | } 35 | 36 | &.reverse-color .up { 37 | color: @green-6; 38 | } 39 | &.reverse-color .down { 40 | color: @red-6; 41 | } 42 | } -------------------------------------------------------------------------------- /src/store/modules/ad.js: -------------------------------------------------------------------------------- 1 | import { getAdPositionList } from '@/api/ad' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const ad = { 5 | state: { 6 | adPositionList: [] 7 | }, 8 | mutations: { 9 | SET_AD_POSITION_LIST: (state, adPositionList) => { 10 | state.adPositionList = adPositionList 11 | } 12 | }, 13 | actions: { 14 | /** 15 | * 获取所有广告位置 16 | * @param commit 17 | * @constructor 18 | */ 19 | GetAllAdPosition({ commit }) { 20 | return new Promise(resolve => { 21 | getAdPositionList().then((res) => { 22 | if (res.err_code === ERR_OK) { 23 | commit('SET_AD_POSITION_LIST', res.data) 24 | resolve(res.data) 25 | } 26 | }) 27 | }) 28 | } 29 | } 30 | } 31 | 32 | export default ad 33 | -------------------------------------------------------------------------------- /src/components/GlobalFooter/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pro Layout 5 | Github 6 | @Sendya 7 | 8 | 9 | vueComponent 10 | 11 | 12 | 13 | 14 | 24 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset=utf-8 3 | end_of_line=lf 4 | insert_final_newline=false 5 | indent_style=space 6 | indent_size=2 7 | 8 | [{*.ng,*.sht,*.html,*.shtm,*.shtml,*.htm}] 9 | indent_style=space 10 | indent_size=2 11 | 12 | [{*.jhm,*.xslt,*.xul,*.rng,*.xsl,*.xsd,*.ant,*.tld,*.fxml,*.jrxml,*.xml,*.jnlp,*.wsdl}] 13 | indent_style=space 14 | indent_size=2 15 | 16 | [{.babelrc,.stylelintrc,jest.config,.eslintrc,.prettierrc,*.json,*.jsb3,*.jsb2,*.bowerrc}] 17 | indent_style=space 18 | indent_size=2 19 | 20 | [*.svg] 21 | indent_style=space 22 | indent_size=2 23 | 24 | [*.js.map] 25 | indent_style=space 26 | indent_size=2 27 | 28 | [*.less] 29 | indent_style=space 30 | indent_size=2 31 | 32 | [*.vue] 33 | indent_style=space 34 | indent_size=2 35 | 36 | [{.analysis_options,*.yml,*.yaml}] 37 | indent_style=space 38 | indent_size=2 39 | 40 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/SettingItem.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 5 | 6 | 7 | 8 | 9 | 24 | 25 | 39 | -------------------------------------------------------------------------------- /src/components/Ellipsis/index.md: -------------------------------------------------------------------------------- 1 | # Ellipsis 文本自动省略号 2 | 3 | 文本过长自动处理省略号,支持按照文本长度和最大行数两种方式截取。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import Ellipsis from '@/components/Ellipsis' 11 | 12 | export default { 13 | components: { 14 | Ellipsis 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 25 | There were injuries alleged in three cases in 2015, and a 26 | fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall. 27 | 28 | ``` 29 | 30 | 31 | 32 | ## API 33 | 34 | 35 | 参数 | 说明 | 类型 | 默认值 36 | ----|------|-----|------ 37 | tooltip | 移动到文本展示完整内容的提示 | boolean | - 38 | length | 在按照长度截取下的文本最大字符数,超过则截取省略 | number | - -------------------------------------------------------------------------------- /src/store/modules/parameter.js: -------------------------------------------------------------------------------- 1 | import { getParameterList } from '@/api/parameter' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const parameter = { 5 | state: { 6 | pageList: [] 7 | }, 8 | mutations: { 9 | SET_PRODUCT_PARAMETER_PAGE_LIST: (state, pageList) => { 10 | state.pageList = pageList 11 | } 12 | }, 13 | actions: { 14 | GetParameterList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 15 | return new Promise(resolve => { 16 | getParameterList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 17 | if (res.err_code === ERR_OK) { 18 | const pageList = res.data 19 | commit('SET_PRODUCT_PARAMETER_PAGE_LIST', pageList) 20 | resolve() 21 | } 22 | }) 23 | }) 24 | } 25 | } 26 | } 27 | 28 | export default parameter 29 | -------------------------------------------------------------------------------- /src/layouts/BasicLayout.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/es/style/themes/default.less"; 2 | 3 | .ant-pro-global-header-index-right { 4 | margin-right: 8px; 5 | 6 | &.ant-pro-global-header-index-dark { 7 | .ant-pro-global-header-index-action { 8 | color: hsla(0, 0%, 100%, .85); 9 | 10 | &:hover { 11 | background: #1890ff; 12 | } 13 | } 14 | } 15 | 16 | .ant-pro-account-avatar { 17 | .antd-pro-global-header-index-avatar { 18 | margin: ~'calc((@{layout-header-height} - 24px) / 2)' 0; 19 | margin-right: 8px; 20 | color: @primary-color; 21 | vertical-align: top; 22 | background: rgba(255, 255, 255, 0.85); 23 | } 24 | } 25 | 26 | .menu { 27 | .anticon { 28 | margin-right: 8px; 29 | } 30 | 31 | .ant-dropdown-menu-item { 32 | min-width: 100px; 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/store/modules/specification.js: -------------------------------------------------------------------------------- 1 | import { getSpecificationList } from '@/api/specification' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const specification = { 5 | state: { 6 | pageList: [] 7 | }, 8 | mutations: { 9 | SET_SPECIFICATION_LIST: (state, pageList) => { 10 | state.pageList = pageList 11 | } 12 | }, 13 | actions: { 14 | GetSpecificationList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 15 | return new Promise(resolve => { 16 | getSpecificationList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 17 | if (res.err_code === ERR_OK) { 18 | const pageList = res.data 19 | commit('SET_SPECIFICATION_LIST', pageList) 20 | resolve() 21 | } 22 | }) 23 | }) 24 | } 25 | } 26 | } 27 | 28 | export default specification 29 | -------------------------------------------------------------------------------- /src/components/Trend/index.md: -------------------------------------------------------------------------------- 1 | # Trend 趋势标记 2 | 3 | 趋势符号,标记上升和下降趋势。通常用绿色代表“好”,红色代表“不好”,股票涨跌场景除外。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import Trend from '@/components/Trend' 11 | 12 | export default { 13 | components: { 14 | Trend 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 5% 25 | ``` 26 | 或 27 | ```html 28 | 29 | 工资 30 | 5% 31 | 32 | ``` 33 | 或 34 | ```html 35 | 5% 36 | ``` 37 | 38 | 39 | ## API 40 | 41 | | 参数 | 说明 | 类型 | 默认值 | 42 | |----------|------------------------------------------|-------------|-------| 43 | | flag | 上升下降标识:`up|down` | string | - | 44 | | reverseColor | 颜色反转 | Boolean | false | 45 | 46 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/index.md: -------------------------------------------------------------------------------- 1 | # FooterToolbar 底部工具栏 2 | 3 | 固定在底部的工具栏。 4 | 5 | 6 | 7 | ## 何时使用 8 | 9 | 固定在内容区域的底部,不随滚动条移动,常用于长页面的数据搜集和提交工作。 10 | 11 | 12 | 13 | 引用方式: 14 | 15 | ```javascript 16 | import FooterToolBar from '@/components/FooterToolbar' 17 | 18 | export default { 19 | components: { 20 | FooterToolBar 21 | } 22 | } 23 | ``` 24 | 25 | 26 | 27 | ## 代码演示 28 | 29 | ```html 30 | 31 | 提交 32 | 33 | ``` 34 | 或 35 | ```html 36 | 37 | 提交 38 | 39 | ``` 40 | 41 | 42 | ## API 43 | 44 | 参数 | 说明 | 类型 | 默认值 45 | ----|------|-----|------ 46 | children (slot) | 工具栏内容,向右对齐 | - | - 47 | extra | 额外信息,向左对齐 | String, Object | - 48 | 49 | -------------------------------------------------------------------------------- /src/components/Trend/Trend.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /src/components/NumberInfo/index.md: -------------------------------------------------------------------------------- 1 | # NumberInfo 数据文本 2 | 3 | 常用在数据卡片中,用于突出展示某个业务数据。 4 | 5 | 6 | 7 | 引用方式: 8 | 9 | ```javascript 10 | import NumberInfo from '@/components/NumberInfo' 11 | 12 | export default { 13 | components: { 14 | NumberInfo 15 | } 16 | } 17 | ``` 18 | 19 | 20 | 21 | ## 代码演示 [demo](https://pro.loacg.com/test/home) 22 | 23 | ```html 24 | 29 | ``` 30 | 31 | 32 | 33 | ## API 34 | 35 | 参数 | 说明 | 类型 | 默认值 36 | ----|------|-----|------ 37 | title | 标题 | ReactNode\|string | - 38 | subTitle | 子标题 | ReactNode\|string | - 39 | total | 总量 | ReactNode\|string | - 40 | subTotal | 子总量 | ReactNode\|string | - 41 | status | 增加状态 | 'up \| down' | - 42 | theme | 状态样式 | string | 'light' 43 | gap | 设置数字和描述之间的间距(像素)| number | 8 44 | -------------------------------------------------------------------------------- /src/layouts/RouteView.vue: -------------------------------------------------------------------------------- 1 | 33 | -------------------------------------------------------------------------------- /src/utils/filter.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import moment from 'moment' 3 | import 'moment/locale/zh-cn' 4 | import store from '@/store' 5 | 6 | moment.locale('zh-cn') 7 | 8 | Vue.filter('NumberFormat', function(value) { 9 | if (!value) { 10 | return '0' 11 | } 12 | // 将整数部分逢三一断 13 | return value.toString().replace(/(\d)(?=(?:\d{3})+$)/g, '$1,') 14 | }) 15 | 16 | Vue.filter('dayjs', function(dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') { 17 | return moment(dataStr).format(pattern) 18 | }) 19 | 20 | Vue.filter('Moment', function(dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') { 21 | return moment(dataStr).format(pattern) 22 | }) 23 | 24 | Vue.filter('Amount', function(amount) { 25 | const currencySign = store.getters.setting.currencySign 26 | return currencySign && currencySign + parseFloat(amount).toFixed(2) 27 | }) 28 | 29 | Vue.filter('Empty', function(data) { 30 | return data || '-' 31 | }) 32 | -------------------------------------------------------------------------------- /src/store/mutation-types.js: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN = 'Access-Token' 2 | 3 | export const SIDEBAR_TYPE = 'sidebar_type' 4 | export const TOGGLE_MOBILE_TYPE = 'is_mobile' 5 | export const TOGGLE_NAV_THEME = 'nav_theme' 6 | export const TOGGLE_LAYOUT = 'layout' 7 | export const TOGGLE_FIXED_HEADER = 'fixed_header' 8 | export const TOGGLE_FIXED_SIDEBAR = 'fixed_sidebar' 9 | export const TOGGLE_CONTENT_WIDTH = 'content_width' 10 | export const TOGGLE_HIDE_HEADER = 'auto_hide_header' 11 | export const TOGGLE_COLOR = 'color' 12 | export const TOGGLE_WEAK = 'weak' 13 | export const TOGGLE_MULTI_TAB = 'multi_tab' 14 | export const APP_LANGUAGE = 'app_language' 15 | export const APP_RELOAD = 'app_reload' 16 | 17 | export const CONTENT_WIDTH_TYPE = { 18 | Fluid: 'Fluid', 19 | Fixed: 'Fixed' 20 | } 21 | 22 | export const NAV_THEME = { 23 | LIGHT: 'light', 24 | DARK: 'dark' 25 | } 26 | 27 | export const SET_ORDER = 'set_order' 28 | -------------------------------------------------------------------------------- /docs/webpack-bundle-analyzer.md: -------------------------------------------------------------------------------- 1 | 先增加依赖 2 | 3 | ```bash 4 | // npm 5 | $ npm install --save-dev webpack-bundle-analyzer 6 | 7 | // or yarn 8 | $ yarn add webpack-bundle-analyzer -D 9 | ``` 10 | 11 | 配置文件 `vue.config.js` 增加 `configureWebpack.plugins` 参数 12 | 13 | ``` 14 | const path = require('path') 15 | const webpack = require('webpack') 16 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 17 | 18 | function resolve (dir) { 19 | return path.join(__dirname, dir) 20 | } 21 | 22 | // vue.config.js 23 | module.exports = { 24 | configureWebpack: { 25 | plugins: [ 26 | // Ignore all locale files of moment.js 27 | new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), 28 | // 依赖大小分析工具 29 | new BundleAnalyzerPlugin(), 30 | ] 31 | }, 32 | 33 | 34 | ... 35 | } 36 | ``` 37 | 38 | 39 | 40 | 启动 `cli` 的 `build` 命令进行项目编译,编译完成时,会自动运行一个 http://localhost:8888 的地址,完整显示了支持库依赖 -------------------------------------------------------------------------------- /src/components/IconSelector/README.md: -------------------------------------------------------------------------------- 1 | IconSelector 2 | ==== 3 | 4 | > 图标选择组件,常用于为某一个数据设定一个图标时使用 5 | > eg: 设定菜单列表时,为每个菜单设定一个图标 6 | 7 | 该组件由 [@Saraka](https://github.com/saraka-tsukai) 封装 8 | 9 | 10 | 11 | ### 使用方式 12 | 13 | ```vue 14 | 15 | 16 | 17 | 18 | 19 | 20 | 39 | ``` 40 | 41 | 42 | 43 | ### 事件 44 | 45 | 46 | | 名称 | 说明 | 类型 | 默认值 | 47 | | ------ | -------------------------- | ------ | ------ | 48 | | change | 当改变了 `icon` 选中项触发 | String | - | 49 | -------------------------------------------------------------------------------- /src/store/app-mixin.js: -------------------------------------------------------------------------------- 1 | import { mapState } from 'vuex' 2 | 3 | const baseMixin = { 4 | computed: { 5 | ...mapState({ 6 | layout: state => state.app.layout, 7 | navTheme: state => state.app.theme, 8 | primaryColor: state => state.app.color, 9 | colorWeak: state => state.app.weak, 10 | fixedHeader: state => state.app.fixedHeader, 11 | fixedSidebar: state => state.app.fixedSidebar, 12 | contentWidth: state => state.app.contentWidth, 13 | autoHideHeader: state => state.app.autoHideHeader, 14 | 15 | isMobile: state => state.app.isMobile, 16 | sideCollapsed: state => state.app.sideCollapsed, 17 | multiTab: state => state.app.multiTab 18 | }), 19 | isTopMenu () { 20 | return this.layout === 'topmenu' 21 | } 22 | }, 23 | methods: { 24 | isSideMenu () { 25 | return !this.isTopMenu 26 | } 27 | } 28 | } 29 | 30 | export { 31 | baseMixin 32 | } 33 | -------------------------------------------------------------------------------- /src/components/MultiTab/index.js: -------------------------------------------------------------------------------- 1 | import events from './events' 2 | import MultiTab from './MultiTab' 3 | import './index.less' 4 | 5 | const api = { 6 | /** 7 | * open new tab on route fullPath 8 | * @param config 9 | */ 10 | open: function (config) { 11 | events.$emit('open', config) 12 | }, 13 | rename: function (key, name) { 14 | events.$emit('rename', { key: key, name: name }) 15 | }, 16 | /** 17 | * close current page 18 | */ 19 | closeCurrentPage: function () { 20 | this.close() 21 | }, 22 | /** 23 | * close route fullPath tab 24 | * @param config 25 | */ 26 | close: function (config) { 27 | events.$emit('close', config) 28 | } 29 | } 30 | 31 | MultiTab.install = function (Vue) { 32 | if (Vue.prototype.$multiTab) { 33 | return 34 | } 35 | api.instance = events 36 | Vue.prototype.$multiTab = api 37 | Vue.component('multi-tab', MultiTab) 38 | } 39 | 40 | export default MultiTab 41 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import { constantRouterMap } from '@/config/router.config' 4 | 5 | // hack router push callback 6 | const originalPush = Router.prototype.push 7 | Router.prototype.push = function push(location, onResolve, onReject) { 8 | if (onResolve || onReject) return originalPush.call(this, location, onResolve, onReject) 9 | return originalPush.call(this, location).catch(err => err) 10 | } 11 | 12 | Vue.use(Router) 13 | 14 | const createRouter = () => new Router({ 15 | mode: 'hash', // require service support 16 | scrollBehavior: () => ({ y: 0 }), 17 | routes: constantRouterMap 18 | }) 19 | 20 | const router = createRouter() 21 | 22 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 23 | export function resetRouter() { 24 | const newRouter = createRouter() 25 | router.matcher = newRouter.matcher // reset router 26 | } 27 | 28 | export default router 29 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/themeColor.js: -------------------------------------------------------------------------------- 1 | import client from 'webpack-theme-color-replacer/client' 2 | import generate from '@ant-design/colors/lib/generate' 3 | 4 | export default { 5 | getAntdSerials (color) { 6 | // 淡化(即less的tint) 7 | const lightens = new Array(9).fill().map((t, i) => { 8 | return client.varyColor.lighten(color, i / 10) 9 | }) 10 | // colorPalette变换得到颜色值 11 | const colorPalettes = generate(color) 12 | const rgb = client.varyColor.toNum3(color.replace('#', '')).join(',') 13 | return lightens.concat(colorPalettes).concat(rgb) 14 | }, 15 | changeColor (newColor) { 16 | var options = { 17 | newColors: this.getAntdSerials(newColor), // new colors array, one-to-one corresponde with `matchColors` 18 | changeUrl (cssUrl) { 19 | return `/${cssUrl}` // while router is not `hash` mode, it needs absolute path 20 | } 21 | } 22 | return client.changer.changeColor(options, Promise) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Charts/MiniSmoothArea.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 37 | 38 | 41 | -------------------------------------------------------------------------------- /src/components/AvatarList/Item.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ tips }} 4 | 5 | 6 | 7 | 8 | 9 | 47 | -------------------------------------------------------------------------------- /src/store/modules/attribute.js: -------------------------------------------------------------------------------- 1 | import { getAttributeList } from '@/api/attribute' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const attribute = { 5 | state: { 6 | pageList: [] 7 | }, 8 | mutations: { 9 | SET_ATTRIBUTE_LIST: (state, pageList) => { 10 | state.pageList = pageList 11 | } 12 | }, 13 | actions: { 14 | /** 15 | * 获取商品属性列表 16 | * @param commit 17 | * @param query 18 | * @param sortby 19 | * @param order 20 | * @param pageNumber 21 | * @param pageSize 22 | * @constructor 23 | */ 24 | GetAttributePageList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 25 | return new Promise(resolve => { 26 | getAttributeList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 27 | if (res.err_code === ERR_OK) { 28 | const pageList = res.data 29 | commit('SET_ATTRIBUTE_LIST', pageList) 30 | resolve() 31 | } 32 | }) 33 | }) 34 | } 35 | } 36 | } 37 | 38 | export default attribute 39 | -------------------------------------------------------------------------------- /src/utils/utils.less: -------------------------------------------------------------------------------- 1 | .textOverflow() { 2 | overflow: hidden; 3 | white-space: nowrap; 4 | text-overflow: ellipsis; 5 | word-break: break-all; 6 | } 7 | 8 | .textOverflowMulti(@line: 3, @bg: #fff) { 9 | position: relative; 10 | max-height: @line * 1.5em; 11 | margin-right: -1em; 12 | padding-right: 1em; 13 | overflow: hidden; 14 | line-height: 1.5em; 15 | text-align: justify; 16 | &::before { 17 | position: absolute; 18 | right: 14px; 19 | bottom: 0; 20 | padding: 0 1px; 21 | background: @bg; 22 | content: '...'; 23 | } 24 | &::after { 25 | position: absolute; 26 | right: 14px; 27 | width: 1em; 28 | height: 1em; 29 | margin-top: 0.2em; 30 | background: white; 31 | content: ''; 32 | } 33 | } 34 | 35 | // mixins for clearfix 36 | // ------------------------ 37 | .clearfix() { 38 | zoom: 1; 39 | &::before, 40 | &::after { 41 | display: table; 42 | content: ' '; 43 | } 44 | &::after { 45 | clear: both; 46 | height: 0; 47 | font-size: 0; 48 | visibility: hidden; 49 | } 50 | } -------------------------------------------------------------------------------- /src/config/defaultSettings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 项目默认配置项 3 | * primaryColor - 默认主题色, 如果修改颜色不生效,请清理 localStorage 4 | * navTheme - sidebar theme ['dark', 'light'] 两种主题 5 | * colorWeak - 色盲模式 6 | * layout - 整体布局方式 ['sidemenu', 'topmenu'] 两种布局 7 | * fixedHeader - 固定 Header : boolean 8 | * fixSiderbar - 固定左侧菜单栏 : boolean 9 | * contentWidth - 内容区布局: 流式 | 固定 10 | * 11 | * storageOptions: {} - Vue-ls 插件配置项 (localStorage/sessionStorage) 12 | * 13 | */ 14 | 15 | export default { 16 | navTheme: 'dark', // theme for nav menu 17 | primaryColor: '#52C41A', // primary color of ant design 18 | layout: 'sidemenu', // nav menu position: `sidemenu` or `topmenu` 19 | contentWidth: 'Fluid', // layout of content: `Fluid` or `Fixed`, only works when layout is topmenu 20 | fixedHeader: false, // sticky header 21 | fixSiderbar: false, // sticky siderbar 22 | colorWeak: false, 23 | menu: { 24 | locale: true 25 | }, 26 | title: 'Ant Design Pro', 27 | pwa: false, 28 | iconfontUrl: '', 29 | production: process.env.NODE_ENV === 'production' && process.env.VUE_APP_PREVIEW !== 'true' 30 | } 31 | -------------------------------------------------------------------------------- /src/api/user.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | 3 | /** 4 | * 登录 5 | * @param account 6 | * @param password 7 | * @returns {Promise<*>} 8 | */ 9 | export function login({ username, password }) { 10 | const url = '/user/login' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'POST', 15 | headers: Object.assign({}, HEADERS.POST), 16 | data: { 17 | username: username, 18 | password: password 19 | } 20 | }).then((res) => { 21 | return Promise.resolve(res.data) 22 | }) 23 | } 24 | 25 | /** 26 | * 注册 27 | * @param account 28 | * @param password1 29 | * @param password2 30 | * @returns {Promise<*>} 31 | */ 32 | export function register({ username, password1, password2 }) { 33 | const url = '/user/register' 34 | 35 | return axios({ 36 | url: url, 37 | method: 'POST', 38 | headers: Object.assign({}, HEADERS.POST), 39 | data: { 40 | account: username, 41 | password1: password1, 42 | password2: password2 43 | } 44 | }).then((res) => { 45 | return Promise.resolve(res.data) 46 | }) 47 | } 48 | -------------------------------------------------------------------------------- /src/components/FooterToolbar/FooterToolBar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ extra }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 44 | 45 | 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Anan Yang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/views/order/returns/components/ReturnsItems.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | {{ creation_date | Moment }} 12 | 13 | {{ creation_date | Moment('YYYY-MM-DD') }} 14 | 15 | 16 | 17 | 18 | 19 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /src/components/_util/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * components util 3 | */ 4 | 5 | /** 6 | * 清理空值,对象 7 | * @param children 8 | * @returns {*[]} 9 | */ 10 | export function filterEmpty (children = []) { 11 | return children.filter(c => c.tag || (c.text && c.text.trim() !== '')) 12 | } 13 | 14 | /** 15 | * 获取字符串长度,英文字符 长度1,中文字符长度2 16 | * @param {*} str 17 | */ 18 | export const getStrFullLength = (str = '') => 19 | str.split('').reduce((pre, cur) => { 20 | const charCode = cur.charCodeAt(0) 21 | if (charCode >= 0 && charCode <= 128) { 22 | return pre + 1 23 | } 24 | return pre + 2 25 | }, 0) 26 | 27 | /** 28 | * 截取字符串,根据 maxLength 截取后返回 29 | * @param {*} str 30 | * @param {*} maxLength 31 | */ 32 | export const cutStrByFullLength = (str = '', maxLength) => { 33 | let showLength = 0 34 | return str.split('').reduce((pre, cur) => { 35 | const charCode = cur.charCodeAt(0) 36 | if (charCode >= 0 && charCode <= 128) { 37 | showLength += 1 38 | } else { 39 | showLength += 2 40 | } 41 | if (showLength <= maxLength) { 42 | return pre + cur 43 | } 44 | return pre 45 | }, '') 46 | } 47 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // with polyfills 2 | import 'core-js/stable' 3 | import 'regenerator-runtime/runtime' 4 | 5 | import Vue from 'vue' 6 | import App from './App.vue' 7 | import router from './router' 8 | import store from './store/' 9 | import i18n from './locales' 10 | import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout' 11 | import themePluginConfig from '../config/themePluginConfig' 12 | import VueCookies from 'vue-cookies' 13 | 14 | // WARNING: `mockjs` NOT SUPPORT `IE` PLEASE DO NOT USE IN `production` ENV. 15 | import bootstrap from './core/bootstrap' 16 | import './core/lazy_use' 17 | import './permission' // permission control 18 | import './utils/filter' // global filter 19 | import './global.less' 20 | 21 | Vue.config.productionTip = false 22 | 23 | // mount axios to `Vue.$http` and `this.$http` 24 | Vue.use(VueCookies) 25 | Vue.component('pro-layout', ProLayout) 26 | Vue.component('page-header-wrapper', PageHeaderWrapper) 27 | 28 | window.umi_plugin_ant_themeVar = themePluginConfig.theme 29 | 30 | new Vue({ 31 | router, 32 | store, 33 | i18n, 34 | created: bootstrap, 35 | render: h => h(App) 36 | }).$mount('#app') 37 | -------------------------------------------------------------------------------- /src/components/InputDropdown/index.less: -------------------------------------------------------------------------------- 1 | @import "~ant-design-vue/es/style/themes/default"; 2 | 3 | .input-dropdown-wrap { 4 | position: relative; 5 | 6 | .input-disabled { 7 | color: @btn-disable-color; 8 | background: @btn-disable-bg; 9 | border-color: @btn-disable-border; 10 | } 11 | 12 | .input-dropdown { 13 | position: absolute; 14 | top: 100%; 15 | left: 0; 16 | z-index: 1000; 17 | width: 100%; 18 | padding: @select-dropdown-vertical-padding; 19 | max-height: 321px; 20 | overflow: hidden; 21 | background: @select-dropdown-bg; 22 | border-width: 1px; 23 | border-style: solid; 24 | border-color: @input-border-color; 25 | border-radius: 4px; 26 | 27 | 28 | .input-wrap { 29 | width: 100%; 30 | background: #fff; 31 | } 32 | 33 | .dropdown-ul { 34 | overflow: auto; 35 | max-height: 270px; 36 | padding: 0; 37 | 38 | .dropdown-li { 39 | line-height: 30px; 40 | font-size: @select-dropdown-font-size; 41 | 42 | &:hover { 43 | background: @select-item-active-bg; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/core/directives/action.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import store from '@/store' 3 | 4 | /** 5 | * Action 权限指令 6 | * 指令用法: 7 | * - 在需要控制 action 级别权限的组件上使用 v-action:[method] , 如下: 8 | * 添加用户 9 | * 删除用户 10 | * 修改 11 | * 12 | * - 当前用户没有权限时,组件上使用了该指令则会被隐藏 13 | * - 当后台权限跟 pro 提供的模式不同时,只需要针对这里的权限过滤进行修改即可 14 | * 15 | * @see https://github.com/sendya/ant-design-pro-vue/pull/53 16 | */ 17 | const action = Vue.directive('action', { 18 | inserted: function (el, binding, vnode) { 19 | const actionName = binding.arg 20 | const roles = store.getters.roles 21 | const elVal = vnode.context.$route.meta.permission 22 | const permissionId = elVal instanceof String && [elVal] || elVal 23 | roles.permissions.forEach(p => { 24 | if (!permissionId.includes(p.permissionId)) { 25 | return 26 | } 27 | if (p.actionList && !p.actionList.includes(actionName)) { 28 | el.parentNode && el.parentNode.removeChild(el) || (el.style.display = 'none') 29 | } 30 | }) 31 | } 32 | }) 33 | 34 | export default action 35 | -------------------------------------------------------------------------------- /src/components/TagSelect/TagSelectOption.jsx: -------------------------------------------------------------------------------- 1 | import { Tag } from 'ant-design-vue' 2 | const { CheckableTag } = Tag 3 | 4 | export default { 5 | name: 'TagSelectOption', 6 | props: { 7 | prefixCls: { 8 | type: String, 9 | default: 'ant-pro-tag-select-option' 10 | }, 11 | value: { 12 | type: [String, Number, Object], 13 | default: '' 14 | }, 15 | checked: { 16 | type: Boolean, 17 | default: false 18 | } 19 | }, 20 | data () { 21 | return { 22 | localChecked: this.checked || false 23 | } 24 | }, 25 | watch: { 26 | 'checked' (val) { 27 | this.localChecked = val 28 | }, 29 | '$parent.items': { 30 | handler: function (val) { 31 | this.value && val.hasOwnProperty(this.value) && (this.localChecked = val[this.value]) 32 | }, 33 | deep: true 34 | } 35 | }, 36 | render () { 37 | const { $slots, value } = this 38 | const onChange = (checked) => { 39 | this.$emit('change', { value, checked }) 40 | } 41 | return ( 42 | {$slots.default} 43 | ) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/components/Charts/MiniBar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 54 | 55 | 58 | -------------------------------------------------------------------------------- /src/components/Charts/MiniArea.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 53 | 54 | 57 | -------------------------------------------------------------------------------- /src/components/NumberInfo/NumberInfo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{ typeof subTitle === 'string' ? subTitle : subTitle() }} 5 | 6 | 7 | {{ total }} 8 | 9 | {{ subTotal }} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 51 | 52 | 55 | -------------------------------------------------------------------------------- /src/mixins/upload.js: -------------------------------------------------------------------------------- 1 | import { getBase64 } from '@/utils/helper/base64' 2 | 3 | const upload = { 4 | data() { 5 | return { 6 | previewVisible: false, // 是否预览 7 | previewImage: '', // 预览图片 8 | fileList: [], // 图片文件 用于辅助表单显示 9 | noPrefixFileList: [] 10 | } 11 | }, 12 | methods: { 13 | normFile(e) { 14 | if (Array.isArray(e)) { 15 | return e 16 | } 17 | return e && e.fileList 18 | }, 19 | handleCancel() { 20 | this.previewVisible = false 21 | }, 22 | /** 23 | * 预览图片处理 24 | * @param file 25 | * @returns {Promise} 26 | */ 27 | async handlePreview(file) { 28 | if (!file.url && !file.preview) { 29 | file.preview = await getBase64(file.originFileObj) 30 | } 31 | this.previewImage = file.url || file.preview 32 | this.previewVisible = true 33 | }, 34 | /** 35 | * 删除图片处理 36 | */ 37 | handleRemove(file) { 38 | const index = this.fileList.indexOf(file) 39 | const newFileList = this.fileList.slice() 40 | newFileList.splice(index, 1) 41 | this.noPrefixFileList.splice(index, 1) 42 | this.fileList = newFileList 43 | } 44 | } 45 | } 46 | 47 | export default upload 48 | -------------------------------------------------------------------------------- /src/mixins/introduction.js: -------------------------------------------------------------------------------- 1 | import { Tinymce } from '@/components' 2 | import { IMAGE_PREFIX } from '@/config/axios.config.js' 3 | import upload from '@/mixins/upload' 4 | 5 | export default { 6 | components: { 7 | Tinymce 8 | }, 9 | mixins: [upload], 10 | name: 'Introduction', 11 | data() { 12 | return { 13 | introduction: '' 14 | } 15 | }, 16 | methods: { 17 | /** 18 | * 设置表单 Filed Value 19 | */ 20 | setFormFieldsValue(form) { 21 | this.introduction = form.introduction 22 | }, 23 | /** 24 | * 上传图片处理 25 | * @param blobInfo 26 | * @param success 27 | * @param failure 28 | */ 29 | handleUpload({ blobInfo, success, failure }) { 30 | const file = blobInfo.blob()// 转化为易于理解的file对象 31 | this.$store.dispatch('UploadImage', file).then((fileList) => { 32 | fileList.forEach((item) => { 33 | item.thumbUrl = IMAGE_PREFIX + item.thumbUrl 34 | success(item.thumbUrl) 35 | }) 36 | }) 37 | }, 38 | /** 39 | * 提交表单 40 | */ 41 | handleSubmit() { 42 | return new Promise(resolve => { 43 | resolve({ 44 | introduction: this.introduction 45 | }) 46 | }) 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/components/SettingDrawer/settingConfig.js: -------------------------------------------------------------------------------- 1 | import message from 'ant-design-vue/es/message' 2 | // import defaultSettings from '../defaultSettings'; 3 | import themeColor from './themeColor.js' 4 | 5 | // let lessNodesAppended 6 | const colorList = [ 7 | { 8 | key: '薄暮', color: '#F5222D' 9 | }, 10 | { 11 | key: '火山', color: '#FA541C' 12 | }, 13 | { 14 | key: '日暮', color: '#FAAD14' 15 | }, 16 | { 17 | key: '明青', color: '#13C2C2' 18 | }, 19 | { 20 | key: '极光绿', color: '#52C41A' 21 | }, 22 | { 23 | key: '拂晓蓝(默认)', color: '#1890FF' 24 | }, 25 | { 26 | key: '极客蓝', color: '#2F54EB' 27 | }, 28 | { 29 | key: '酱紫', color: '#722ED1' 30 | } 31 | ] 32 | 33 | const updateTheme = newPrimaryColor => { 34 | const hideMessage = message.loading('正在切换主题!', 0) 35 | themeColor.changeColor(newPrimaryColor).finally(() => { 36 | setTimeout(() => { 37 | hideMessage() 38 | }, 10) 39 | }) 40 | } 41 | 42 | const updateColorWeak = colorWeak => { 43 | // document.body.className = colorWeak ? 'colorWeak' : ''; 44 | const app = document.body.querySelector('#app') 45 | colorWeak ? app.classList.add('colorWeak') : app.classList.remove('colorWeak') 46 | } 47 | 48 | export { updateTheme, colorList, updateColorWeak } 49 | -------------------------------------------------------------------------------- /src/components/AvatarList/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @avatar-list-prefix-cls: ~"@{ant-pro-prefix}-avatar-list"; 4 | @avatar-list-item-prefix-cls: ~"@{ant-pro-prefix}-avatar-list-item"; 5 | 6 | .@{avatar-list-prefix-cls} { 7 | display: inline-block; 8 | 9 | ul { 10 | list-style: none; 11 | display: inline-block; 12 | padding: 0; 13 | margin: 0 0 0 8px; 14 | font-size: 0; 15 | } 16 | } 17 | 18 | .@{avatar-list-item-prefix-cls} { 19 | display: inline-block; 20 | font-size: @font-size-base; 21 | margin-left: -8px; 22 | width: @avatar-size-base; 23 | height: @avatar-size-base; 24 | 25 | :global { 26 | .ant-avatar { 27 | border: 1px solid #fff; 28 | cursor: pointer; 29 | } 30 | } 31 | 32 | &.large { 33 | width: @avatar-size-lg; 34 | height: @avatar-size-lg; 35 | } 36 | 37 | &.small { 38 | width: @avatar-size-sm; 39 | height: @avatar-size-sm; 40 | } 41 | 42 | &.mini { 43 | width: 20px; 44 | height: 20px; 45 | 46 | :global { 47 | .ant-avatar { 48 | width: 20px; 49 | height: 20px; 50 | line-height: 20px; 51 | 52 | .ant-avatar-string { 53 | font-size: 12px; 54 | line-height: 18px; 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | -------------------------------------------------------------------------------- /src/components/Charts/TransferBar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 65 | -------------------------------------------------------------------------------- /src/components/Charts/Bar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 63 | -------------------------------------------------------------------------------- /src/components/NumberInfo/index.less: -------------------------------------------------------------------------------- 1 | @import "../index"; 2 | 3 | @numberInfo-prefix-cls: ~"@{ant-pro-prefix}-number-info"; 4 | 5 | .@{numberInfo-prefix-cls} { 6 | 7 | .ant-pro-number-info-subtitle { 8 | color: @text-color-secondary; 9 | font-size: @font-size-base; 10 | height: 22px; 11 | line-height: 22px; 12 | overflow: hidden; 13 | text-overflow: ellipsis; 14 | word-break: break-all; 15 | white-space: nowrap; 16 | } 17 | 18 | .number-info-value { 19 | margin-top: 4px; 20 | font-size: 0; 21 | overflow: hidden; 22 | text-overflow: ellipsis; 23 | word-break: break-all; 24 | white-space: nowrap; 25 | 26 | & > span { 27 | color: @heading-color; 28 | display: inline-block; 29 | line-height: 32px; 30 | height: 32px; 31 | font-size: 24px; 32 | margin-right: 32px; 33 | } 34 | 35 | .sub-total { 36 | color: @text-color-secondary; 37 | font-size: @font-size-lg; 38 | vertical-align: top; 39 | margin-right: 0; 40 | i { 41 | font-size: 12px; 42 | transform: scale(0.82); 43 | margin-left: 4px; 44 | } 45 | :global { 46 | .anticon-caret-up { 47 | color: @red-6; 48 | } 49 | .anticon-caret-down { 50 | color: @green-6; 51 | } 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /src/components/GlobalHeader/RightContent.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 57 | -------------------------------------------------------------------------------- /src/views/order/shipping/components/ShippingItems.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | {{ is_delivery ? '是' : '否' }} 10 | 11 | 12 | 13 | 14 | {{ creation_date | Moment }} 15 | 16 | {{ creation_date | Moment('YYYY-MM-DD') }} 17 | 18 | 19 | 20 | 21 | 22 | 61 | 62 | 65 | -------------------------------------------------------------------------------- /src/core/bootstrap.js: -------------------------------------------------------------------------------- 1 | import store from '@/store' 2 | import storage from 'store' 3 | import { 4 | APP_LANGUAGE, 5 | TOGGLE_CONTENT_WIDTH, 6 | TOGGLE_FIXED_HEADER, 7 | TOGGLE_FIXED_SIDEBAR, TOGGLE_HIDE_HEADER, 8 | TOGGLE_LAYOUT, TOGGLE_NAV_THEME, TOGGLE_WEAK, 9 | TOGGLE_COLOR, TOGGLE_MULTI_TAB 10 | } from '@/store/mutation-types' 11 | import defaultSettings from '@/config/defaultSettings' 12 | 13 | export default function Initializer () { 14 | store.commit(TOGGLE_LAYOUT, storage.get(TOGGLE_LAYOUT, defaultSettings.layout)) 15 | store.commit(TOGGLE_FIXED_HEADER, storage.get(TOGGLE_FIXED_HEADER, defaultSettings.fixedHeader)) 16 | store.commit(TOGGLE_FIXED_SIDEBAR, storage.get(TOGGLE_FIXED_SIDEBAR, defaultSettings.fixSiderbar)) 17 | store.commit(TOGGLE_CONTENT_WIDTH, storage.get(TOGGLE_CONTENT_WIDTH, defaultSettings.contentWidth)) 18 | store.commit(TOGGLE_HIDE_HEADER, storage.get(TOGGLE_HIDE_HEADER, defaultSettings.autoHideHeader)) 19 | store.commit(TOGGLE_NAV_THEME, storage.get(TOGGLE_NAV_THEME, defaultSettings.navTheme)) 20 | store.commit(TOGGLE_WEAK, storage.get(TOGGLE_WEAK, defaultSettings.colorWeak)) 21 | store.commit(TOGGLE_COLOR, storage.get(TOGGLE_COLOR, defaultSettings.primaryColor)) 22 | store.commit(TOGGLE_MULTI_TAB, storage.get(TOGGLE_MULTI_TAB, defaultSettings.multiTab)) 23 | 24 | store.dispatch('setLang', storage.get(APP_LANGUAGE, 'zh-CN')) 25 | // last step 26 | } 27 | -------------------------------------------------------------------------------- /src/api/point_log.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 添加积分记录 6 | * @param pointLog 7 | * @returns {*} 8 | */ 9 | export function addPointLog(pointLog) { 10 | const url = '/point_log/add' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'POST', 15 | headers: Object.assign({}, HEADERS.POST), 16 | data: { 17 | point_log: pointLog 18 | } 19 | }).then((res) => { 20 | return Promise.resolve(res.data) 21 | }) 22 | } 23 | 24 | /** 25 | * 获取积分记录 26 | */ 27 | export function getPointLog(id) { 28 | const url = `/point_log/${id}` 29 | 30 | return axios({ 31 | url: url, 32 | method: 'GET' 33 | }).then((res) => { 34 | return Promise.resolve(res.data) 35 | }) 36 | } 37 | 38 | /** 39 | * 获取积分记录列表 40 | * @param query 41 | * @param sortby 42 | * @param order 43 | * @param pageNumber 44 | * @param pageSize 45 | * @returns {Promise<*>} 46 | */ 47 | export function getPointLogList({ query, sortby, order, pageNumber, pageSize }) { 48 | const url = '/point_log' 49 | 50 | return axios({ 51 | url: url, 52 | method: 'GET', 53 | params: { 54 | query: getObjectKeyValueToString(query), 55 | sortby: sortby, 56 | order: order, 57 | pageNumber: pageNumber, 58 | pageSize: pageSize 59 | } 60 | }).then((res) => { 61 | return Promise.resolve(res.data) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /src/api/deposit_log.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 添加预存款记录 6 | * @param pointLog 7 | * @returns {*} 8 | */ 9 | export function addDepositLog(pointLog) { 10 | const url = '/deposit_log/add' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'POST', 15 | headers: Object.assign({}, HEADERS.POST), 16 | data: { 17 | deposit_log: pointLog 18 | } 19 | }).then((res) => { 20 | return Promise.resolve(res.data) 21 | }) 22 | } 23 | 24 | /** 25 | * 获取预存款记录 26 | */ 27 | export function getDepositLog(id) { 28 | const url = `/deposit_log/${id}` 29 | 30 | return axios({ 31 | url: url, 32 | method: 'GET' 33 | }).then((res) => { 34 | return Promise.resolve(res.data) 35 | }) 36 | } 37 | 38 | /** 39 | * 获取预存款记录列表 40 | * @param query 41 | * @param sortby 42 | * @param order 43 | * @param pageNumber 44 | * @param pageSize 45 | * @returns {Promise<*>} 46 | */ 47 | export function getDepositLogList({ query, sortby, order, pageNumber, pageSize }) { 48 | const url = '/deposit_log' 49 | 50 | return axios({ 51 | url: url, 52 | method: 'GET', 53 | params: { 54 | query: getObjectKeyValueToString(query), 55 | sortby: sortby, 56 | order: order, 57 | pageNumber: pageNumber, 58 | pageSize: pageSize 59 | } 60 | }).then((res) => { 61 | return Promise.resolve(res.data) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /src/components/index.js: -------------------------------------------------------------------------------- 1 | // pro components 2 | import AvatarList from '@/components/AvatarList' 3 | import Ellipsis from '@/components/Ellipsis' 4 | import FooterToolbar from '@/components/FooterToolbar' 5 | import NumberInfo from '@/components/NumberInfo' 6 | import Tree from '@/components/Tree/Tree' 7 | import Trend from '@/components/Trend' 8 | import MultiTab from '@/components/MultiTab' 9 | import IconSelector from '@/components/IconSelector' 10 | import TagSelect from '@/components/TagSelect' 11 | import StandardFormRow from '@/components/StandardFormRow' 12 | import ArticleListContent from '@/components/ArticleListContent' 13 | 14 | // custom components 15 | import Box from '@/components/Box/Box' 16 | import Dialog from '@/components/Dialog' 17 | import InputDropdown from '@/components/InputDropdown' 18 | import ConfirmBack from '@/components/ConfirmBack/ConfirmBack' 19 | import Tinymce from '@/components/Tinymce' 20 | import Reload from '@/components/Reload/Reload' 21 | import UploadButton from '@/components/UploadButton/UploadButton' 22 | import SelectArea from '@/components/SelectArea/SelectArea' 23 | 24 | export { 25 | AvatarList, 26 | Trend, 27 | Ellipsis, 28 | FooterToolbar, 29 | NumberInfo, 30 | Tree, 31 | MultiTab, 32 | IconSelector, 33 | TagSelect, 34 | StandardFormRow, 35 | ArticleListContent, 36 | 37 | Dialog, 38 | 39 | Box, 40 | InputDropdown, 41 | ConfirmBack, 42 | Tinymce, 43 | Reload, 44 | UploadButton, 45 | SelectArea 46 | } 47 | -------------------------------------------------------------------------------- /src/store/modules/setting.js: -------------------------------------------------------------------------------- 1 | import { updateSetting, getSettingList } from '@/api/setting' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const setting = { 5 | state: { 6 | list: [] 7 | }, 8 | getters: { 9 | // 系统设置 10 | setting: state => { 11 | const settingList = state.list 12 | const obj = {} 13 | settingList.forEach((item) => { 14 | obj[item.name] = item.value 15 | }) 16 | return obj 17 | } 18 | }, 19 | mutations: { 20 | SET_SETTING: (state, list) => { 21 | state.list = list 22 | } 23 | }, 24 | actions: { 25 | /** 26 | * 获取系统设置 27 | * @param commit 28 | * @constructor 29 | */ 30 | GetSettingList({ commit }) { 31 | return new Promise(resolve => { 32 | getSettingList().then((res) => { 33 | if (res.err_code === ERR_OK) { 34 | const data = res.data 35 | commit('SET_SETTING', data) 36 | resolve() 37 | } 38 | }) 39 | }) 40 | }, 41 | /** 42 | * 更新系统设置 43 | * @param commit 44 | * @param setting 45 | * @constructor 46 | */ 47 | UpdateSetting({ commit }, setting) { 48 | const body = JSON.stringify(setting) 49 | return new Promise(resolve => { 50 | updateSetting(body).then((res) => { 51 | if (res.err_code === ERR_OK) { 52 | resolve() 53 | } 54 | }) 55 | }) 56 | } 57 | } 58 | } 59 | 60 | export default setting 61 | -------------------------------------------------------------------------------- /src/api/payment.js: -------------------------------------------------------------------------------- 1 | import axios from '@/api/config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 通过订单获取收款信息 6 | * @param orderId 7 | * @returns {Promise<*>} 8 | */ 9 | export function getPaymentByOrder(orderId) { 10 | const url = '/payment/order' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'GET', 15 | params: { 16 | order_id: orderId, 17 | sortby: 'CreationDate', 18 | order: 'desc' 19 | } 20 | }).then((res) => { 21 | return Promise.resolve(res.data) 22 | }) 23 | } 24 | 25 | /** 26 | * 获取支付信息 27 | * @param sn 28 | * @returns {*} 29 | */ 30 | export function getPayment(sn) { 31 | const url = `/payment/${sn}` 32 | 33 | return axios({ 34 | url: url, 35 | method: 'GET' 36 | }).then((res) => { 37 | return Promise.resolve(res.data) 38 | }) 39 | } 40 | 41 | /** 42 | * 获取支付列表 43 | * @param query 44 | * @param sortby 45 | * @param order 46 | * @param pageNumber 47 | * @param pageSize 48 | * @returns {Promise} 49 | */ 50 | export function getPaymentList({ query, sortby, order, pageNumber, pageSize }) { 51 | const url = '/payment' 52 | 53 | return axios({ 54 | url: url, 55 | method: 'GET', 56 | params: { 57 | query: getObjectKeyValueToString(query), 58 | sortby: sortby, 59 | order: order, 60 | pageNumber: pageNumber, 61 | pageSize: pageSize 62 | } 63 | }).then((res) => { 64 | return Promise.resolve(res.data) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /src/api/returns.js: -------------------------------------------------------------------------------- 1 | import axios from '@/api/config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 通过订单获取发货信息 6 | * @param orderId 7 | * @Returns {Promise<*>} 8 | */ 9 | export function getReturnsByOrder(orderId) { 10 | const url = '/returns/order' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'GET', 15 | params: { 16 | order_id: orderId, 17 | sortby: 'CreationDate', 18 | order: 'desc' 19 | } 20 | }).then((res) => { 21 | return Promise.resolve(res.data) 22 | }) 23 | } 24 | 25 | /** 26 | * 获取发货信息 27 | * @param sn 28 | * @Returns {*} 29 | */ 30 | export function getReturns(sn) { 31 | const url = `/returns/${sn}` 32 | 33 | return axios({ 34 | url: url, 35 | method: 'GET' 36 | }).then((res) => { 37 | return Promise.resolve(res.data) 38 | }) 39 | } 40 | 41 | /** 42 | * 获取发货列表 43 | * @param query 44 | * @param sortby 45 | * @param order 46 | * @param pageNumber 47 | * @param pageSize 48 | * @Returns {Promise} 49 | */ 50 | export function getReturnsList({ query, sortby, order, pageNumber, pageSize }) { 51 | const url = '/returns' 52 | 53 | return axios({ 54 | url: url, 55 | method: 'GET', 56 | params: { 57 | query: getObjectKeyValueToString(query), 58 | sortby: sortby, 59 | order: order, 60 | pageNumber: pageNumber, 61 | pageSize: pageSize 62 | } 63 | }).then((res) => { 64 | return Promise.resolve(res.data) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /src/api/message_config.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 更新消息配置 6 | * @param messageConfig 7 | * @returns {*} 8 | */ 9 | export function updateMessageConfig(messageConfig) { 10 | const url = '/message_config/update' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'PUT', 15 | headers: Object.assign({}, HEADERS.PUT), 16 | data: { 17 | message_config: messageConfig 18 | } 19 | }).then((res) => { 20 | return Promise.resolve(res.data) 21 | }) 22 | } 23 | 24 | /** 25 | * 获取消息配置 26 | */ 27 | export function getMessageConfig(id) { 28 | const url = `/message_config/${id}` 29 | 30 | return axios({ 31 | url: url, 32 | method: 'GET' 33 | }).then((res) => { 34 | return Promise.resolve(res.data) 35 | }) 36 | } 37 | 38 | /** 39 | * 获取消息配置列表 40 | * @param query 41 | * @param sortby 42 | * @param order 43 | * @param pageNumber 44 | * @param pageSize 45 | * @returns {Promise<*>} 46 | */ 47 | export function getMessageConfigList({ query, sortby, order, pageNumber, pageSize }) { 48 | const url = '/message_config' 49 | 50 | return axios({ 51 | url: url, 52 | method: 'GET', 53 | params: { 54 | query: getObjectKeyValueToString(query), 55 | sortby: sortby, 56 | order: order, 57 | pageNumber: pageNumber, 58 | pageSize: pageSize 59 | } 60 | }).then((res) => { 61 | return Promise.resolve(res.data) 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /src/api/shipping.js: -------------------------------------------------------------------------------- 1 | import axios from '@/api/config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 通过订单获取发货信息 6 | * @param orderId 7 | * @returns {Promise<*>} 8 | */ 9 | export function getShippingByOrder(orderId) { 10 | const url = '/shipping/order' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'GET', 15 | params: { 16 | order_id: orderId, 17 | sortby: 'CreationDate', 18 | order: 'desc' 19 | } 20 | }).then((res) => { 21 | return Promise.resolve(res.data) 22 | }) 23 | } 24 | 25 | /** 26 | * 获取发货信息 27 | * @param sn 28 | * @returns {*} 29 | */ 30 | export function getShipping(sn) { 31 | const url = `/shipping/${sn}` 32 | 33 | return axios({ 34 | url: url, 35 | method: 'GET' 36 | }).then((res) => { 37 | return Promise.resolve(res.data) 38 | }) 39 | } 40 | 41 | /** 42 | * 获取发货列表 43 | * @param query 44 | * @param sortby 45 | * @param order 46 | * @param pageNumber 47 | * @param pageSize 48 | * @returns {Promise} 49 | */ 50 | export function getShippingList({ query, sortby, order, pageNumber, pageSize }) { 51 | const url = '/shipping' 52 | 53 | return axios({ 54 | url: url, 55 | method: 'GET', 56 | params: { 57 | query: getObjectKeyValueToString(query), 58 | sortby: sortby, 59 | order: order, 60 | pageNumber: pageNumber, 61 | pageSize: pageSize 62 | } 63 | }).then((res) => { 64 | return Promise.resolve(res.data) 65 | }) 66 | } 67 | -------------------------------------------------------------------------------- /src/components/Charts/Liquid.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 29 | 45 | 46 | 47 | 48 | 49 | 64 | 65 | 68 | -------------------------------------------------------------------------------- /src/components/SelectLang/index.jsx: -------------------------------------------------------------------------------- 1 | import './index.less' 2 | 3 | import { Icon, Menu, Dropdown } from 'ant-design-vue' 4 | import { i18nRender } from '@/locales' 5 | import i18nMixin from '@/store/i18n-mixin' 6 | 7 | const locales = ['zh-CN'] 8 | const languageLabels = { 9 | 'zh-CN': '简体中文', 10 | 'en-US': 'English' 11 | } 12 | // eslint-disable-next-line 13 | const languageIcons = { 14 | 'zh-CN': '🇨🇳', 15 | 'en-US': '🇺🇸' 16 | } 17 | 18 | const SelectLang = { 19 | props: { 20 | prefixCls: { 21 | type: String, 22 | default: 'ant-pro-drop-down' 23 | } 24 | }, 25 | name: 'SelectLang', 26 | mixins: [i18nMixin], 27 | render() { 28 | const { prefixCls } = this 29 | const changeLang = ({ key }) => { 30 | this.setLang(key) 31 | } 32 | const langMenu = ( 33 | 34 | {locales.map(locale => ( 35 | 36 | 37 | {languageIcons[locale]} 38 | {' '} 39 | {languageLabels[locale]} 40 | 41 | ))} 42 | 43 | ) 44 | return ( 45 | 46 | 47 | 48 | 49 | 50 | ) 51 | } 52 | } 53 | 54 | export default SelectLang 55 | -------------------------------------------------------------------------------- /src/locales/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueI18n from 'vue-i18n' 3 | import storage from 'store' 4 | import moment from 'moment' 5 | 6 | // default lang 7 | import zhCh from './lang/zh-CN' 8 | 9 | Vue.use(VueI18n) 10 | 11 | export const defaultLang = 'zh-CN' 12 | 13 | const messages = { 14 | 'zh-CN': { 15 | ...zhCh 16 | } 17 | } 18 | 19 | const i18n = new VueI18n({ 20 | silentTranslationWarn: true, 21 | locale: defaultLang, 22 | fallbackLocale: defaultLang, 23 | messages 24 | }) 25 | 26 | const loadedLanguages = [defaultLang] 27 | 28 | function setI18nLanguage (lang) { 29 | i18n.locale = lang 30 | // request.headers['Accept-Language'] = lang 31 | document.querySelector('html').setAttribute('lang', lang) 32 | return lang 33 | } 34 | 35 | export function loadLanguageAsync (lang = defaultLang) { 36 | return new Promise(resolve => { 37 | // 缓存语言设置 38 | storage.set('lang', lang) 39 | if (i18n.locale !== lang) { 40 | if (!loadedLanguages.includes(lang)) { 41 | return import(/* webpackChunkName: "lang-[request]" */ `./lang/${lang}`).then(msg => { 42 | const locale = msg.default 43 | i18n.setLocaleMessage(lang, locale) 44 | loadedLanguages.push(lang) 45 | moment.updateLocale(locale.momentName, locale.momentLocale) 46 | return setI18nLanguage(lang) 47 | }) 48 | } 49 | return resolve(setI18nLanguage(lang)) 50 | } 51 | return resolve(lang) 52 | }) 53 | } 54 | 55 | export function i18nRender (key) { 56 | return i18n.t(`${key}`) 57 | } 58 | 59 | export default i18n 60 | -------------------------------------------------------------------------------- /src/store/modules/area.js: -------------------------------------------------------------------------------- 1 | import { getAreaList } from '@/api/area' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const area = { 5 | state: { 6 | areas: [], 7 | currentArea: null, 8 | parentArea: null 9 | }, 10 | mutations: { 11 | SET_AREAS: (state, areas) => { 12 | state.areas = areas 13 | }, 14 | SET_CURRENT_AREA: (state, currentArea) => { 15 | state.currentArea = currentArea 16 | }, 17 | SET_PARENT_AREA: (state, parentArea) => { 18 | state.parentArea = parentArea 19 | } 20 | }, 21 | actions: { 22 | /** 23 | * 获取区域 24 | * @param commit 25 | * @param query 26 | * @returns {Promise} 27 | * @constructor 28 | */ 29 | GetAllArea({ commit }, { query }) { 30 | return new Promise(resolve => { 31 | getAreaList({ query }).then((res) => { 32 | if (res.err_code === ERR_OK) { 33 | commit('SET_AREAS', res.data) 34 | resolve(res.data) 35 | } 36 | }) 37 | }) 38 | }, 39 | /** 40 | * 设置当前区域 41 | * @param commit 42 | * @param currentArea 43 | * @constructor 44 | */ 45 | SetCurrentArea({ commit }, currentArea) { 46 | commit('SET_CURRENT_AREA', currentArea) 47 | }, 48 | /** 49 | * 获取上级区域 50 | * @param commit 51 | * @param parentArea 52 | * @constructor 53 | */ 54 | SetParentArea({ commit }, parentArea) { 55 | return new Promise(resolve => { 56 | commit('SET_PARENT_AREA', parentArea) 57 | }) 58 | } 59 | } 60 | } 61 | 62 | export default area 63 | -------------------------------------------------------------------------------- /src/store/modules/tag.js: -------------------------------------------------------------------------------- 1 | import { ERR_OK } from '@/api/config' 2 | import { getTagList, getTagCount } from '@/api/tag' 3 | 4 | const tag = { 5 | state: { 6 | pageList: [] 7 | }, 8 | mutations: { 9 | SET_TAG_LIST: (state, pageList) => { 10 | state.pageList = pageList 11 | } 12 | }, 13 | actions: { 14 | /** 15 | * 获取标签列表 16 | * @param commit 17 | * @param query 18 | * @param sortby 19 | * @param order 20 | * @param pageNumber 21 | * @param pageSize 22 | * @returns {Promise} 23 | * @constructor 24 | */ 25 | GetTagList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 26 | return new Promise(resolve => { 27 | getTagList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 28 | if (res.err_code === ERR_OK) { 29 | const tagList = res.data 30 | commit('SET_TAG_LIST', tagList) 31 | resolve() 32 | } 33 | }) 34 | }) 35 | }, 36 | /** 37 | * 获取所有的标签列表 38 | * @param dispatch 39 | * @param commit 40 | * @param query 41 | * @constructor 42 | */ 43 | GetAllTagPageList({ dispatch, commit }, query) { 44 | getTagCount(query).then((res) => { 45 | if (res.err_code) { 46 | const pageNumber = 1 47 | const pageSize = res.data 48 | const sortby = 'CreationDate' 49 | const order = 'desc' // 倒序 50 | dispatch('GetTagList', { query, sortby, order, pageNumber, pageSize }) 51 | } 52 | }) 53 | } 54 | } 55 | } 56 | 57 | export default tag 58 | -------------------------------------------------------------------------------- /src/components/Charts/Radar.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 65 | 66 | 69 | -------------------------------------------------------------------------------- /src/store/modules/payment_method.js: -------------------------------------------------------------------------------- 1 | import { getPaymentMethodCount, getPaymentMethodList } from '@/api/payment_method' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const payment = { 5 | state: { 6 | pageList: {} 7 | }, 8 | mutations: { 9 | SET_PAYMENT_METHODS: (state, list) => { 10 | state.pageList = list 11 | } 12 | }, 13 | actions: { 14 | /** 15 | * 获取订单支付方法 16 | * @param commit 17 | * @param query 18 | * @param sortby 19 | * @param order 20 | * @param pageNumber 21 | * @param pageSize 22 | * @returns {Promise} 23 | * @constructor 24 | */ 25 | GetPaymentMethods({ commit }, { query, sortby, order, pageNumber, pageSize }) { 26 | return new Promise(resolve => { 27 | getPaymentMethodList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 28 | if (res.err_code === ERR_OK) { 29 | commit('SET_PAYMENT_METHODS', res.data) 30 | resolve(res.data) 31 | } 32 | }) 33 | }) 34 | }, 35 | GetAllPaymentMethod({ commit }) { 36 | return new Promise(resolve => { 37 | getPaymentMethodCount().then((res) => { 38 | if (res.err_code === ERR_OK) { 39 | const pageNumber = 1 40 | const pageSize = res.data 41 | getPaymentMethodList({ pageNumber, pageSize }).then((res) => { 42 | if (res.err_code === ERR_OK) { 43 | commit('SET_PAYMENT_METHODS', res.data) 44 | resolve() 45 | } 46 | }) 47 | } 48 | }) 49 | }) 50 | } 51 | } 52 | } 53 | 54 | export default payment 55 | -------------------------------------------------------------------------------- /src/components/Box/Box.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 26 | 27 | 72 | -------------------------------------------------------------------------------- /src/store/modules/order.js: -------------------------------------------------------------------------------- 1 | import { getOrder } from '@/api/order' 2 | import { ERR_OK } from '@/api/config' 3 | import * as types from '../mutation-types' 4 | 5 | const order = { 6 | state: { 7 | types: { 8 | general: '普通订单' 9 | }, 10 | status: { 11 | // 等待付款 0 12 | pendingPayment: 0, 13 | // 等待审核 1 14 | pendingReview: 1, 15 | // 等待发货 2 16 | pendingShipment: 2, 17 | // 已发货 3 18 | shipped: 3, 19 | // 已收货 4 20 | received: 4, 21 | // 已完成 5 22 | completed: 5, 23 | // 已失败 6 24 | failed: 6, 25 | // 已取消 7 26 | canceled: 7, 27 | // 已拒绝 8 28 | denied: 8, 29 | // 已退货 30 | returns: 9, 31 | // 未完成 10 32 | unfinished: 10, 33 | // 退款中 11 34 | refunding: 11, 35 | // 退款完成 11 36 | refunded: 12 37 | }, 38 | currentOrder: null 39 | }, 40 | getters: { 41 | // 当前订单 42 | order: state => state.currentOrder, 43 | // 订单类型 44 | orderTypes: state => state.types, 45 | // 订单状态 46 | orderStatus: state => state.status 47 | }, 48 | mutations: { 49 | [types.SET_ORDER]: (state, order) => { 50 | state.currentOrder = order 51 | } 52 | }, 53 | actions: { 54 | /** 55 | * 获取订单 56 | * @param commit 57 | * @param sn 58 | * @returns {Promise} 59 | * @constructor 60 | */ 61 | GetOrder({ commit }, sn) { 62 | return new Promise(resolve => { 63 | getOrder(sn).then((res) => { 64 | if (res.err_code === ERR_OK) { 65 | commit(types.SET_ORDER, res.data) 66 | resolve(res.data) 67 | } 68 | }) 69 | }) 70 | } 71 | } 72 | } 73 | 74 | export default order 75 | -------------------------------------------------------------------------------- /src/locales/lang/en-US.js: -------------------------------------------------------------------------------- 1 | import antdEnUS from 'ant-design-vue/es/locale-provider/en_US' 2 | import momentEU from 'moment/locale/eu' 3 | 4 | const components = { 5 | antLocale: antdEnUS, 6 | momentName: 'eu', 7 | momentLocale: momentEU 8 | } 9 | 10 | const locale = { 11 | 'message': '-', 12 | 'menu.home': 'Home', 13 | 'menu.dashboard': 'Dashboard', 14 | 'menu.dashboard.analysis': 'Analysis', 15 | 'menu.dashboard.monitor': 'Monitor', 16 | 'menu.dashboard.workplace': 'Workplace', 17 | 18 | 'layouts.usermenu.dialog.title': 'Message', 19 | 'layouts.usermenu.dialog.content': 'Do you really log-out.', 20 | 21 | 'app.setting.pagestyle': 'Page style setting', 22 | 'app.setting.pagestyle.light': 'Light style', 23 | 'app.setting.pagestyle.dark': 'Dark style', 24 | 'app.setting.pagestyle.realdark': 'RealDark style', 25 | 'app.setting.themecolor': 'Theme Color', 26 | 'app.setting.navigationmode': 'Navigation Mode', 27 | 'app.setting.content-width': 'Content Width', 28 | 'app.setting.fixedheader': 'Fixed Header', 29 | 'app.setting.fixedsidebar': 'Fixed Sidebar', 30 | 'app.setting.sidemenu': 'Side Menu Layout', 31 | 'app.setting.topmenu': 'Top Menu Layout', 32 | 'app.setting.content-width.fixed': 'Fixed', 33 | 'app.setting.content-width.fluid': 'Fluid', 34 | 'app.setting.othersettings': 'Other Settings', 35 | 'app.setting.weakmode': 'Weak Mode', 36 | 'app.setting.copy': 'Copy Setting', 37 | 'app.setting.loading': 'Loading theme', 38 | 'app.setting.copyinfo': 'copy success,please replace defaultSettings in src/models/setting.js', 39 | 'app.setting.production.hint': 'Setting panel shows in development environment only, please manually modify' 40 | } 41 | 42 | export default { 43 | ...components, 44 | ...locale 45 | } 46 | -------------------------------------------------------------------------------- /src/components/Ellipsis/Ellipsis.vue: -------------------------------------------------------------------------------- 1 | 65 | -------------------------------------------------------------------------------- /src/api/stock.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 入库 6 | * @param inQuantity 7 | * @param sn 8 | * @param memo 9 | * @returns {*} 10 | */ 11 | export function inStock({ inQuantity, sn, memo }) { 12 | const url = '/stock/in' 13 | 14 | return axios({ 15 | url: url, 16 | method: 'POST', 17 | headers: Object.assign({}, HEADERS.POST), 18 | data: { 19 | in_quantity: inQuantity, 20 | sn: sn, 21 | memo: memo 22 | } 23 | }).then((res) => { 24 | return Promise.resolve(res.data) 25 | }) 26 | } 27 | 28 | /** 29 | * 出库 30 | * @param inQuantity 31 | * @param sn 32 | * @param memo 33 | * @returns {*} 34 | */ 35 | export function outStock({ outQuantity, sn, memo }) { 36 | const url = '/stock/out' 37 | 38 | return axios({ 39 | url: url, 40 | method: 'POST', 41 | headers: Object.assign({}, HEADERS.POST), 42 | data: { 43 | out_quantity: outQuantity, 44 | sn: sn, 45 | memo: memo 46 | } 47 | }).then((res) => { 48 | return Promise.resolve(res.data) 49 | }) 50 | } 51 | 52 | /** 53 | * 获取库存记录列表 54 | * @param query 55 | * @param sortby 56 | * @param order 57 | * @param pageNumber 58 | * @param pageSize 59 | * @returns {Promise} 60 | */ 61 | export function getStockLogList({ query, sortby, order, pageNumber, pageSize }) { 62 | const url = '/stock/log' 63 | 64 | return axios({ 65 | url: url, 66 | method: 'GET', 67 | params: { 68 | query: getObjectKeyValueToString(query), 69 | sortby: sortby, 70 | order: order, 71 | pageNumber: pageNumber, 72 | pageSize: pageSize 73 | } 74 | }).then((res) => { 75 | return Promise.resolve(res.data) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /src/views/order/list/Edit.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 65 | 66 | 69 | -------------------------------------------------------------------------------- /src/components/Charts/MiniProgress.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 36 | 37 | 76 | -------------------------------------------------------------------------------- /src/components/Charts/RankList.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ title }} 4 | 5 | 6 | {{ index + 1 }} 7 | {{ item.name }} 8 | {{ item.total }} 9 | 10 | 11 | 12 | 13 | 14 | 30 | 31 | 78 | -------------------------------------------------------------------------------- /src/store/modules/brand.js: -------------------------------------------------------------------------------- 1 | import { getBrandList, getBrandCount } from '@/api/brand' 2 | import { ERR_OK } from '@/api/config' 3 | import { IMAGE_PREFIX } from '@/config/axios.config.js' 4 | 5 | const brand = { 6 | state: { 7 | pageList: {} 8 | }, 9 | mutations: { 10 | SET_BRAND_PAGES: (state, pageList) => { 11 | state.pageList = pageList 12 | } 13 | }, 14 | actions: { 15 | /** 16 | * 获取品牌列表 17 | * @param commit 18 | * @param query 19 | * @param sortby 20 | * @param order 21 | * @param pageNumber 22 | * @param pageSize 23 | * @returns {Promise} 24 | * @constructor 25 | */ 26 | GetBrandPageList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 27 | getBrandList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 28 | if (res.err_code === ERR_OK) { 29 | const pageList = res.data 30 | if (pageList && pageList.list) { 31 | pageList.list.forEach((brand) => { 32 | brand.logo = IMAGE_PREFIX + brand.logo 33 | }) 34 | commit('SET_BRAND_PAGES', pageList) 35 | } 36 | } 37 | }) 38 | }, 39 | /** 40 | * 获取所有的标签列表 41 | * @param dispatch 42 | * @param commit 43 | * @param query 44 | * @constructor 45 | */ 46 | GetAllBrandPageList({ dispatch, commit }, query) { 47 | getBrandCount(query).then((res) => { 48 | if (res.err_code === ERR_OK) { 49 | const pageNumber = 1 50 | const pageSize = res.data 51 | const sortby = 'CreationDate' 52 | const order = 'desc' // 倒序 53 | dispatch('GetBrandPageList', { query, sortby, order, pageNumber, pageSize }) 54 | } 55 | }) 56 | } 57 | } 58 | } 59 | 60 | export default brand 61 | -------------------------------------------------------------------------------- /src/store/modules/member_rank.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 会员等级 3 | */ 4 | import { getMemberRankCount, getMemberRankList } from '@/api/member_rank' 5 | import { ERR_OK } from '@/api/config' 6 | 7 | const memberRank = { 8 | state: { 9 | pageList: {} 10 | }, 11 | mutations: { 12 | SET_MEMBER_RANK_PAGE_LIST: (state, pageList) => { 13 | state.pageList = pageList 14 | } 15 | }, 16 | actions: { 17 | /** 18 | * 获取所有会员等级列表 19 | * @param commit 20 | * @returns {Promise} 21 | */ 22 | GetAllMemberRank({ commit }) { 23 | return new Promise(resolve => { 24 | getMemberRankCount().then((res) => { 25 | if (res.err_code === ERR_OK) { 26 | const pageNumber = 1 27 | const pageSize = res.data 28 | getMemberRankList({ pageNumber, pageSize }).then((res) => { 29 | if (res.err_code === ERR_OK) { 30 | commit('SET_MEMBER_RANK_PAGE_LIST', res.data) 31 | resolve(res.data) 32 | } 33 | }) 34 | } 35 | }) 36 | }) 37 | }, 38 | /** 39 | * 获取会员等级列表 40 | * @param commit 41 | * @param query 42 | * @param sortby 43 | * @param order 44 | * @param pageNumber 45 | * @param pageSize 46 | * @returns {Promise} 47 | * @constructor 48 | */ 49 | GetMemberRankList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 50 | return new Promise(resolve => { 51 | getMemberRankList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 52 | if (res.err_code === ERR_OK) { 53 | commit('SET_MEMBER_RANK_PAGE_LIST', res.data) 54 | resolve(res.data) 55 | } 56 | }) 57 | }) 58 | } 59 | } 60 | } 61 | 62 | export default memberRank 63 | -------------------------------------------------------------------------------- /src/views/account/settings/Security.vue: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | {{ item.title }} 9 | 10 | {{ item.description }} 11 | : 12 | {{ item.value }} 13 | 14 | 15 | 16 | {{ item.actions.title }} 17 | 18 | 19 | 20 | 21 | 22 | 23 | 38 | 39 | 42 | -------------------------------------------------------------------------------- /src/store/modules/delivery_corp.js: -------------------------------------------------------------------------------- 1 | import { getDeliveryCorpCount, getDeliveryCorpList } from '@/api/delivery_corp' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const deliveryCorp = { 5 | state: { 6 | pageList: {} 7 | }, 8 | mutations: { 9 | SET_ORDER_DELIVERY_CORPS: (state, deliveryCorps) => { 10 | state.pageList = deliveryCorps 11 | } 12 | }, 13 | actions: { 14 | /** 15 | * 获取 物流公司 16 | * @param commit 17 | * @param query 18 | * @param sortby 19 | * @param order 20 | * @param pageNumber 21 | * @param pageSize 22 | * @returns {Promise} 23 | * @constructor 24 | */ 25 | GetDeliveryCorps({ commit }, { query, sortby, order, pageNumber, pageSize }) { 26 | return new Promise(resolve => { 27 | getDeliveryCorpList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 28 | if (res.err_code === ERR_OK) { 29 | commit('SET_ORDER_DELIVERY_CORPS', res.data) 30 | resolve(res.data) 31 | } 32 | }) 33 | }) 34 | }, 35 | /** 36 | * 获取所有物流公司 37 | * @param commit 38 | * @returns {Promise} 39 | * @constructor 40 | */ 41 | GetAllDeliveryCorp({ commit }) { 42 | return new Promise(resolve => { 43 | getDeliveryCorpCount().then((res) => { 44 | if (res.err_code === ERR_OK) { 45 | const pageNumber = 1 46 | const pageSize = res.data 47 | getDeliveryCorpList({ pageNumber, pageSize }).then((res) => { 48 | if (res.err_code === ERR_OK) { 49 | commit('SET_ORDER_DELIVERY_CORPS', res.data) 50 | resolve() 51 | } 52 | }) 53 | } 54 | }) 55 | }) 56 | } 57 | } 58 | } 59 | 60 | export default deliveryCorp 61 | -------------------------------------------------------------------------------- /src/assets/icons/bx-analyse.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/order/returns/Look.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 返回 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 68 | 69 | 72 | -------------------------------------------------------------------------------- /src/components/Search/GlobalSearch.jsx: -------------------------------------------------------------------------------- 1 | import { Select } from 'ant-design-vue' 2 | import './index.less' 3 | 4 | const GlobalSearch = { 5 | name: 'GlobalSearch', 6 | data () { 7 | return { 8 | visible: false 9 | } 10 | }, 11 | mounted () { 12 | const keyboardHandle = (e) => { 13 | e.preventDefault() 14 | e.stopPropagation() 15 | const { ctrlKey, shiftKey, altKey, keyCode } = e 16 | console.log('keyCode:', e.keyCode, e) 17 | // key is `K` and hold ctrl 18 | if (keyCode === 75 && ctrlKey && !shiftKey && !altKey) { 19 | this.visible = !this.visible 20 | } 21 | } 22 | document.addEventListener('keydown', keyboardHandle) 23 | }, 24 | render () { 25 | const { visible } = this 26 | const handleSearch = (e) => { 27 | this.$emit('search', e) 28 | } 29 | 30 | const handleChange = (e) => { 31 | this.$emit('change', e) 32 | } 33 | if (!visible) { 34 | return null 35 | } 36 | return ( 37 | 38 | 39 | 51 | 52 | Open with Ctrl/⌘ + K 53 | 54 | 55 | ) 56 | } 57 | } 58 | 59 | GlobalSearch.install = function (Vue) { 60 | Vue.component(GlobalSearch.name, GlobalSearch) 61 | } 62 | 63 | export default GlobalSearch 64 | -------------------------------------------------------------------------------- /src/views/order/shipping/Look.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 返回 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 68 | 69 | 72 | -------------------------------------------------------------------------------- /src/components/Charts/Trend.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ term }} 4 | {{ rate }}% 5 | 6 | 7 | 8 | 9 | 52 | 53 | 83 | -------------------------------------------------------------------------------- /src/components/Tinymce/dynamicLoadScript.js: -------------------------------------------------------------------------------- 1 | let callbacks = [] 2 | 3 | function loadedTinymce() { 4 | // to fixed https://github.com/PanJiaChen/vue-element-admin/issues/2144 5 | // check is successfully downloaded script 6 | return window.tinymce 7 | } 8 | 9 | const dynamicLoadScript = (src, callback) => { 10 | const existingScript = document.getElementById(src) 11 | const cb = callback || function() {} 12 | 13 | if (!existingScript) { 14 | const script = document.createElement('script') 15 | script.src = src // src url for the third-party library being loaded. 16 | script.id = src 17 | document.body.appendChild(script) 18 | callbacks.push(cb) 19 | const onEnd = 'onload' in script ? stdOnEnd : ieOnEnd 20 | onEnd(script) 21 | } 22 | 23 | if (existingScript && cb) { 24 | if (loadedTinymce()) { 25 | cb(null, existingScript) 26 | } else { 27 | callbacks.push(cb) 28 | } 29 | } 30 | 31 | function stdOnEnd(script) { 32 | script.onload = function() { 33 | // this.onload = null here is necessary 34 | // because even IE9 works not like others 35 | this.onerror = this.onload = null 36 | for (const cb of callbacks) { 37 | cb(null, script) 38 | } 39 | callbacks = null 40 | } 41 | script.onerror = function() { 42 | this.onerror = this.onload = null 43 | cb(new Error('Failed to load ' + src), script) 44 | } 45 | } 46 | 47 | function ieOnEnd(script) { 48 | script.onreadystatechange = function() { 49 | if (this.readyState !== 'complete' && this.readyState !== 'loaded') return 50 | this.onreadystatechange = null 51 | for (const cb of callbacks) { 52 | cb(null, script) // there is no way to catch loading errors in IE8 53 | } 54 | callbacks = null 55 | } 56 | } 57 | } 58 | 59 | export default dynamicLoadScript 60 | -------------------------------------------------------------------------------- /src/components/NProgress/nprogress.less: -------------------------------------------------------------------------------- 1 | @import url('../index.less'); 2 | 3 | /* Make clicks pass-through */ 4 | #nprogress { 5 | pointer-events: none; 6 | } 7 | 8 | #nprogress .bar { 9 | background: @primary-color; 10 | 11 | position: fixed; 12 | z-index: 1031; 13 | top: 0; 14 | left: 0; 15 | 16 | width: 100%; 17 | height: 2px; 18 | } 19 | 20 | /* Fancy blur effect */ 21 | #nprogress .peg { 22 | display: block; 23 | position: absolute; 24 | right: 0px; 25 | width: 100px; 26 | height: 100%; 27 | box-shadow: 0 0 10px @primary-color, 0 0 5px @primary-color; 28 | opacity: 1.0; 29 | 30 | -webkit-transform: rotate(3deg) translate(0px, -4px); 31 | -ms-transform: rotate(3deg) translate(0px, -4px); 32 | transform: rotate(3deg) translate(0px, -4px); 33 | } 34 | 35 | /* Remove these to get rid of the spinner */ 36 | #nprogress .spinner { 37 | display: block; 38 | position: fixed; 39 | z-index: 1031; 40 | top: 15px; 41 | right: 15px; 42 | } 43 | 44 | #nprogress .spinner-icon { 45 | width: 18px; 46 | height: 18px; 47 | box-sizing: border-box; 48 | 49 | border: solid 2px transparent; 50 | border-top-color: @primary-color; 51 | border-left-color: @primary-color; 52 | border-radius: 50%; 53 | 54 | -webkit-animation: nprogress-spinner 400ms linear infinite; 55 | animation: nprogress-spinner 400ms linear infinite; 56 | } 57 | 58 | .nprogress-custom-parent { 59 | overflow: hidden; 60 | position: relative; 61 | } 62 | 63 | .nprogress-custom-parent #nprogress .spinner, 64 | .nprogress-custom-parent #nprogress .bar { 65 | position: absolute; 66 | } 67 | 68 | @-webkit-keyframes nprogress-spinner { 69 | 0% { -webkit-transform: rotate(0deg); } 70 | 100% { -webkit-transform: rotate(360deg); } 71 | } 72 | @keyframes nprogress-spinner { 73 | 0% { transform: rotate(0deg); } 74 | 100% { transform: rotate(360deg); } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/components/TextArea/index.jsx: -------------------------------------------------------------------------------- 1 | import './style.less' 2 | import { getStrFullLength, cutStrByFullLength } from '../_util/util' 3 | import Input from 'ant-design-vue/es/input' 4 | const TextArea = Input.TextArea 5 | 6 | export default { 7 | name: 'LimitTextArea', 8 | model: { 9 | prop: 'value', 10 | event: 'change' 11 | }, 12 | props: Object.assign({}, TextArea.props, { 13 | prefixCls: { 14 | type: String, 15 | default: 'ant-textarea-limit' 16 | }, 17 | // eslint-disable-next-line 18 | value: { 19 | type: String 20 | }, 21 | limit: { 22 | type: Number, 23 | default: 200 24 | } 25 | }), 26 | data () { 27 | return { 28 | currentLimit: 0 29 | } 30 | }, 31 | watch: { 32 | value (val) { 33 | this.calcLimitNum(val) 34 | } 35 | }, 36 | created () { 37 | this.calcLimitNum(this.value) 38 | }, 39 | methods: { 40 | handleChange (e) { 41 | const value = e.target.value 42 | const len = getStrFullLength(value) 43 | if (len <= this.limit) { 44 | this.currentLimit = len 45 | this.$emit('change', value) 46 | return 47 | } else { 48 | const str = cutStrByFullLength(value, this.limit) 49 | this.currentLimit = getStrFullLength(str) 50 | this.$emit('change', str) 51 | } 52 | console.error('limit out! currentLimit:', this.currentLimit) 53 | }, 54 | calcLimitNum (val) { 55 | const len = getStrFullLength(val) 56 | this.currentLimit = len 57 | } 58 | }, 59 | render () { 60 | const { prefixCls, ...props } = this.$props 61 | return ( 62 | 63 | 64 | 65 | {this.currentLimit}/{this.limit} 66 | 67 | ) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/api/area.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 添加地区 6 | * @param area 7 | * @returns {*} 8 | */ 9 | export function addArea(area) { 10 | const url = '/area/add' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'POST', 15 | headers: Object.assign({}, HEADERS.POST), 16 | data: { 17 | area: area 18 | } 19 | }).then((res) => { 20 | return Promise.resolve(res.data) 21 | }) 22 | } 23 | 24 | /** 25 | * 删除区域 26 | * @param ids 27 | * @returns {*} 28 | */ 29 | export function deleteArea(ids) { 30 | const url = '/area/delete' 31 | 32 | return axios({ 33 | url: url, 34 | method: 'DELETE', 35 | params: { 36 | ids: ids 37 | } 38 | }).then((res) => { 39 | return Promise.resolve(res.data) 40 | }) 41 | } 42 | 43 | /** 44 | * 更新区域信息 45 | * @param area 46 | * @returns {*} 47 | */ 48 | export function updateArea(area) { 49 | const url = '/area/update' 50 | 51 | return axios({ 52 | url: url, 53 | method: 'PUT', 54 | headers: Object.assign({}, HEADERS.PUT), 55 | data: { 56 | area: area 57 | } 58 | }).then((res) => { 59 | return Promise.resolve(res.data) 60 | }) 61 | } 62 | 63 | /** 64 | * 获取一个区域信息 65 | * @param id 66 | * @returns {*} 67 | */ 68 | export function getArea(id) { 69 | const url = `/area/${id}` 70 | 71 | return axios({ 72 | url: url, 73 | method: 'GET' 74 | }).then((res) => { 75 | return Promise.resolve(res.data) 76 | }) 77 | } 78 | 79 | /** 80 | * 获取区域列表 81 | * @returns {*} 82 | */ 83 | export function getAreaList({ query }) { 84 | const url = '/area' 85 | 86 | return axios({ 87 | url: url, 88 | method: 'GET', 89 | params: { 90 | query: getObjectKeyValueToString(query) 91 | } 92 | }).then((res) => { 93 | return Promise.resolve(res.data) 94 | }) 95 | } 96 | -------------------------------------------------------------------------------- /src/views/account/settings/Custom.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 风格配色 7 | 8 | 9 | 10 | 整体风格配色设置 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 主题色 22 | 23 | 24 | 25 | 页面风格配色: {{ colorFilter(primaryColor) }} 26 | 27 | 28 | 29 | 30 | 31 | 32 | 69 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/strongly-recommended', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'generator-star-spacing': 'off', 14 | 'no-mixed-operators': 0, 15 | 'vue/max-attributes-per-line': [ 16 | 2, 17 | { 18 | 'singleline': 5, 19 | 'multiline': { 20 | 'max': 1, 21 | 'allowFirstLine': false 22 | } 23 | } 24 | ], 25 | 'vue/attribute-hyphenation': 0, 26 | 'vue/html-self-closing': 0, 27 | 'vue/component-name-in-template-casing': 0, 28 | 'vue/html-closing-bracket-spacing': 0, 29 | 'vue/singleline-html-element-content-newline': 0, 30 | 'vue/no-unused-components': 0, 31 | 'vue/multiline-html-element-content-newline': 0, 32 | 'vue/no-use-v-if-with-v-for': 0, 33 | 'vue/html-closing-bracket-newline': 0, 34 | 'vue/no-parsing-error': 0, 35 | 'no-tabs': 0, 36 | 'quotes': [ 37 | 2, 38 | 'single', 39 | { 40 | 'avoidEscape': true, 41 | 'allowTemplateLiterals': true 42 | } 43 | ], 44 | 'semi': [ 45 | 2, 46 | 'never', 47 | { 48 | 'beforeStatementContinuationChars': 'never' 49 | } 50 | ], 51 | 'no-delete-var': 2, 52 | 'prefer-const': [ 53 | 2, 54 | { 55 | 'ignoreReadBeforeAssign': false 56 | } 57 | ], 58 | 'template-curly-spacing': 'off', 59 | 'indent': 'off', 60 | 'space-before-function-paren': 'off' 61 | }, 62 | parserOptions: { 63 | parser: 'babel-eslint' 64 | }, 65 | overrides: [ 66 | { 67 | files: [ 68 | '**/__tests__/*.{j,t}s?(x)', 69 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 70 | ], 71 | env: { 72 | jest: true 73 | } 74 | } 75 | ] 76 | } 77 | -------------------------------------------------------------------------------- /src/views/member/list/Add.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 74 | 75 | 77 | -------------------------------------------------------------------------------- /src/components/UploadButton/UploadButton.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 选择文件 8 | 9 | 查看 10 | 11 | 12 | 13 | 72 | 73 | 78 | -------------------------------------------------------------------------------- /src/api/refunds.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from '@/api/config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 订单退款审核 6 | * @param sn 7 | * @param review 8 | * @returns {Promise} 9 | */ 10 | export function reviewRefundOrder(sn, review) { 11 | const url = '/refunds/review' 12 | 13 | return axios({ 14 | url: url, 15 | method: 'PUT', 16 | headers: Object.assign({}, HEADERS.PUT), 17 | data: { 18 | sn: sn, 19 | review: review 20 | } 21 | }).then((res) => { 22 | return Promise.resolve(res.data) 23 | }) 24 | } 25 | 26 | /** 27 | * 通过订单获取退款信息 28 | * @param orderId 29 | * @returns {Promise<*>} 30 | */ 31 | export function getRefundsByOrder(orderId) { 32 | const url = '/refunds/order' 33 | 34 | return axios({ 35 | url: url, 36 | method: 'GET', 37 | params: { 38 | order_id: orderId, 39 | sortby: 'CreationDate', 40 | order: 'desc' 41 | } 42 | }).then((res) => { 43 | return Promise.resolve(res.data) 44 | }) 45 | } 46 | 47 | /** 48 | * 获取退款信息 49 | * @param sn 50 | * @returns {*} 51 | */ 52 | export function getRefunds(sn) { 53 | const url = `/refunds/${sn}` 54 | 55 | return axios({ 56 | url: url, 57 | method: 'GET' 58 | }).then((res) => { 59 | return Promise.resolve(res.data) 60 | }) 61 | } 62 | 63 | /** 64 | * 获取退款列表 65 | * @param query 66 | * @param sortby 67 | * @param order 68 | * @param pageNumber 69 | * @param pageSize 70 | * @returns {Promise} 71 | */ 72 | export function getRefundsList({ query, sortby, order, pageNumber, pageSize }) { 73 | const url = '/refunds' 74 | 75 | return axios({ 76 | url: url, 77 | method: 'GET', 78 | params: { 79 | query: getObjectKeyValueToString(query), 80 | sortby: sortby, 81 | order: order, 82 | pageNumber: pageNumber, 83 | pageSize: pageSize 84 | } 85 | }).then((res) => { 86 | return Promise.resolve(res.data) 87 | }) 88 | } 89 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | 4 | import app from './modules/app' 5 | 6 | import asyncRouter from './modules/async-router' 7 | 8 | import permission from './modules/permission' 9 | import product from './modules/product' 10 | import productCategory from './modules/product_category' 11 | import promotion from './modules/promotion' 12 | import brand from './modules/brand' 13 | import tag from './modules/tag' 14 | import setting from './modules/setting' 15 | import parameter from './modules/parameter' 16 | import attribute from './modules/attribute' 17 | import specification from './modules/specification' 18 | import sku from './modules/sku' 19 | import order from './modules/order' 20 | import area from './modules/area' 21 | import user from './modules/user' 22 | import deliveryCorp from './modules/delivery_corp' 23 | import shippingMethod from './modules/shipping_method' 24 | import upload from './modules/upload' 25 | import role from './modules/role' 26 | import paymentMethod from './modules/payment_method' 27 | import memberRank from './modules/member_rank' 28 | import member from './modules/member' 29 | import coupon from './modules/coupon' 30 | import ad from './modules/ad' 31 | 32 | // dynamic router permission control (Experimental) 33 | // import permission from './modules/async-router' 34 | import getters from './getters' 35 | 36 | Vue.use(Vuex) 37 | 38 | export default new Vuex.Store({ 39 | modules: { 40 | app, 41 | asyncRouter, 42 | permission, 43 | product, 44 | productCategory, 45 | promotion, 46 | brand, 47 | tag, 48 | setting, 49 | parameter, 50 | attribute, 51 | specification, 52 | sku, 53 | order, 54 | area, 55 | user, 56 | deliveryCorp, 57 | shippingMethod, 58 | paymentMethod, 59 | upload, 60 | role, 61 | memberRank, 62 | member, 63 | coupon, 64 | ad 65 | }, 66 | state: {}, 67 | mutations: {}, 68 | actions: {}, 69 | getters 70 | }) 71 | -------------------------------------------------------------------------------- /src/store/modules/shipping_method.js: -------------------------------------------------------------------------------- 1 | import { getShippingMethodList, getShippingMethodCount } from '@/api/shipping_method' 2 | import { ERR_OK } from '@/api/config' 3 | 4 | const shippingMethod = { 5 | state: { 6 | pageList: {}, 7 | currentShippingMethod: null 8 | }, 9 | mutations: { 10 | SET_SHIPPING_METHODS: (state, pageList) => { 11 | state.pageList = pageList 12 | }, 13 | SET_CURRENT_SHIPPING_METHOD: (state, method) => { 14 | state.currentShippingMethod = method 15 | } 16 | }, 17 | actions: { 18 | /** 19 | * 获取配送方式 20 | * @param commit 21 | * @param query 22 | * @param sortby 23 | * @param order 24 | * @param pageNumber 25 | * @param pageSize 26 | * @returns {Promise} 27 | * @constructor 28 | */ 29 | GetShippingMethods({ commit }, { query, sortby, order, pageNumber, pageSize }) { 30 | return new Promise(resolve => { 31 | getShippingMethodList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 32 | if (res.err_code === ERR_OK) { 33 | commit('SET_SHIPPING_METHODS', res.data) 34 | resolve(res.data) 35 | } 36 | }) 37 | }) 38 | }, 39 | /** 40 | * 获取所有配送方式 41 | * @param dispatch 42 | * @constructor 43 | */ 44 | GetAllShippingMethods({ dispatch }) { 45 | getShippingMethodCount().then((res) => { 46 | if (res.err_code === ERR_OK) { 47 | const pageNumber = 1 48 | const pageSize = res.data 49 | dispatch('GetShippingMethods', { pageNumber, pageSize }) 50 | } 51 | }) 52 | }, 53 | /** 54 | * 设置当前配送方式 55 | * @param commit 56 | * @param method 57 | * @constructor 58 | */ 59 | SetCurrentShippingMethod({ commit }, method) { 60 | return new Promise(resolve => { 61 | commit('SET_CURRENT_SHIPPING_METHOD', method) 62 | resolve() 63 | }) 64 | } 65 | } 66 | } 67 | 68 | export default shippingMethod 69 | -------------------------------------------------------------------------------- /src/views/marketing/coupon/Add.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 74 | 75 | 77 | -------------------------------------------------------------------------------- /src/views/order/list/components/LogsInfo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | {{ content ? content : '-' }} 12 | 13 | 14 | 15 | 16 | {{ creation_date | Moment }} 17 | 18 | {{ creation_date | Moment('YYYY-MM-DD') }} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 86 | 87 | 90 | -------------------------------------------------------------------------------- /docs/load-on-demand.md: -------------------------------------------------------------------------------- 1 | 按需加载 减小打包 2 | ==== 3 | 4 | 5 | 6 | ## 按需引入组件依赖 7 | 8 | `Ant Design Pro Vue` 默认编码工作并不支持按需引入,不过可以通过以下操作结合 [Ant Design Of Vue](https://vuecomponent.github.io/ant-design-vue/docs/vue/introduce-cn/) 官方文档来进行按需引入。 9 | 10 | - 增加项目按需引入依赖 11 | - 修改引入组件方式 12 | 13 | 14 | 15 | 1. 增加按需引入所需依赖 `babel-plugin-import` 16 | 并且修改文件 `babel.config.js` 17 | ```ecmascript 6 18 | module.exports = { 19 | presets: [ 20 | '@vue/app' 21 | ], 22 | plugins: [ 23 | [ "import", { 24 | "libraryName": "ant-design-vue", 25 | "libraryDirectory": "es", 26 | "style": "css" 27 | } ] 28 | ] 29 | } 30 | ``` 31 | 32 | 33 | 2. 修改引入组件方式 (注意,这只是一个例子,请完整引入你所需要的组件) 34 | 35 | 文件 `@/core/lazy_lib/component_use.js` 36 | 37 | ```javascript 38 | import Vue from 'vue' 39 | import { 40 | Input, 41 | Button, 42 | Select, 43 | Card, 44 | Form, 45 | Row, 46 | Col, 47 | Modal, 48 | Table, 49 | notification 50 | } from 'ant-design-vue' 51 | 52 | Vue.use(Input) 53 | Vue.use(Button) 54 | Vue.use(Select) 55 | Vue.use(Card) 56 | Vue.use(Form) 57 | Vue.use(Row) 58 | Vue.use(Col) 59 | Vue.use(Modal) 60 | Vue.use(Table) 61 | Vue.use(notification) 62 | 63 | Vue.prototype.$notification = notification; 64 | ``` 65 | 66 | 67 | 3. 最后在 `main.js` 中引入 `@/core/lazy_use.js` 文件即可,如下 68 | 69 | ```javascript 70 | 71 | import Vue from 'vue' 72 | import App from './App' 73 | 74 | // 引入 按需组件的统一引入文件 75 | import './core/lazy_use' 76 | 77 | import './style/index.less' 78 | 79 | 80 | Vue.config.productionTip = false 81 | 82 | new Vue({ 83 | render: h => h(App), 84 | }).$mount('#app') 85 | 86 | ``` 87 | 88 | 89 | 90 | ## 其他 减少打包大小 91 | 92 | 93 | 94 | 1. Ant Design Vue 1.2.x 版本起,采用的 ant-design 官方方案 svg Icon 组件,整个项目打包会变大很多,图标进行按需加载可参考 https://github.com/HeskeyBaozi/reduce-antd-icons-bundle-demo 95 | 2. moment 按需加载 可参考 https://github.com/jmblog/how-to-optimize-momentjs-with-webpack 96 | -------------------------------------------------------------------------------- /src/views/order/list/Look.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 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 | 32 | 返回 33 | 34 | 35 | 36 | 37 | 38 | 39 | 69 | 70 | 73 | -------------------------------------------------------------------------------- /src/components/ArticleListContent/ArticleListContent.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ description }} 6 | 7 | 8 | 9 | 10 | {{ owner }} 发布在 {{ href }} 11 | {{ updateAt | Moment }} 12 | 13 | 14 | 15 | 16 | 47 | 48 | 90 | -------------------------------------------------------------------------------- /src/views/system/login_plugin/Index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | {{ version ? version : '1.0' }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | [启用] 23 | 24 | [设置] 25 | 26 | [卸载] 27 | 28 | 29 | 30 | 31 | 32 | 33 | 79 | 80 | 83 | -------------------------------------------------------------------------------- /src/views/system/storage_plugin/Index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | {{ version ? version : '1.0' }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | [启用] 23 | 24 | [设置] 25 | 26 | [卸载] 27 | 28 | 29 | 30 | 31 | 32 | 33 | 79 | 80 | 83 | -------------------------------------------------------------------------------- /src/views/system/payment_plugin/Index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 14 | 15 | {{ version ? version : '1.0' }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | [启用] 23 | 24 | [设置] 25 | 26 | [卸载] 27 | 28 | 29 | 30 | 31 | 32 | 33 | 79 | 80 | 83 | -------------------------------------------------------------------------------- /src/views/order/list/components/ProductsInfo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | {{ price | Amount }} 12 | 13 | 14 | {{ total | Amount }} 15 | 16 | 17 | 18 | 19 | 20 | 93 | 94 | 97 | -------------------------------------------------------------------------------- /src/views/order/list/components/ReturnsInfo.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | {{ creation_date | Moment }} 14 | 15 | {{ creation_date | Moment('YYYY-MM-DD') }} 16 | 17 | 18 | 19 | 20 | 21 | 22 | 94 | 95 | 98 | -------------------------------------------------------------------------------- /src/components/GlobalHeader/AvatarDropdown.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | {{ currentUser.name }} 9 | 10 | 11 | 12 | 13 | 14 | 个人设置 15 | 16 | 17 | 18 | 19 | 退出登录 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 66 | 67 | 78 | -------------------------------------------------------------------------------- /src/store/modules/product_category.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 商品分类 3 | */ 4 | import { getProductCategoryList } from '@/api/product_category' 5 | import { ERR_OK } from '@/api/config' 6 | import { toTreeData } from '@/utils/util' 7 | 8 | const productCategory = { 9 | state: { 10 | list: [], 11 | // 对应后端返回字段 12 | attrs: [ 13 | { key: 'is_marketable', label: '是否上架' }, 14 | { key: 'is_top', label: '是否置顶' }, 15 | { key: 'is_show', label: '是否显示' } 16 | ], 17 | currentCategory: {} 18 | }, 19 | getters: { 20 | // 商品分类列表 21 | productCategoryList: state => state.list, 22 | // 当前商品分类 23 | productCategory: state => state.currentCategory, 24 | treeDataProductCategoryList: state => { 25 | if (state.list && state.list.length) { 26 | return toTreeData(state.list.slice(), 'id') 27 | } 28 | return [] 29 | }, 30 | // 商品分类属性列表 31 | productCategoryAttrs: state => state.attrs 32 | }, 33 | mutations: { 34 | SET_PRODUCT_CATEGORY_LIST: (state, list) => { 35 | state.list = list 36 | }, 37 | SET_CURRENT_PRODUCT_CATEGORY: (state, category) => { 38 | state.currentCategory = category 39 | } 40 | }, 41 | actions: { 42 | /** 43 | * 获取商品分类列表 44 | * @param commit 45 | * @param query 46 | * @param sortBy 47 | * @returns {Promise} 48 | * @constructor 49 | */ 50 | GetProductCategoryList({ commit }, query) { 51 | return new Promise(resolve => { 52 | const sortBy = 'Orders' 53 | const order = 'asc' 54 | getProductCategoryList({ query, sortBy, order }).then((res) => { 55 | if (res.err_code === ERR_OK) { 56 | const categoryList = res.data 57 | commit('SET_PRODUCT_CATEGORY_LIST', categoryList) 58 | resolve(categoryList) 59 | } 60 | }) 61 | }) 62 | }, 63 | /** 64 | * 获取当前分类 65 | * @param commit 66 | * @param category 67 | * @returns {Promise} 68 | * @constructor 69 | */ 70 | GetCurrentProductCategory({ commit }, category) { 71 | commit('SET_CURRENT_PRODUCT_CATEGORY', category) 72 | } 73 | } 74 | } 75 | 76 | export default productCategory 77 | -------------------------------------------------------------------------------- /src/components/IconSelector/IconSelector.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 62 | 63 | 87 | -------------------------------------------------------------------------------- /src/api/member.js: -------------------------------------------------------------------------------- 1 | import axios, { HEADERS } from './config' 2 | import { getObjectKeyValueToString } from '@/utils/util' 3 | 4 | /** 5 | * 添加会员 6 | * @param memberRank 7 | * @returns {*} 8 | */ 9 | export function addMember(memberRank) { 10 | const url = '/member/add' 11 | 12 | return axios({ 13 | url: url, 14 | method: 'POST', 15 | headers: Object.assign({}, HEADERS.POST), 16 | data: { 17 | member: memberRank 18 | } 19 | }).then((res) => { 20 | return Promise.resolve(res.data) 21 | }) 22 | } 23 | 24 | /** 25 | * 删除会员 26 | * @returns {*} 27 | */ 28 | export function deleteMember(ids) { 29 | const url = '/member/delete' 30 | 31 | return axios({ 32 | url: url, 33 | method: 'DELETE', 34 | params: { 35 | ids: ids 36 | } 37 | }).then((res) => { 38 | return Promise.resolve(res.data) 39 | }) 40 | } 41 | 42 | /** 43 | * 更新会员 44 | * @param memberRank 45 | * @returns {*} 46 | */ 47 | export function updateMember(memberRank) { 48 | const url = '/member/update' 49 | 50 | return axios({ 51 | url: url, 52 | method: 'PUT', 53 | headers: Object.assign({}, HEADERS.PUT), 54 | data: { 55 | member: memberRank 56 | } 57 | }).then((res) => { 58 | return Promise.resolve(res.data) 59 | }) 60 | } 61 | 62 | /** 63 | * 获取会员 64 | */ 65 | export function getMember(id) { 66 | const url = `/member/${id}` 67 | 68 | return axios({ 69 | url: url, 70 | method: 'GET' 71 | }).then((res) => { 72 | return Promise.resolve(res.data) 73 | }) 74 | } 75 | 76 | /** 77 | * 获取会员列表 78 | * @param query 79 | * @param sortby 80 | * @param order 81 | * @param pageNumber 82 | * @param pageSize 83 | * @returns {Promise<*>} 84 | */ 85 | export function getMemberList({ query, sortby, order, pageNumber, pageSize }) { 86 | const url = '/member' 87 | 88 | return axios({ 89 | url: url, 90 | method: 'GET', 91 | params: { 92 | query: getObjectKeyValueToString(query), 93 | sortby: sortby, 94 | order: order, 95 | pageNumber: pageNumber, 96 | pageSize: pageSize 97 | } 98 | }).then((res) => { 99 | return Promise.resolve(res.data) 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-antd-pro", 3 | "version": "3.0.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "lint": "vue-cli-service lint", 10 | "build:preview": "vue-cli-service build --mode preview", 11 | "lint:nofix": "vue-cli-service lint --no-fix", 12 | "report": "vue-cli-service --report" 13 | }, 14 | "dependencies": { 15 | "@ant-design-vue/pro-layout": "^0.3.0", 16 | "ant-design-vue": "1.6.0", 17 | "axios": "^0.19.0", 18 | "core-js": "^3.1.2", 19 | "enquire.js": "^2.1.6", 20 | "lodash.clonedeep": "^4.5.0", 21 | "lodash.get": "^4.4.2", 22 | "lodash.pick": "^4.4.0", 23 | "md5": "^2.2.1", 24 | "moment": "^2.24.0", 25 | "nprogress": "^0.2.0", 26 | "qs": "^6.9.4", 27 | "store": "^2.0.12", 28 | "uuid": "^8.2.0", 29 | "vue": "^2.6.10", 30 | "vue-clipboard2": "^0.2.1", 31 | "vue-cookies": "^1.7.4", 32 | "vue-cropper": "0.4.9", 33 | "vue-i18n": "^8.17.4", 34 | "vue-router": "^3.0.0", 35 | "vue-svg-component-runtime": "^1.0.1", 36 | "vuex": "^3.1.1" 37 | }, 38 | "devDependencies": { 39 | "@ant-design/colors": "^3.2.1", 40 | "@vue/cli-plugin-babel": "^4.0.4", 41 | "@vue/cli-plugin-eslint": "^4.0.4", 42 | "@vue/cli-plugin-router": "^4.0.4", 43 | "@vue/cli-plugin-unit-jest": "^4.0.4", 44 | "@vue/cli-plugin-vuex": "^4.0.4", 45 | "@vue/cli-service": "^4.0.4", 46 | "@vue/eslint-config-standard": "^4.0.0", 47 | "@vue/test-utils": "^1.0.0-beta.29", 48 | "babel-eslint": "^10.0.1", 49 | "babel-plugin-import": "^1.12.2", 50 | "babel-plugin-transform-remove-console": "^6.9.4", 51 | "compression-webpack-plugin": "^5.0.2", 52 | "eslint": "^5.16.0", 53 | "eslint-plugin-html": "^5.0.0", 54 | "eslint-plugin-vue": "^5.2.3", 55 | "git-revision-webpack-plugin": "^3.0.6", 56 | "less": "^3.0.4", 57 | "less-loader": "^5.0.0", 58 | "opencollective": "^1.0.3", 59 | "opencollective-postinstall": "^2.0.2", 60 | "vue-svg-icon-loader": "^2.1.1", 61 | "vue-template-compiler": "^2.6.10", 62 | "webpack-bundle-analyzer": "^4.3.0", 63 | "webpack-theme-color-replacer": "^1.3.12" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/store/modules/async-router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 向后端请求用户的菜单,动态生成路由 3 | */ 4 | import { asyncRouterMap, constantRouterMap } from '@/config/router.config' 5 | import { deepClone } from '@/utils/util' 6 | 7 | /** 8 | * 过滤账户是否拥有某一个权限,并将菜单从加载列表移除 9 | * @param permission 10 | * @param route 11 | * @returns {boolean} 12 | */ 13 | function hasPermission(permission, route) { 14 | if (route.meta && route.meta.permission) { 15 | let flag = false 16 | for (let i = 0, len = permission.length; i < len; i++) { 17 | flag = route.meta.permission.includes(permission[i]) 18 | if (flag) { 19 | return true 20 | } 21 | } 22 | return false 23 | } 24 | return true 25 | } 26 | 27 | /** 28 | * 单账户多角色时,使用该方法可过滤角色不存在的菜单 29 | * 30 | * @param roles 31 | * @param route 32 | * @returns {*} 33 | */ 34 | // eslint-disable-next-line 35 | function hasRole(roles, route) { 36 | if (route.meta && route.meta.roles) { 37 | return route.meta.roles.includes(roles.id) 38 | } else { 39 | return true 40 | } 41 | } 42 | 43 | function filterAsyncRouter(routerMap, permissions) { 44 | return routerMap.filter(route => { 45 | if (hasPermission(permissions, route)) { 46 | if (route.children && route.children.length) { 47 | route.children = filterAsyncRouter(route.children, permissions) 48 | } 49 | return true 50 | } 51 | return false 52 | }) 53 | } 54 | 55 | /** 56 | * 过滤权限值 57 | */ 58 | function filterPermissions(permissions) { 59 | return permissions.map((item) => { 60 | return item.value 61 | }) 62 | } 63 | 64 | const asyncRouter = { 65 | state: { 66 | routers: constantRouterMap, 67 | addRouters: [] 68 | }, 69 | mutations: { 70 | SET_ROUTERS: (state, routers) => { 71 | state.addRouters = routers 72 | state.routers = constantRouterMap.concat(routers) 73 | } 74 | }, 75 | actions: { 76 | GenerateRoutes({ commit }, permissions) { 77 | return new Promise(resolve => { 78 | const deepCloneRouterMap = deepClone(asyncRouterMap) 79 | const accessedRouters = filterAsyncRouter(deepCloneRouterMap, filterPermissions(permissions)) 80 | commit('SET_ROUTERS', accessedRouters) 81 | resolve() 82 | }) 83 | } 84 | } 85 | } 86 | 87 | export default asyncRouter 88 | -------------------------------------------------------------------------------- /src/store/modules/coupon.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 优惠券 3 | */ 4 | import { getCoupon, getCouponCount, getCouponList } from '@/api/coupon' 5 | import { ERR_OK } from '@/api/config' 6 | 7 | const memberRank = { 8 | state: { 9 | pageList: {}, 10 | coupon: {} 11 | }, 12 | mutations: { 13 | SET_COUPON_PAGE_LIST: (state, pageList) => { 14 | state.pageList = pageList 15 | }, 16 | SET_COUPON: (state, coupon) => { 17 | state.coupon = coupon 18 | } 19 | }, 20 | actions: { 21 | /** 22 | * 获取优惠券 23 | * @param commit 24 | * @param id 25 | * @constructor 26 | */ 27 | GetCoupon({ commit }, id) { 28 | return new Promise(resolve => { 29 | getCoupon(id).then((res) => { 30 | if (res.err_code === ERR_OK) { 31 | commit('SET_COUPON', res.data) 32 | resolve(res.data) 33 | } 34 | }) 35 | }) 36 | }, 37 | /** 38 | * 获取所有优惠券列表 39 | * @param commit 40 | * @returns {Promise} 41 | */ 42 | GetAllCoupon({ commit }) { 43 | return new Promise(resolve => { 44 | getCouponCount().then((res) => { 45 | if (res.err_code === ERR_OK) { 46 | const pageNumber = 1 47 | const pageSize = res.data 48 | getCouponList({ pageNumber, pageSize }).then((res) => { 49 | if (res.err_code === ERR_OK) { 50 | commit('SET_COUPON_PAGE_LIST', res.data) 51 | resolve(res.data) 52 | } 53 | }) 54 | } 55 | }) 56 | }) 57 | }, 58 | /** 59 | * 获取优惠券列表 60 | * @param commit 61 | * @param query 62 | * @param sortby 63 | * @param order 64 | * @param pageNumber 65 | * @param pageSize 66 | * @returns {Promise} 67 | * @constructor 68 | */ 69 | GetCouponList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 70 | return new Promise(resolve => { 71 | getCouponList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 72 | if (res.err_code === ERR_OK) { 73 | commit('SET_COUPON_LIST', res.data) 74 | resolve(res.data) 75 | } 76 | }) 77 | }) 78 | } 79 | } 80 | } 81 | 82 | export default memberRank 83 | -------------------------------------------------------------------------------- /src/store/modules/promotion.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 促销 3 | */ 4 | import { getPromotion, getPromotionCount, getPromotionList } from '@/api/promotion' 5 | import { ERR_OK } from '@/api/config' 6 | 7 | const promotion = { 8 | state: { 9 | pageList: {}, 10 | promotion: {} 11 | }, 12 | mutations: { 13 | SET_PROMOTION_PAGE_LIST: (state, pageList) => { 14 | state.pageList = pageList 15 | }, 16 | SET_PROMOTION: (state, promotion) => { 17 | state.promotion = promotion 18 | } 19 | }, 20 | actions: { 21 | /** 22 | * 获取所有促销列表 23 | * @param commit 24 | * @returns {Promise} 25 | */ 26 | GetAllPromotionList({ commit }) { 27 | getPromotionCount().then((res) => { 28 | if (res.err_code === ERR_OK && res.data) { 29 | const pageNumber = 1 30 | const pageSize = res.data 31 | getPromotionList({ pageNumber, pageSize }).then((res) => { 32 | if (res.err_code === ERR_OK) { 33 | commit('SET_PROMOTION_PAGE_LIST', res.data) 34 | } 35 | }) 36 | } 37 | }) 38 | }, 39 | /** 40 | * 获取促销列表 41 | * @param commit 42 | * @param query 43 | * @param sortby 44 | * @param order 45 | * @param pageNumber 46 | * @param pageSize 47 | * @returns {Promise} 48 | * @constructor 49 | */ 50 | GetPromotionList({ commit }, { query, sortby, order, pageNumber, pageSize }) { 51 | return new Promise(resolve => { 52 | getPromotionList({ query, sortby, order, pageNumber, pageSize }).then((res) => { 53 | if (res.err_code === ERR_OK) { 54 | commit('SET_PROMOTION_PAGE_LIST', res.data) 55 | resolve(res.data) 56 | } 57 | }) 58 | }) 59 | }, 60 | /** 61 | * 获取促销 62 | * @param commit 63 | * @param id 64 | * @returns {Promise} 65 | * @constructor 66 | */ 67 | GetPromotion({ commit }, id) { 68 | return new Promise(resolve => { 69 | getPromotion(id).then((res) => { 70 | if (res.err_code === ERR_OK) { 71 | commit('SET_PROMOTION', res.data) 72 | resolve(res.data) 73 | } 74 | }) 75 | }) 76 | } 77 | } 78 | } 79 | 80 | export default promotion 81 | -------------------------------------------------------------------------------- /src/views/member/list/components/Add/Info.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 19 | 男 20 | 女 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 87 | 88 | 91 | -------------------------------------------------------------------------------- /src/views/member/list/components/Edit/Info.vue: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 19 | 男 20 | 女 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 33 | 34 | 35 | 36 | 87 | 88 | 91 | -------------------------------------------------------------------------------- /src/views/marketing/promotion/Add.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 82 | 83 | 85 | -------------------------------------------------------------------------------- /src/api/config.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import qs from 'qs' 3 | import router from '@/router' 4 | import cookies from 'vue-cookies' 5 | import { BASE_URL } from '@/config/axios.config.js' 6 | import { Message } from 'ant-design-vue' 7 | 8 | export const ERR_ERROR = 0 9 | export const ERR_OK = 1 10 | export const UNAUTHORIZED = 401 11 | export const SERVER_ERROR = 500 12 | 13 | export const HEADERS = { 14 | POST: { 15 | 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' 16 | }, 17 | PUT: { 18 | 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' 19 | }, 20 | FromData: { 21 | 'Content-Type': 'multipart/form-data' 22 | } 23 | } 24 | 25 | function creatAxios() { 26 | const instance = axios.create({ 27 | baseURL: BASE_URL 28 | }) 29 | 30 | // 允许携带 cookie 31 | instance.defaults.withCredentials = true 32 | 33 | // Add a request interceptor 34 | instance.interceptors.request.use(function(config) { 35 | const { headers, data } = config 36 | if (headers['Content-Type'] === HEADERS.POST['Content-Type']) { 37 | // 把一个参数对象格式化为一个字符串 38 | config.data = qs.stringify(data) 39 | } else if (headers['Content-Type'] === 'multipart/form-data') { 40 | config.data = data 41 | } else { 42 | headers['Content-Type'] = 'application/json' 43 | } 44 | return config 45 | }, function(error) { 46 | // Do something with request error 47 | return Promise.reject(error) 48 | }) 49 | 50 | // Add a response interceptor 51 | instance.interceptors.response.use(function(response) { 52 | // Any status code that lie within the range of 2xx cause this function to trigger 53 | // Do something with response data 54 | if (response.data.err_code === UNAUTHORIZED) { 55 | cookies.remove('admin_login') 56 | router.push('/user/login') 57 | return response 58 | } 59 | if (response.data.err_code === ERR_ERROR) { 60 | Message.error(response.data.data) 61 | return response 62 | } 63 | return response 64 | }, function(error) { 65 | // Any status codes that falls outside the range of 2xx cause this function to trigger 66 | // Do something with response error 67 | return Promise.reject(error) 68 | }) 69 | 70 | return instance 71 | } 72 | 73 | const Axios = creatAxios() 74 | 75 | export default Axios 76 | --------------------------------------------------------------------------------