├── .browserslistrc ├── .editorconfig ├── .env.development ├── .env.production ├── .env.production.uat ├── .env.staging ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .gitlab-ci.yml ├── .prettierrc.json ├── Dockerfile ├── LICENSE ├── README.md ├── babel.config.js ├── cypress.json ├── jest.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── img │ └── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── apple-touch-icon-120x120.png │ │ ├── apple-touch-icon-152x152.png │ │ ├── apple-touch-icon-180x180.png │ │ ├── apple-touch-icon-60x60.png │ │ ├── apple-touch-icon-76x76.png │ │ ├── apple-touch-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── msapplication-icon-144x144.png │ │ ├── mstile-150x150.png │ │ └── safari-pinned-tab.svg ├── index.html ├── manifest.json └── robots.txt ├── report.20210610.155436.5628.0.001.json ├── src ├── App.vue ├── api │ ├── category.ts │ ├── charts.ts │ ├── dish.ts │ ├── employee.ts │ ├── index.ts │ ├── inform.ts │ ├── order.ts │ ├── setMeal.ts │ └── users.ts ├── assets │ ├── 404-images │ │ ├── 404-cloud.png │ │ └── 404.png │ ├── icons │ │ ├── addicon.png │ │ ├── btn_add@2x.png │ │ ├── btn_back@2x.png │ │ ├── btn_clean@2x.png │ │ ├── btn_close@2x.png │ │ ├── btn_close@2x1.png │ │ ├── btn_gaimi@2x.png │ │ ├── fall.png │ │ ├── gzt_daijiedan.png │ │ ├── gzt_daipaisong.png │ │ ├── gzt_quanbu.png │ │ ├── gzt_quxiao.png │ │ ├── gzt_wancheng.png │ │ ├── gzt_yiqishou@2x.png │ │ ├── gzt_yitingshou@2x.png │ │ ├── icon_index.png │ │ ├── icon_more@2x.png │ │ ├── icon_upload@2x.png │ │ ├── jine_m-2@2x.png │ │ ├── msg.png │ │ ├── renshu@2x.png │ │ ├── time.png │ │ ├── up.png │ │ └── xiangmujine@2x.png │ ├── img_denglu_bj.jpg │ ├── login │ │ ├── icon_logo.png │ │ ├── login-l.png │ │ ├── login-logo.png │ │ ├── logo.png │ │ ├── mini-logo.png │ │ └── white_logo.png │ ├── noImg.png │ ├── preview.mp3 │ ├── reminder.mp3 │ ├── search_table_empty.png │ └── table_empty.png ├── components │ ├── Breadcrumb │ │ └── index.vue │ ├── Charts │ │ ├── BarChart.vue │ │ ├── Basic.vue │ │ ├── Line.vue │ │ ├── MixedChart.vue │ │ └── mixins │ │ │ └── resize.ts │ ├── Empty │ │ └── index.vue │ ├── Hamburger │ │ └── index.vue │ ├── HeadLable │ │ └── index.vue │ ├── ImgUpload │ │ └── index.vue │ └── InputAutoComplete │ │ └── index.vue ├── config.json ├── icons │ ├── README.md │ ├── components │ │ ├── dashboard.ts │ │ ├── employee.ts │ │ ├── hamburger.ts │ │ ├── index.ts │ │ ├── inform.ts │ │ ├── main.ts │ │ ├── pay.ts │ │ ├── shop.ts │ │ └── vip.ts │ └── svg │ │ ├── dashboard.svg │ │ ├── hamburger.svg │ │ ├── inform.svg │ │ ├── main.svg │ │ ├── member.svg │ │ ├── pay.svg │ │ ├── shop.svg │ │ └── vip.svg ├── layout │ ├── components │ │ ├── AppMain.vue │ │ ├── Navbar │ │ │ └── index.vue │ │ ├── Sidebar │ │ │ ├── SidebarItem.vue │ │ │ ├── SidebarItemLink.vue │ │ │ └── index.vue │ │ ├── components │ │ │ └── password.vue │ │ └── index.ts │ ├── index.vue │ └── mixin │ │ └── resize.ts ├── main.ts ├── permission.ts ├── registerServiceWorker.ts ├── router.ts ├── shims-vue.d.ts ├── store │ ├── index.ts │ └── modules │ │ ├── app.ts │ │ └── user.ts ├── styles │ ├── _mixins.scss │ ├── _svgicon.scss │ ├── _transition.scss │ ├── _variables.scss │ ├── _variables.scss.d.ts │ ├── element-variables.scss │ ├── fonts │ │ ├── element-icons.ttf │ │ └── element-icons.woff │ ├── home.scss │ ├── icon │ │ ├── demo.css │ │ ├── demo_index.html │ │ ├── iconfont.css │ │ ├── iconfont.js │ │ ├── iconfont.json │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ ├── index.scss │ ├── newRJWMsystem.scss │ └── sidebar.scss ├── utils │ ├── common.ts │ ├── cookies.ts │ ├── formValidate.ts │ ├── request.ts │ ├── requestOptimize.ts │ └── validate.ts └── views │ ├── 404.vue │ ├── category │ └── index.vue │ ├── chart │ └── index.vue │ ├── dashboard │ ├── components │ │ ├── cuisineStatistics.vue │ │ ├── orderList.vue │ │ ├── orderview.vue │ │ ├── overview.vue │ │ └── setMealStatistics.vue │ └── index.vue │ ├── dish │ ├── addDishtype.vue │ ├── components │ │ └── SelectInput.vue │ └── index.vue │ ├── employee │ ├── addEmployee.vue │ └── index.vue │ ├── inform │ └── index.vue │ ├── login │ └── index.vue │ ├── orderDetails │ ├── index.vue │ └── tabChange.vue │ ├── setmeal │ ├── addSetmeal.vue │ ├── components │ │ └── AddDish.vue │ └── index.vue │ └── statistics │ ├── components │ ├── orderStatistics.vue │ ├── overview.vue │ ├── titleIndex.vue │ ├── top10.vue │ ├── turnoverStatistics.vue │ └── userStatistics.vue │ └── index.vue ├── tests └── unit │ ├── .eslintrc.js │ ├── components │ └── Breadcrumb.spec.ts │ └── utils │ └── validate.spec.ts ├── tsconfig.json ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | # Indentation override for js(x), ts(x) and vue files 14 | [*.{js,jsx,ts,tsx,vue}] 15 | indent_size = 2 16 | indent_style = space 17 | 18 | # Indentation override for css related files 19 | [*.{css,styl,scss,less,sass}] 20 | indent_size = 2 21 | indent_style = space 22 | 23 | # Indentation override for html files 24 | [*.html] 25 | indent_size = 2 26 | indent_style = space 27 | 28 | # Trailing space override for markdown file 29 | [*.md] 30 | trim_trailing_whitespace = false 31 | 32 | # Indentation override for config files 33 | [*.{json,yml}] 34 | indent_size = 2 35 | indent_style = space 36 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # Base api 2 | VUE_APP_BASE_API = '/api' 3 | 4 | # vue-cli uses the VUE_CLI_BABEL_TRANSPILE_MODULES environment variable, 5 | # to control whether the babel-plugin-dynamic-import-node plugin is enabled. 6 | # It only does one thing by converting all import() to require(). 7 | # This configuration can significantly increase the speed of hot updates, 8 | # when you have a large number of pages. 9 | # Detail: https://github.com/vuejs/vue-cli/blob/dev/packages/@vue/babel-preset-app/index.js 10 | 11 | NODE_ENV = 'development' 12 | VUE_APP_NODE_ENV = 'dev' 13 | 14 | //后端服务的地址 15 | VUE_APP_URL = 'http://localhost:8080/admin' 16 | 17 | VUE_APP_SOCKET_URL = 'ws://localhost:8080/ws/' 18 | //VUE_APP_SOCKET_URL = 'ws://http://23ce4dbd.cpolar.cn/ws/' 19 | 20 | 21 | VUE_CLI_BABEL_TRANSPILE_MODULES = true 22 | # 删除权限 true/有 23 | VUE_APP_DELETE_PERMISSIONS = true 24 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # Base api 2 | # Remeber to change this to your production server address 3 | # Here I used my mock server for this project 4 | # VUE_APP_BASE_API = 'https://vue-typescript-admin-mock-server.armour.now.sh/mock-api/v1/' 5 | 6 | NODE_ENV = 'production' 7 | VUE_APP_NODE_ENV = 'prod' 8 | VUE_APP_BASE_API = '/api' 9 | VUE_APP_URL='' 10 | VUE_APP_SOCKET_URL = '' 11 | # 删除权限 true/有 12 | VUE_APP_DELETE_PERMISSIONS = false 13 | -------------------------------------------------------------------------------- /.env.production.uat: -------------------------------------------------------------------------------- 1 | # Base api 2 | # Remeber to change this to your production server address 3 | # Here I used my mock server for this project 4 | # VUE_APP_BASE_API = 'https://vue-typescript-admin-mock-server.armour.now.sh/mock-api/v1/' 5 | 6 | NODE_ENV = 'production' 7 | VUE_APP_NODE_ENV = 'prod:uat' 8 | VUE_APP_BASE_API = '/api' 9 | # 删除权限 true/有 10 | VUE_APP_DELETE_PERMISSIONS = true -------------------------------------------------------------------------------- /.env.staging: -------------------------------------------------------------------------------- 1 | # Set to production for building optimization 2 | NODE_ENV = production 3 | 4 | # Base api 5 | # VUE_APP_BASE_API = '/stage-api' 6 | 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/*.js 2 | src/assets 3 | tests/unit/coverage 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | es6: true 7 | }, 8 | 'extends': [ 9 | 'eslint:recommended', 10 | 'plugin:vue/recommended', 11 | '@vue/standard', 12 | '@vue/typescript' 13 | ], 14 | rules: { 15 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 17 | 'space-before-function-paren': 0, 18 | 'vue/array-bracket-spacing': 0, 19 | 'vue/arrow-spacing': 0, 20 | 'vue/block-spacing': 0, 21 | 'vue/brace-style': 'error', 22 | 'vue/camelcase': 'error', 23 | 'vue/comma-dangle': 'error', 24 | 'vue/component-name-in-template-casing': 'error', 25 | 'vue/eqeqeq': 'error', 26 | 'vue/key-spacing': 0, 27 | 'vue/match-component-file-name': 'error', 28 | 'vue/object-curly-spacing': 0, 29 | 'vue/max-attributes-per-line': 0, 30 | 'padded-blocks': 0, 31 | 'semi': 0, 32 | 'indent': 0, 33 | 'space-infix-ops': 0, 34 | 'space-before-blocks': 0, 35 | 'eqeqeq': 0, 36 | 'vue/eqeqeq': 0, 37 | 'object-curly-spacing': 0, 38 | 'keyword-spacing': 0, 39 | 'spaced-comment': 0, 40 | 'key-spacing': 0, 41 | 'comma-spacing': 0, 42 | 'comma-dangle': 0, 43 | 'space-in-parens': 0, 44 | 'standard/object-curly-even-spacing': 0, 45 | }, 46 | parserOptions: { 47 | parser: '@typescript-eslint/parser' 48 | }, 49 | overrides: [ 50 | { 51 | files: [ 52 | '**/__tests__/*.{j,t}s?(x)', 53 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 54 | ], 55 | env: { 56 | jest: true 57 | } 58 | } 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /tests/e2e/videos/ 6 | /tests/e2e/screenshots/ 7 | /tests/**/coverage/ 8 | 9 | # local env files 10 | .env.local 11 | .env.*.local 12 | 13 | # Log files 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | 18 | # Editor directories and files 19 | .idea 20 | .vscode 21 | .history 22 | *.suo 23 | *.ntvs* 24 | *.njsproj 25 | *.sln 26 | *.sw* 27 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | ####提示 2 | #1.以下脚本设置的是master分支被合并时执行(设置为平常用来发版本的分支即可) 3 | #2.版本自动化部署到nginx下 4 | #3.执行yml脚本的runner名字设置的vue-runner1,即对应tags 5 | 6 | stages: # Stages 表示构建阶段,这里有两个阶段 install, deploy 7 | - install 8 | - deploy 9 | 10 | cache: 11 | key: ${CI_BUILD_REF_NAME} 12 | paths: 13 | - node_modules/ 14 | 15 | install-staging:dep: # Jobs 表示构建工作,表示某个 Stage 里面执行的工作。 16 | stage: install 17 | tags: 18 | - reggie-vue-runner #与注册runner时填写的tags保持一致 19 | only: # 定义了只有在被merge到了master分支上 才会执行部署脚本。 20 | - master 21 | script: 22 | - echo "=====start install======" 23 | - npm install --registry=https://registry.npm.taobao.org #安装依赖 24 | - echo "=====end install======" 25 | artifacts: # 将这个job生成的依赖传递给下一个job。需要设置dependencies 26 | expire_in: 60 mins # artifacets 的过期时间,因为这些数据都是直接保存在 Gitlab 机器上的,过于久远的资源就可以删除掉了 27 | paths: # 需要被传递给下一个job的目录。 28 | - node_modules/ 29 | 30 | deploy-staging:dep: 31 | stage: deploy 32 | tags: 33 | - reggie-vue-runner 34 | only: 35 | - master 36 | script: 37 | - echo "=====start build======" 38 | - npm run build:uat # 将项目打包 测试环境用npm run build:uat ---有删除权限 线上还是用npm run build ---没有删除权限 39 | - echo "=====start deploy======" 40 | - sudo cp -rf ./dist/ /usr/share/nginx/html/reggie 41 | - echo "=====end deploy!!!!!!======" 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "singleQuote": true, 4 | "semi": false 5 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx 2 | 3 | COPY ./dist /usr/share/nginx/html 4 | RUN chmod -R 777 /usr/share/nginx/html 5 | 6 | CMD ["nginx", "-g", "daemon off;"] 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Chong Guo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-typescript-admin-template 2 | 3 | ## 环境配置 4 | 5 | ``` 6 | node v12.22.12 7 | npm v6.14.16 8 | ``` 9 | 10 | [nvm 安装 nodejs v12.22.12 如何手动安装 npm ](https://blog.csdn.net/qq_39293316/article/details/132413050) 11 | 12 | ## 项目启动 13 | 14 | **依赖安装** 15 | 16 | ``` 17 | npm install yarn -g 18 | yarn config set registry https://registry.npmmirror.com 19 | yarn install 20 | ``` 21 | 22 | **启动** 23 | 24 | ``` 25 | yarn serve 26 | ``` 27 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'presets': [ 3 | '@vue/app' 4 | ] 5 | }; 6 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "pluginsFile": "tests/e2e/plugins/index.js" 3 | } 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'moduleFileExtensions': [ 3 | 'js', 4 | 'jsx', 5 | 'json', 6 | 'vue', 7 | 'ts', 8 | 'tsx' 9 | ], 10 | 'transform': { 11 | '^.+\\.vue$': 'vue-jest', 12 | '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 13 | '^.+\\.tsx?$': 'ts-jest' 14 | }, 15 | 'transformIgnorePatterns': [ 16 | '/node_modules/' 17 | ], 18 | 'moduleNameMapper': { 19 | '^@/(.*)$': '/src/$1' 20 | }, 21 | 'snapshotSerializers': [ 22 | 'jest-serializer-vue' 23 | ], 24 | 'testMatch': [ 25 | '**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)' 26 | ], 27 | 'collectCoverage': true, 28 | 'collectCoverageFrom': [ 29 | 'src/utils/**/*.{ts,vue}', 30 | '!src/utils/auth.ts', 31 | '!src/utils/request.ts', 32 | 'src/components/**/*.{ts,vue}' 33 | ], 34 | 'coverageDirectory': '/tests/unit/coverage', 35 | 'coverageReporters': [ 36 | 'lcov', 37 | 'text-summary' 38 | ], 39 | 'testURL': 'http://localhost/', 40 | 'watchPlugins': [ 41 | 'jest-watch-typeahead/filename', 42 | 'jest-watch-typeahead/testname' 43 | ], 44 | 'globals': { 45 | 'ts-jest': { 46 | 'babelConfig': true 47 | } 48 | } 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-typescript-admin-template", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Chong Guo ", 6 | "scripts": { 7 | "serve": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "build:uat": "vue-cli-service build --mode production.uat", 10 | "lint": "vue-cli-service lint", 11 | "svg": "vsvg -s ./src/icons/svg -t ./src/icons/components --ext ts --es6", 12 | "test:e2e": "vue-cli-service test:e2e", 13 | "test:unit": "vue-cli-service test:unit" 14 | }, 15 | "dependencies": { 16 | "@types/echarts": "^4.4.6", 17 | "@types/webpack": "^4.41.12", 18 | "area-data": "^5.0.6", 19 | "axios": "^0.19.0", 20 | "echarts": "^5.3.2", 21 | "element-ui": "^2.12.0", 22 | "js-cookie": "^2.2.1", 23 | "md5": "^2.3.0", 24 | "moment": "^2.24.0", 25 | "normalize.css": "^8.0.1", 26 | "nprogress": "^0.2.0", 27 | "path-to-regexp": "^3.0.0", 28 | "register-service-worker": "^1.6.2", 29 | "vue": "^2.6.10", 30 | "vue-area-linkage": "^5.1.0", 31 | "vue-class-component": "^7.1.0", 32 | "vue-property-decorator": "^8.2.2", 33 | "vue-router": "^3.1.2", 34 | "vue-svgicon": "^3.2.6", 35 | "vuex": "^3.1.1", 36 | "vuex-class": "^0.3.2", 37 | "vuex-module-decorators": "^0.10.1", 38 | "vuex-persistedstate": "^2.7.0" 39 | }, 40 | "devDependencies": { 41 | "@types/jest": "^24.0.18", 42 | "@types/js-cookie": "^2.2.2", 43 | "@types/nprogress": "^0.2.0", 44 | "@types/webpack-env": "^1.14.0", 45 | "@vue/cli-plugin-babel": "^3.11.0", 46 | "@vue/cli-plugin-e2e-cypress": "^3.11.0", 47 | "@vue/cli-plugin-eslint": "^3.11.0", 48 | "@vue/cli-plugin-pwa": "^3.11.0", 49 | "@vue/cli-plugin-typescript": "^3.11.0", 50 | "@vue/cli-plugin-unit-jest": "^3.11.0", 51 | "@vue/cli-service": "^3.11.0", 52 | "@vue/eslint-config-standard": "^4.0.0", 53 | "@vue/eslint-config-typescript": "^4.0.0", 54 | "@vue/test-utils": "^1.0.0-beta.29", 55 | "babel-core": "^7.0.0-bridge.0", 56 | "babel-eslint": "^10.0.3", 57 | "eslint": "^6.2.2", 58 | "eslint-plugin-vue": "^5.2.3", 59 | "fibers": "^4.0.2", 60 | "jest": "^24.9.0", 61 | "sass": "^1.22.10", 62 | "sass-loader": "^7.3.1", 63 | "style-resources-loader": "^1.2.1", 64 | "ts-jest": "^24.0.2", 65 | "typescript": "3.6.2", 66 | "vue-cli-plugin-element": "^1.0.1", 67 | "vue-cli-plugin-style-resources-loader": "^0.1.3", 68 | "vue-template-compiler": "^2.6.10", 69 | "webpack": "^4.39.3" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'plugins': { 3 | 'autoprefixer': {} 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 苍穹外卖 10 | 11 | 12 | 18 |
19 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "苍穹外卖", 3 | "short_name": "Vue Ts Admin", 4 | "icons": [ 5 | { 6 | "src": "./img/icons/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "./img/icons/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "start_url": "./index.html", 17 | "display": "standalone", 18 | "background_color": "#fff", 19 | "theme_color": "#4DBA87" 20 | } 21 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /src/api/category.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request'; 2 | /** 3 | * 4 | * 分类管理 5 | * 6 | **/ 7 | 8 | // 查询分类列表接口 9 | export const getCategoryPage = (params: any) => { 10 | return request({ 11 | url: '/category/page', 12 | method: 'get', 13 | params 14 | }); 15 | }; 16 | 17 | // 删除当前列的接口 18 | export const deleCategory = (ids: string) => { 19 | return request({ 20 | url: '/category', 21 | method: 'delete', 22 | params: { id:ids } 23 | }); 24 | }; 25 | 26 | // 修改接口 27 | export const editCategory = (params: any) => { 28 | return request({ 29 | url: '/category', 30 | method: 'put', 31 | data: { ...params } 32 | }); 33 | }; 34 | 35 | // 新增接口 36 | export const addCategory = (params: any) => { 37 | return request({ 38 | url: '/category', 39 | method: 'post', 40 | data: { ...params } 41 | }); 42 | }; 43 | 44 | // 修改---启用禁用接口 45 | export const enableOrDisableEmployee = (params: any) => { 46 | return request({ 47 | url: `/category/status/${params.status}`, 48 | method: 'post', 49 | params: { id:params.id } 50 | }) 51 | } 52 | -------------------------------------------------------------------------------- /src/api/charts.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | /** 3 | * 4 | * 报表数据 5 | * 6 | **/ 7 | 8 | // 获取当日销售数据 -> 顶部数据 9 | export const getDataes = (params: any) => 10 | request({ 11 | 'url': `/report/amountCollect/${params.date}`, 12 | 'method': 'get' 13 | }) 14 | 15 | // 获取当日销售数据 -> 顶部数据 - 营收概况 16 | export const getChartsDataes = (params: any) => 17 | request({ 18 | 'url': `/report/dayCollect/${params.start}/${params.end}`, 19 | 'method': 'get' 20 | }) 21 | 22 | // 获取当日销售趋势数据(24小时)-> 销售趋势 23 | export const getDayDataes= (params: any) => 24 | request({ 25 | 'url': `/report/hourCollect/${params.type}/${params.date}`, 26 | 'method': 'get' 27 | }) 28 | 29 | // 支付类型数据汇总 -> 店内收款构成 - 当日 30 | export const getDayPayType = (params: any) => 31 | request({ 32 | 'url': `/report/payTypeCollect/${params.date}`, 33 | 'method': 'get' 34 | }) 35 | // 获取当日各种优惠类型数据汇总 -> 优惠指标 36 | export const getprivilege = (params: any) => 37 | request({ 38 | 'url': `/report/privilegeCollect/${params.date}`, 39 | 'method': 'get' 40 | }) 41 | 42 | // 获取菜品分类销售排行 - 菜品分类占比 -当日 43 | export const getSalesRanking = (params: any) => 44 | request({ 45 | 'url': `/report/categoryCollect/${params.type}/${params.date}`, 46 | 'method': 'get' 47 | }) 48 | 49 | // 获取当日菜品销售排行 50 | export const getDayRanking = (params: any) => 51 | request({ 52 | 'url': `/report/currentDishRank/${params.date}`, 53 | 'method': 'get' 54 | }) 55 | 56 | // 获取一定日期之内的销售趋势 - 销售趋势 图 57 | export const getTimeQuantumDataes = (params: any) => 58 | request({ 59 | 'url': `/report/dayAmountCollect/${params.type}/${params.start}/${params.end}`, 60 | 'method': 'get' 61 | }) 62 | 63 | // 获取时间范围之内的各种支付类型数据汇总 - 店内收款构成 - 时间段 64 | export const getTimeQuantumReceivables = (params: any) => 65 | request({ 66 | 'url': `/report/datePayTypeCollect/${params.start}/${params.end}`, 67 | 'method': 'get' 68 | }) 69 | 70 | // 获取时间范围之内的菜品类别销售汇总 - 菜品分类占比 - 时间段 71 | export const getTimeQuantumType = (params: any) => 72 | request({ 73 | 'url': `/report/dateCategoryCollect/${params.type}/${params.start}/${params.end}`, 74 | 'method': 'get' 75 | }) 76 | 77 | // 获取时间范围之内的菜品销售排行 - 菜品销售排行 78 | export const getTimeQuantumDishes = (params: any) => 79 | request({ 80 | 'url': `/report/dishRankForDate/${params.start}/${params.end}`, 81 | 'method': 'get' 82 | }) 83 | 84 | // 获取时间范围之内的优惠指标汇总数据 - 顶部信息 85 | export const getTimeQuantumDiscount = (params: any) => 86 | request({ 87 | 'url': `/report/privilegeByDate/${params.start}/${params.end}`, 88 | 'method': 'get' 89 | }) 90 | -------------------------------------------------------------------------------- /src/api/dish.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | /** 3 | * 4 | * 菜品管理 5 | * 6 | **/ 7 | // 查询列表接口 8 | export const getDishPage = (params: any) => { 9 | return request({ 10 | url: '/dish/page', 11 | method: 'get', 12 | params 13 | }) 14 | } 15 | 16 | // 删除接口 17 | export const deleteDish = (ids: string) => { 18 | return request({ 19 | url: '/dish', 20 | method: 'delete', 21 | params: { ids } 22 | }) 23 | } 24 | 25 | // 修改接口 26 | export const editDish = (params: any) => { 27 | return request({ 28 | url: '/dish', 29 | method: 'put', 30 | data: { ...params } 31 | }) 32 | } 33 | 34 | // 新增接口 35 | export const addDish = (params: any) => { 36 | return request({ 37 | url: '/dish', 38 | method: 'post', 39 | data: { ...params } 40 | }) 41 | } 42 | 43 | // 查询详情 44 | export const queryDishById = (id: string | (string | null)[]) => { 45 | return request({ 46 | url: `/dish/${id}`, 47 | method: 'get' 48 | }) 49 | } 50 | 51 | // 获取菜品分类列表 52 | export const getCategoryList = (params: any) => { 53 | return request({ 54 | url: '/category/list', 55 | method: 'get', 56 | params 57 | }) 58 | } 59 | 60 | // 查菜品列表的接口 61 | export const queryDishList = (params: any) => { 62 | return request({ 63 | url: '/dish/list', 64 | method: 'get', 65 | params 66 | }) 67 | } 68 | 69 | // 文件down预览 70 | export const commonDownload = (params: any) => { 71 | return request({ 72 | headers: { 73 | 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' 74 | }, 75 | url: '/common/download', 76 | method: 'get', 77 | params 78 | }) 79 | } 80 | 81 | // 起售停售---批量起售停售接口 82 | export const dishStatusByStatus = (params: any) => { 83 | return request({ 84 | url: `/dish/status/${params.status}`, 85 | method: 'post', 86 | params: { id: params.id } 87 | }) 88 | } 89 | 90 | //菜品分类数据查询 91 | export const dishCategoryList = (params: any) => { 92 | return request({ 93 | url: `/category/list`, 94 | method: 'get', 95 | params: { ...params } 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /src/api/employee.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | /** 3 | * 4 | * 员工管理 5 | * 6 | **/ 7 | // 登录、 8 | export const login = (data: any) => 9 | request({ 10 | 'url': '/employee/login', 11 | 'method': 'post', 12 | data 13 | }) 14 | // 退出 15 | export const userLogout = (params: any) => 16 | request({ 17 | 'url': `/employee/logout`, // 授课老师接口 18 | 'method': 'post', 19 | params 20 | }) 21 | 22 | export const getEmployeeList = (params: any) => { 23 | return request({ 24 | url: '/employee/page', 25 | method: 'get', 26 | params 27 | }) 28 | } 29 | 30 | // 修改---启用禁用接口 31 | export const enableOrDisableEmployee = (params: any) => { 32 | return request({ 33 | url: `/employee/status/${params.status}`, 34 | method: 'post', 35 | params: { id:params.id } 36 | }) 37 | } 38 | 39 | // 新增---添加员工 40 | export const addEmployee = (params: any) => { 41 | return request({ 42 | url: '/employee', 43 | method: 'post', 44 | data: { ...params } 45 | }) 46 | } 47 | 48 | // 修改---添加员工 49 | export const editEmployee = (params: any) => { 50 | return request({ 51 | url: '/employee', 52 | method: 'put', 53 | data: { ...params } 54 | }) 55 | } 56 | 57 | // 修改页面反查详情接口 58 | export const queryEmployeeById = (id: string | (string | null)[]) => { 59 | return request({ 60 | url: `/employee/${id}`, 61 | method: 'get' 62 | }) 63 | } 64 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | // 营业额数据 3 | // export const getTurnoverDataes = (data) => 4 | // request({ 5 | // 'url': `/report/turnoverStatistics`, 6 | // 'method': 'get', 7 | // data 8 | // }) 9 | // 首页数据 10 | // // 今日数据 11 | // export const getTodayDataes = () => 12 | // request({ 13 | // 'url': `/workspace/todaydate`, 14 | // 'method': 'get' 15 | // }) 16 | // 订单管理 17 | export const getOrderData = () => 18 | request({ 19 | 'url': `/workspace/overviewOrders`, 20 | 'method': 'get' 21 | }) 22 | // 菜品总览 23 | export const getOverviewDishes = () => 24 | request({ 25 | 'url': `/workspace/overviewDishes`, 26 | 'method': 'get' 27 | }) 28 | // 套餐总览 29 | export const getSetMealStatistics = () => 30 | request({ 31 | 'url': `/workspace/overviewSetmeals`, 32 | 'method': 'get' 33 | }) 34 | // 营业数据 35 | export const getBusinessData= () => 36 | request({ 37 | 'url': `/workspace/businessData`, 38 | 'method': 'get' 39 | }) 40 | /** 41 | * 42 | * 报表数据 43 | * 44 | **/ 45 | // 统计 46 | // 获取当日销售数据 -> 顶部数据 47 | // export const getDataes = (params: any) => 48 | // request({ 49 | // 'url': `/report/amountCollect/${params.date}`, 50 | // 'method': 'get' 51 | // }) 52 | 53 | 54 | // 营业额统计 55 | export const getTurnoverStatistics= (params: any) => 56 | request({ 57 | 'url': `/report/turnoverStatistics`, 58 | 'method': 'get', 59 | params 60 | }) 61 | 62 | // 用户统计 63 | export const getUserStatistics= (params: any) => 64 | request({ 65 | 'url': `/report/userStatistics`, 66 | 'method': 'get', 67 | params 68 | }) 69 | // 订单统计 70 | export const getOrderStatistics= (params: any) => 71 | request({ 72 | 'url': `/report/ordersStatistics`, 73 | 'method': 'get', 74 | params 75 | }) 76 | // 销量排名TOP10 77 | export const getTop= (params: any) => 78 | request({ 79 | 'url': `/report/top10`, 80 | 'method': 'get', 81 | params 82 | }) 83 | // 数据概览 84 | export const getDataOverView= (params: any) => 85 | request({ 86 | 'url': `/report/dataOverView`, 87 | 'method': 'get', 88 | params 89 | }) 90 | // 导出 91 | export function exportInfor() { 92 | return request({ 93 | url: '/report/export', 94 | method: 'get', 95 | responseType: "blob" 96 | }) 97 | } 98 | -------------------------------------------------------------------------------- /src/api/inform.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | // 获取列表数据 3 | export const getInformData = (params: any) => { 4 | return request({ 5 | url: '/messages/page', 6 | method: 'get', 7 | params, 8 | },) 9 | } 10 | // 获取未读 11 | export const getCountUnread = () => { 12 | return request({ 13 | url: '/messages/countUnread', 14 | method: 'get' 15 | },) 16 | } 17 | // 全部已读 18 | export const batchMsg = (data: any) => { 19 | return request({ 20 | url: '/messages/batch', 21 | method: 'put', 22 | data 23 | }) 24 | } 25 | // 标记已读 26 | export const setStatus = (params: any) => { 27 | return request({ 28 | url: `/messages/${params}`, 29 | method: 'PUT' 30 | }) 31 | } -------------------------------------------------------------------------------- /src/api/order.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 查询列表页接口 4 | export const getOrderDetailPage = (params: any) => { 5 | return request({ 6 | url: '/order/conditionSearch', 7 | method: 'get', 8 | params 9 | }) 10 | } 11 | 12 | // 查看接口 13 | export const queryOrderDetailById = (params: any) => { 14 | return request({ 15 | url: `/order/details/${params.orderId}`, 16 | method: 'get' 17 | }) 18 | } 19 | 20 | // 派送接口 21 | export const deliveryOrder = (params: any) => { 22 | return request({ 23 | url: `/order/delivery/${params.id}`, 24 | method: 'put' /* */ 25 | }) 26 | } 27 | //完成接口 28 | export const completeOrder = (params: any) => { 29 | return request({ 30 | url: `/order/complete/${params.id}`, 31 | method: 'put' /* */ 32 | }) 33 | } 34 | 35 | //订单取消 36 | export const orderCancel = (params: any) => { 37 | return request({ 38 | url: '/order/cancel', 39 | method: 'put' /* */, 40 | data: { ...params } 41 | }) 42 | } 43 | 44 | //接单 45 | export const orderAccept = (params: any) => { 46 | return request({ 47 | url: '/order/confirm', 48 | method: 'put' /* */, 49 | data: { ...params } 50 | }) 51 | } 52 | 53 | //拒单 54 | export const orderReject = (params: any) => { 55 | return request({ 56 | url: '/order/rejection', 57 | method: 'put' /* */, 58 | data: { ...params } 59 | }) 60 | } 61 | 62 | //获取待处理,待派送,派送中数量 63 | export const getOrderListBy = (params: any) => { 64 | return request({ 65 | url: '/order/statistics', 66 | method: 'get' /* */ 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /src/api/setMeal.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | /** 3 | * 4 | * 套餐管理 5 | * 6 | **/ 7 | 8 | // 查询列表数据 9 | export const getSetmealPage = (params: any) => { 10 | return request({ 11 | url: '/setmeal/page', 12 | method: 'get', 13 | params, 14 | },) 15 | } 16 | 17 | // 删除数据接口 18 | export const deleteSetmeal = (ids: string) => { 19 | return request({ 20 | url: '/setmeal', 21 | method: 'delete', 22 | params: { ids } 23 | }) 24 | } 25 | 26 | // 修改数据接口 27 | export const editSetmeal = (params: any) => { 28 | return request({ 29 | url: '/setmeal', 30 | method: 'put', 31 | data: { ...params } 32 | }) 33 | } 34 | 35 | // 新增数据接口 36 | export const addSetmeal = (params: any) => { 37 | return request({ 38 | url: '/setmeal', 39 | method: 'post', 40 | data: { ...params } 41 | }) 42 | } 43 | 44 | // 查询详情接口 45 | export const querySetmealById = (id: string | (string | null)[]) => { 46 | return request({ 47 | url: `/setmeal/${id}`, 48 | method: 'get' 49 | }) 50 | } 51 | 52 | // 批量起售禁售 53 | export const setmealStatusByStatus = (params: any) => { 54 | return request({ 55 | url: `/setmeal/status/${params.status}`, 56 | method: 'post', 57 | params: { id: params.ids } 58 | }) 59 | } 60 | 61 | //菜品分类数据查询 62 | export const dishCategoryList = (params: any) => { 63 | return request({ 64 | url: `/category/list`, 65 | method: 'get', 66 | params: { ...params } 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /src/api/users.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | // 修改密码 3 | export const editPassword = (data: any) => 4 | request({ 5 | 'url': '/employee/editPassword', 6 | 'method': 'put', 7 | data 8 | }) 9 | // 获取营业状态 10 | export const getStatus = () => 11 | request({ 12 | 'url': `/shop/status`, 13 | 'method': 'get' 14 | }) 15 | // 设置营业状态 16 | export const setStatus = (data:any) => 17 | request({ 18 | 'url': `/shop/`+data, 19 | 'method': 'put', 20 | 'data':data 21 | }) -------------------------------------------------------------------------------- /src/assets/404-images/404-cloud.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/404-images/404-cloud.png -------------------------------------------------------------------------------- /src/assets/404-images/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/404-images/404.png -------------------------------------------------------------------------------- /src/assets/icons/addicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/addicon.png -------------------------------------------------------------------------------- /src/assets/icons/btn_add@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/btn_add@2x.png -------------------------------------------------------------------------------- /src/assets/icons/btn_back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/btn_back@2x.png -------------------------------------------------------------------------------- /src/assets/icons/btn_clean@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/btn_clean@2x.png -------------------------------------------------------------------------------- /src/assets/icons/btn_close@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/btn_close@2x.png -------------------------------------------------------------------------------- /src/assets/icons/btn_close@2x1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/btn_close@2x1.png -------------------------------------------------------------------------------- /src/assets/icons/btn_gaimi@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/btn_gaimi@2x.png -------------------------------------------------------------------------------- /src/assets/icons/fall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/fall.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_daijiedan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_daijiedan.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_daipaisong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_daipaisong.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_quanbu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_quanbu.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_quxiao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_quxiao.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_wancheng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_wancheng.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_yiqishou@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_yiqishou@2x.png -------------------------------------------------------------------------------- /src/assets/icons/gzt_yitingshou@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/gzt_yitingshou@2x.png -------------------------------------------------------------------------------- /src/assets/icons/icon_index.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/icon_index.png -------------------------------------------------------------------------------- /src/assets/icons/icon_more@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/icon_more@2x.png -------------------------------------------------------------------------------- /src/assets/icons/icon_upload@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/icon_upload@2x.png -------------------------------------------------------------------------------- /src/assets/icons/jine_m-2@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/jine_m-2@2x.png -------------------------------------------------------------------------------- /src/assets/icons/msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/msg.png -------------------------------------------------------------------------------- /src/assets/icons/renshu@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/renshu@2x.png -------------------------------------------------------------------------------- /src/assets/icons/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/time.png -------------------------------------------------------------------------------- /src/assets/icons/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/up.png -------------------------------------------------------------------------------- /src/assets/icons/xiangmujine@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/icons/xiangmujine@2x.png -------------------------------------------------------------------------------- /src/assets/img_denglu_bj.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/img_denglu_bj.jpg -------------------------------------------------------------------------------- /src/assets/login/icon_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/login/icon_logo.png -------------------------------------------------------------------------------- /src/assets/login/login-l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/login/login-l.png -------------------------------------------------------------------------------- /src/assets/login/login-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/login/login-logo.png -------------------------------------------------------------------------------- /src/assets/login/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/login/logo.png -------------------------------------------------------------------------------- /src/assets/login/mini-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/login/mini-logo.png -------------------------------------------------------------------------------- /src/assets/login/white_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/login/white_logo.png -------------------------------------------------------------------------------- /src/assets/noImg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/noImg.png -------------------------------------------------------------------------------- /src/assets/preview.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/preview.mp3 -------------------------------------------------------------------------------- /src/assets/reminder.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/reminder.mp3 -------------------------------------------------------------------------------- /src/assets/search_table_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/search_table_empty.png -------------------------------------------------------------------------------- /src/assets/table_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/assets/table_empty.png -------------------------------------------------------------------------------- /src/components/Breadcrumb/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 86 | 87 | 105 | -------------------------------------------------------------------------------- /src/components/Charts/BarChart.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 95 | -------------------------------------------------------------------------------- /src/components/Charts/Basic.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 155 | -------------------------------------------------------------------------------- /src/components/Charts/Line.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 136 | 137 | 140 | -------------------------------------------------------------------------------- /src/components/Charts/MixedChart.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 153 | -------------------------------------------------------------------------------- /src/components/Charts/mixins/resize.ts: -------------------------------------------------------------------------------- 1 | import { ECharts } from 'echarts'; 2 | import { Component, Vue } from 'vue-property-decorator'; 3 | 4 | @Component({ 5 | 'name': 'ResizeMixin' 6 | }) 7 | export default class extends Vue { 8 | protected chart!: ECharts | null 9 | private sidebarElm?: Element 10 | 11 | mounted() { 12 | this.initResizeEvent(); 13 | this.initSidebarResizeEvent(); 14 | } 15 | 16 | beforeDestroy() { 17 | this.destroyResizeEvent(); 18 | this.destroySidebarResizeEvent(); 19 | } 20 | 21 | activated() { 22 | this.initResizeEvent(); 23 | this.initSidebarResizeEvent(); 24 | } 25 | 26 | deactivated() { 27 | this.destroyResizeEvent(); 28 | this.destroySidebarResizeEvent(); 29 | } 30 | 31 | private chartResizeHandler() { 32 | if (this.chart) { 33 | this.chart.resize(); 34 | } 35 | } 36 | 37 | private sidebarResizeHandler(e: TransitionEvent) { 38 | if (e.propertyName === 'width') { 39 | this.chartResizeHandler(); 40 | } 41 | } 42 | 43 | private initResizeEvent() { 44 | if (this.chartResizeHandler) { 45 | window.addEventListener('resize', this.chartResizeHandler); 46 | } 47 | } 48 | 49 | private destroyResizeEvent() { 50 | if (this.chartResizeHandler) { 51 | window.removeEventListener('resize', this.chartResizeHandler); 52 | } 53 | } 54 | 55 | private initSidebarResizeEvent() { 56 | this.sidebarElm = document.getElementsByClassName('sidebar-container')[0]; 57 | if (this.sidebarElm) { 58 | this.sidebarElm.addEventListener('transitionend', this.sidebarResizeHandler as EventListener); 59 | } 60 | } 61 | 62 | private destroySidebarResizeEvent() { 63 | if (this.sidebarElm) { 64 | this.sidebarElm.removeEventListener('transitionend', this.sidebarResizeHandler as EventListener); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/components/Empty/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 24 | 41 | -------------------------------------------------------------------------------- /src/components/Hamburger/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 22 | 23 | 32 | -------------------------------------------------------------------------------- /src/components/HeadLable/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 38 | 39 | 84 | -------------------------------------------------------------------------------- /src/components/ImgUpload/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 35 | 36 | 92 | 99 | 200 | -------------------------------------------------------------------------------- /src/components/InputAutoComplete/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 38 | 43 | -------------------------------------------------------------------------------- /src/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "/api" 3 | } -------------------------------------------------------------------------------- /src/icons/README.md: -------------------------------------------------------------------------------- 1 | # vue-svgicon 2 | 3 | ## English 4 | 5 | * All svg components were generated by `vue-svgicon` using svg files 6 | * After you adding new svg files into `icons/svg` folder, run `yarn svg` to regerenrate all svg components (before this, you should have `vue-svgicon` installed globally or use `npx`) 7 | * See details at: [https://github.com/MMF-FE/vue-svgicon](https://github.com/MMF-FE/vue-svgicon) 8 | 9 | ## 中文 10 | 11 | * 所有的 svg 组件都是由 `vue-svgicon` 生成的 12 | * 每当在 `icons/svg` 文件夹内添加 icon 之后,可以通过执行 `yarn svg` 来重新生成所有组件 (在此之前需要全局安装 `vue-svgicon` 或使用 `npx`) 13 | * 详细文档请见:[https://github.com/MMF-FE/vue-svgicon](https://github.com/MMF-FE/vue-svgicon) 14 | -------------------------------------------------------------------------------- /src/icons/components/dashboard.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'dashboard': { 7 | width: 32, 8 | height: 32, 9 | viewBox: '0 0 1024 1024', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/employee.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'employee': { 7 | width: 62, 8 | height: 62, 9 | viewBox: '0 0 62 62', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/hamburger.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'hamburger': { 7 | width: 64, 8 | height: 64, 9 | viewBox: '0 0 1024 1024', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/index.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | import './main' 3 | import './employee' 4 | import './pay' 5 | import './shop' 6 | import './vip' 7 | import './hamburger' 8 | import './dashboard' 9 | import './inform' 10 | -------------------------------------------------------------------------------- /src/icons/components/inform.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'dashboard': { 7 | width: 32, 8 | height: 32, 9 | viewBox: '0 0 1024 1024', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/main.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'main': { 7 | width: 128, 8 | height: 128, 9 | viewBox: '0 0 128 128', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/pay.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'pay': { 7 | width: 62, 8 | height: 62, 9 | viewBox: '0 0 62 62', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/shop.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'shop': { 7 | width: 62, 8 | height: 62, 9 | viewBox: '0 0 62 62', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/components/vip.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* tslint:disable */ 3 | // @ts-ignore 4 | import icon from 'vue-svgicon' 5 | icon.register({ 6 | 'vip': { 7 | width: 62, 8 | height: 62, 9 | viewBox: '0 0 62 62', 10 | data: '' 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /src/icons/svg/dashboard.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/hamburger.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/icons/svg/inform.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/main.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 21 | 22 | -------------------------------------------------------------------------------- /src/icons/svg/member.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 27 | 28 | -------------------------------------------------------------------------------- /src/icons/svg/pay.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 37 | 38 | -------------------------------------------------------------------------------- /src/icons/svg/shop.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 28 | 29 | -------------------------------------------------------------------------------- /src/icons/svg/vip.svg: -------------------------------------------------------------------------------- 1 |  2 | 3 | 29 | 30 | -------------------------------------------------------------------------------- /src/layout/components/AppMain.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 27 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 124 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/SidebarItemLink.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | -------------------------------------------------------------------------------- /src/layout/components/Sidebar/index.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 134 | 135 | 163 | -------------------------------------------------------------------------------- /src/layout/components/components/password.vue: -------------------------------------------------------------------------------- 1 | 38 | 95 | -------------------------------------------------------------------------------- /src/layout/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as AppMain } from './AppMain.vue' 2 | export { default as Navbar } from './Navbar/index.vue' 3 | export { default as Sidebar } from './Sidebar/index.vue' 4 | -------------------------------------------------------------------------------- /src/layout/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 46 | 47 | 139 | -------------------------------------------------------------------------------- /src/layout/mixin/resize.ts: -------------------------------------------------------------------------------- 1 | import { Component, Vue, Watch } from 'vue-property-decorator' 2 | import { AppModule, DeviceType } from '@/store/modules/app' 3 | 4 | const WIDTH = 992; // refer to Bootstrap's responsive design 5 | 6 | @Component({ 7 | 'name': 'ResizeMixin' 8 | }) 9 | export default class extends Vue { 10 | get device () { 11 | return AppModule.device 12 | } 13 | 14 | get sidebar () { 15 | return AppModule.sidebar 16 | } 17 | 18 | @Watch('$route') 19 | private onRouteChange() { 20 | if (this.device === DeviceType.Mobile && this.sidebar.opened) { 21 | AppModule.CloseSideBar(false) 22 | } 23 | } 24 | 25 | beforeMount() { 26 | window.addEventListener('resize', this.resizeHandler) 27 | } 28 | 29 | mounted() { 30 | const isMobile = this.isMobile() 31 | if (isMobile) { 32 | AppModule.ToggleDevice(DeviceType.Mobile) 33 | AppModule.CloseSideBar(true) 34 | } 35 | } 36 | 37 | beforeDestroy() { 38 | window.removeEventListener('resize', this.resizeHandler) 39 | } 40 | 41 | private isMobile() { 42 | const rect = document.body.getBoundingClientRect() 43 | return rect.width - 1 < WIDTH 44 | } 45 | 46 | private resizeHandler() { 47 | if (!document.hidden) { 48 | const isMobile = this.isMobile() 49 | AppModule.ToggleDevice(isMobile ? DeviceType.Mobile : DeviceType.Desktop) 50 | if (isMobile) { 51 | AppModule.CloseSideBar(true) 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import 'normalize.css' 4 | import ElementUI from 'element-ui' 5 | import SvgIcon from 'vue-svgicon' 6 | import VueAreaLinkage from 'vue-area-linkage' 7 | import moment from 'moment' 8 | import '@/styles/element-variables.scss' 9 | import '@/styles/index.scss' 10 | import '@/styles/home.scss' 11 | import 'vue-area-linkage/dist/index.css' 12 | 13 | import * as echarts from 'echarts' 14 | // 瑞吉外卖样式表 15 | import '@/styles/newRJWMsystem.scss' 16 | import '@/styles/icon/iconfont.css' 17 | import App from '@/App.vue' 18 | import store from '@/store' 19 | import router from '@/router' 20 | import '@/icons/components' 21 | import '@/permission' 22 | import { checkProcessEnv } from '@/utils/common' 23 | 24 | Vue.use(ElementUI) 25 | Vue.use(VueAreaLinkage) 26 | Vue.use(SvgIcon, { 27 | 'tagName': 'svg-icon', 28 | 'defaultWidth': '1em', 29 | 'defaultHeight': '1em' 30 | }) 31 | 32 | Vue.config.productionTip = false 33 | Vue.prototype.moment = moment 34 | Vue.prototype.$checkProcessEnv = checkProcessEnv 35 | const routerPush = Router.prototype.push 36 | Router.prototype.push = function push(location) { 37 | return routerPush.call(this, location).catch(error=> error) 38 | } 39 | Vue.prototype.$echarts = echarts 40 | new Vue({ 41 | router, 42 | store, 43 | 'render': (h) => h(App) 44 | }).$mount('#app') 45 | -------------------------------------------------------------------------------- /src/permission.ts: -------------------------------------------------------------------------------- 1 | import router from './router' 2 | import NProgress from 'nprogress' 3 | import 'nprogress/nprogress.css' 4 | import { Message } from 'element-ui' 5 | import { Route } from 'vue-router' 6 | import { UserModule } from '@/store/modules/user' 7 | import Cookies from 'js-cookie' 8 | 9 | NProgress.configure({ 'showSpinner': false }) 10 | 11 | router.beforeEach(async (to: Route, _: Route, next: any) => { 12 | NProgress.start() 13 | if (Cookies.get('token')) { 14 | next() 15 | } else { 16 | if (!to.meta.notNeedAuth) { 17 | next('/login') 18 | } else { 19 | next() 20 | } 21 | } 22 | }) 23 | 24 | router.afterEach((to: Route) => { 25 | NProgress.done() 26 | document.title = to.meta.title 27 | }) 28 | -------------------------------------------------------------------------------- /src/registerServiceWorker.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker'; 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready() { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ); 12 | }, 13 | registered() { 14 | console.log('Service worker has been registered.'); 15 | }, 16 | cached() { 17 | console.log('Content has been cached for offline use.'); 18 | }, 19 | updatefound() { 20 | console.log('New content is downloading.'); 21 | }, 22 | updated() { 23 | console.log('New content is available; please refresh.'); 24 | }, 25 | offline() { 26 | console.log('No internet connection found. App is running in offline mode.'); 27 | }, 28 | error(error) { 29 | console.error('Error during service worker registration:', error); 30 | } 31 | }); 32 | } 33 | -------------------------------------------------------------------------------- /src/router.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Router from "vue-router"; 3 | import Layout from "@/layout/index.vue"; 4 | import { 5 | getToken, 6 | setToken, 7 | removeToken, 8 | getStoreId, 9 | setStoreId, 10 | removeStoreId, 11 | setUserInfo, 12 | getUserInfo, 13 | removeUserInfo 14 | } from "@/utils/cookies"; 15 | import store from "@/store"; 16 | 17 | Vue.use(Router); 18 | 19 | const router = new Router({ 20 | scrollBehavior: (to, from, savedPosition) => { 21 | if (savedPosition) { 22 | return savedPosition; 23 | } 24 | return { x: 0, y: 0 }; 25 | }, 26 | base: process.env.BASE_URL, 27 | routes: [ 28 | { 29 | path: "/login", 30 | component: () => 31 | import(/* webpackChunkName: "login" */ "@/views/login/index.vue"), 32 | meta: { title: "苍穹外卖", hidden: true, notNeedAuth: true } 33 | }, 34 | { 35 | path: "/404", 36 | component: () => import(/* webpackChunkName: "404" */ "@/views/404.vue"), 37 | meta: { title: "苍穹外卖", hidden: true, notNeedAuth: true } 38 | }, 39 | { 40 | path: "/", 41 | component: Layout, 42 | redirect: "/dashboard", 43 | children: [ 44 | { 45 | path: "dashboard", 46 | component: () => 47 | import(/* webpackChunkName: "dashboard" */ "@/views/dashboard/index.vue"), 48 | name: "Dashboard", 49 | meta: { 50 | title: "工作台", 51 | icon: "dashboard", 52 | affix: true 53 | } 54 | }, 55 | { 56 | path: "/statistics", 57 | component: () => 58 | import(/* webpackChunkName: "shopTable" */ "@/views/statistics/index.vue"), 59 | meta: { 60 | title: "数据统计", 61 | icon: "icon-statistics" 62 | } 63 | }, 64 | { 65 | path: "order", 66 | component: () => 67 | import(/* webpackChunkName: "shopTable" */ "@/views/orderDetails/index.vue"), 68 | meta: { 69 | title: "订单管理", 70 | icon: "icon-order" 71 | } 72 | }, 73 | { 74 | path: "setmeal", 75 | component: () => 76 | import(/* webpackChunkName: "shopTable" */ "@/views/setmeal/index.vue"), 77 | meta: { 78 | title: "套餐管理", 79 | icon: "icon-combo" 80 | } 81 | }, 82 | { 83 | path: "dish", 84 | component: () => 85 | import(/* webpackChunkName: "shopTable" */ "@/views/dish/index.vue"), 86 | meta: { 87 | title: "菜品管理", 88 | icon: "icon-dish" 89 | } 90 | }, 91 | { 92 | path: "/dish/add", 93 | component: () => 94 | import(/* webpackChunkName: "shopTable" */ "@/views/dish/addDishtype.vue"), 95 | meta: { 96 | title: "添加菜品", 97 | hidden: true 98 | } 99 | }, 100 | 101 | { 102 | path: "category", 103 | component: () => 104 | import(/* webpackChunkName: "shopTable" */ "@/views/category/index.vue"), 105 | meta: { 106 | title: "分类管理", 107 | icon: "icon-category" 108 | } 109 | }, 110 | { 111 | path: "employee", 112 | component: () => 113 | import(/* webpackChunkName: "shopTable" */ "@/views/employee/index.vue"), 114 | meta: { 115 | title: "员工管理", 116 | icon: "icon-employee" 117 | } 118 | }, 119 | 120 | { 121 | path: "/employee/add", 122 | component: () => 123 | import(/* webpackChunkName: "dashboard" */ "@/views/employee/addEmployee.vue"), 124 | meta: { 125 | title: "添加员工", 126 | hidden: true 127 | } 128 | }, 129 | 130 | { 131 | path: "/setmeal/add", 132 | component: () => 133 | import(/* webpackChunkName: "shopTable" */ "@/views/setmeal/addSetmeal.vue"), 134 | meta: { 135 | title: "添加套餐", 136 | hidden: true 137 | } 138 | } 139 | ] 140 | }, 141 | { 142 | path: "*", 143 | redirect: "/404", 144 | meta: { hidden: true } 145 | } 146 | ] 147 | }); 148 | 149 | export default router; 150 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue'; 3 | export default Vue; 4 | } 5 | 6 | declare module 'is-promise' 7 | 8 | declare module '@/config.json' -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import { IAppState } from './modules/app' 4 | import { IUserState } from './modules/user' 5 | 6 | Vue.use(Vuex) 7 | 8 | export interface IRootState { 9 | app: IAppState 10 | user: IUserState 11 | } 12 | 13 | export default new Vuex.Store({}) 14 | -------------------------------------------------------------------------------- /src/store/modules/app.ts: -------------------------------------------------------------------------------- 1 | import { VuexModule, Module, Mutation, Action, getModule } from 'vuex-module-decorators' 2 | import { getSidebarStatus, setSidebarStatus } from '@/utils/cookies' 3 | import store from '@/store' 4 | 5 | export enum DeviceType { 6 | Mobile, 7 | Desktop 8 | } 9 | 10 | export interface IAppState { 11 | device: DeviceType 12 | sidebar: { 13 | opened: boolean 14 | withoutAnimation: boolean 15 | 16 | } 17 | statusNumber:Number 18 | } 19 | 20 | @Module({ 'dynamic': true, store, 'name': 'app' }) 21 | class App extends VuexModule implements IAppState { 22 | public sidebar = { 23 | 'opened': true, //getSidebarStatus() !== 'closed', 24 | 'withoutAnimation': false 25 | } 26 | public device = DeviceType.Desktop 27 | public statusNumber = 0 28 | @Mutation 29 | private TOGGLE_SIDEBAR(withoutAnimation: boolean) { 30 | this.sidebar.opened = !this.sidebar.opened 31 | this.sidebar.withoutAnimation = withoutAnimation 32 | if (this.sidebar.opened) { 33 | setSidebarStatus('opened') 34 | } else { 35 | setSidebarStatus('closed') 36 | } 37 | } 38 | 39 | @Mutation 40 | private CLOSE_SIDEBAR(withoutAnimation: boolean) { 41 | this.sidebar.opened = false 42 | this.sidebar.withoutAnimation = withoutAnimation 43 | setSidebarStatus('closed') 44 | } 45 | 46 | @Mutation 47 | private STATUS_NUMBER(device: DeviceType) { 48 | this.statusNumber = device 49 | } 50 | 51 | @Mutation 52 | private TOGGLE_DEVICE(device: DeviceType) { 53 | this.device = device 54 | } 55 | 56 | @Action 57 | public ToggleSideBar(withoutAnimation: boolean) { 58 | this.TOGGLE_SIDEBAR(withoutAnimation) 59 | } 60 | 61 | @Action 62 | public CloseSideBar(withoutAnimation: boolean) { 63 | this.CLOSE_SIDEBAR(withoutAnimation) 64 | } 65 | 66 | @Action 67 | public ToggleDevice(device: DeviceType) { 68 | this.TOGGLE_DEVICE(device) 69 | } 70 | 71 | @Action 72 | public StatusNumber(device: any) { 73 | this.STATUS_NUMBER(device) 74 | } 75 | } 76 | 77 | export const AppModule = getModule(App) 78 | -------------------------------------------------------------------------------- /src/store/modules/user.ts: -------------------------------------------------------------------------------- 1 | import { VuexModule, Module, Action, Mutation, getModule } from 'vuex-module-decorators' 2 | import { login,userLogout } from '@/api/employee' 3 | import { getToken, setToken, removeToken,getStoreId, setStoreId, removeStoreId, setUserInfo, getUserInfo, removeUserInfo } from '@/utils/cookies' 4 | import store from '@/store' 5 | import Cookies from 'js-cookie' 6 | import { Message } from 'element-ui' 7 | export interface IUserState { 8 | token: string 9 | name: string 10 | avatar: string 11 | storeId: string 12 | introduction: string 13 | userInfo: any 14 | roles: string[] 15 | username: string 16 | } 17 | 18 | @Module({ 'dynamic': true, store, 'name': 'user' }) 19 | class User extends VuexModule implements IUserState { 20 | public token = getToken() || '' 21 | public name = '' 22 | public avatar = '' 23 | // @ts-ignore 24 | public storeId: string = getStoreId() || '' 25 | public introduction = '' 26 | public userInfo = {} 27 | public roles: string[] = [] 28 | public username = Cookies.get('username') || '' 29 | 30 | @Mutation 31 | private SET_TOKEN(token: string) { 32 | this.token = token 33 | } 34 | 35 | @Mutation 36 | private SET_NAME(name: string) { 37 | this.name = name 38 | } 39 | 40 | @Mutation 41 | private SET_USERINFO(userInfo: any) { 42 | this.userInfo = { ...userInfo } 43 | } 44 | 45 | @Mutation 46 | private SET_AVATAR(avatar: string) { 47 | this.avatar = avatar 48 | } 49 | 50 | @Mutation 51 | private SET_INTRODUCTION(introduction: string) { 52 | this.introduction = introduction 53 | } 54 | 55 | @Mutation 56 | private SET_ROLES(roles: string[]) { 57 | this.roles = roles 58 | } 59 | 60 | @Mutation 61 | private SET_STOREID(storeId: string) { 62 | this.storeId = storeId 63 | } 64 | @Mutation 65 | private SET_USERNAME(name: string) { 66 | this.username = name 67 | } 68 | 69 | @Action 70 | public async Login(userInfo: { username: string, password: string }) { 71 | let { username, password } = userInfo 72 | username = username.trim() 73 | this.SET_USERNAME(username) 74 | Cookies.set('username', username) 75 | const { data } = await login({ username, password }) 76 | if (String(data.code) === '1') { 77 | // const dataParams = { 78 | // // status: 200, 79 | // token: data.data.token, 80 | // // msg: '登录成功', 81 | // // ...data.data 82 | // ...data 83 | // } 84 | this.SET_TOKEN(data.data.token) 85 | setToken(data.data.token) 86 | this.SET_USERINFO(data.data) 87 | Cookies.set('user_info', data.data) 88 | return data 89 | } else { 90 | return Message.error(data.msg) 91 | } 92 | } 93 | 94 | @Action 95 | public ResetToken () { 96 | removeToken() 97 | this.SET_TOKEN('') 98 | this.SET_ROLES([]) 99 | } 100 | 101 | @Action 102 | public async changeStore(data: any) { 103 | this.SET_STOREID = data.data 104 | this.SET_TOKEN(data.authorization) 105 | setStoreId(data.data) 106 | setToken(data.authorization) 107 | } 108 | 109 | @Action 110 | public async GetUserInfo () { 111 | if (this.token === '') { 112 | throw Error('GetUserInfo: token is undefined!') 113 | } 114 | 115 | const data = JSON.parse(getUserInfo()) // { roles: ['admin'], name: 'zhangsan', avatar: '/login', introduction: '' } 116 | if (!data) { 117 | throw Error('Verification failed, please Login again.') 118 | } 119 | 120 | const { roles, name, avatar, introduction, applicant, storeManagerName, storeId='' } = data // data.user 121 | // roles must be a non-empty array 122 | if (!roles || roles.length <= 0) { 123 | throw Error('GetUserInfo: roles must be a non-null array!') 124 | } 125 | 126 | this.SET_ROLES(roles) 127 | this.SET_USERINFO(data) 128 | this.SET_NAME(name || applicant || storeManagerName) 129 | this.SET_AVATAR(avatar) 130 | this.SET_INTRODUCTION(introduction) 131 | } 132 | 133 | @Action 134 | public async LogOut () { 135 | const { data } = await userLogout({}) 136 | removeToken() 137 | this.SET_TOKEN('') 138 | this.SET_ROLES([]) 139 | Cookies.remove('username') 140 | Cookies.remove('user_info') 141 | removeUserInfo() 142 | } 143 | } 144 | 145 | export const UserModule = getModule(User) 146 | -------------------------------------------------------------------------------- /src/styles/_mixins.scss: -------------------------------------------------------------------------------- 1 | /* Mixins */ 2 | @mixin clearfix { 3 | &:after { 4 | content: ""; 5 | display: table; 6 | clear: both; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/styles/_svgicon.scss: -------------------------------------------------------------------------------- 1 | /* Recommended css code for vue-svgicon */ 2 | .svg-icon { 3 | display: inline-block; 4 | width: 16px; 5 | height: 16px; 6 | color: inherit; 7 | fill: none; 8 | stroke: currentColor; 9 | vertical-align: -0.15em; 10 | } 11 | 12 | .svg-fill { 13 | fill: currentColor; 14 | stroke: none; 15 | } 16 | 17 | .svg-up { 18 | transform: rotate(0deg); 19 | } 20 | 21 | .svg-right { 22 | transform: rotate(90deg); 23 | } 24 | 25 | .svg-down { 26 | transform: rotate(180deg); 27 | } 28 | 29 | .svg-left { 30 | transform: rotate(-90deg); 31 | } 32 | -------------------------------------------------------------------------------- /src/styles/_transition.scss: -------------------------------------------------------------------------------- 1 | /* Global transition */ 2 | // See https://vuejs.org/v2/guide/transitions.html for detail 3 | 4 | // fade 5 | .fade-enter-active, 6 | .fade-leave-active { 7 | transition: opacity 0.28s; 8 | } 9 | 10 | .fade-enter, 11 | .fade-leave-active { 12 | opacity: 0; 13 | } 14 | 15 | // fade-transform 16 | .fade-transform-leave-active, 17 | .fade-transform-enter-active { 18 | transition: all .5s; 19 | } 20 | 21 | .fade-transform-enter { 22 | opacity: 0; 23 | transform: translateX(-30px); 24 | } 25 | 26 | .fade-transform-leave-to { 27 | opacity: 0; 28 | transform: translateX(30px); 29 | } 30 | 31 | // breadcrumb 32 | .breadcrumb-enter-active, 33 | .breadcrumb-leave-active { 34 | transition: all .5s; 35 | } 36 | 37 | .breadcrumb-enter, 38 | .breadcrumb-leave-active { 39 | opacity: 0; 40 | transform: translateX(20px); 41 | } 42 | 43 | .breadcrumb-move { 44 | transition: all .5s; 45 | } 46 | 47 | .breadcrumb-leave-active { 48 | position: absolute; 49 | } 50 | -------------------------------------------------------------------------------- /src/styles/_variables.scss: -------------------------------------------------------------------------------- 1 | /* Variables */ 2 | 3 | // Base color 4 | $mine:#FFC200; 5 | $loginBg: #FFCFAC; 6 | $blue:#2892E5; 7 | $green: #1DC779; 8 | $yellow:#FFB302; 9 | $pink: #F56C6C; 10 | $block:#000; 11 | $gray-1: #20232A; 12 | $gray-2: #818693; 13 | $gray-3: #BAC0CD; 14 | $gray-4: #D8DDE3; 15 | $gray-5: #F3F4F7; 16 | $gray-6: #EBEEF5; 17 | $bgColor: #FAFAFB; 18 | $white: #fff; 19 | 20 | // Sidebar 21 | $sideBarWidth: 190px; 22 | $subMenuBg:#272A36; 23 | $subMenuHover:#272A36; 24 | $subMenuActiveText:#f4f4f5; 25 | $menuBg:#343744; 26 | $menuText:#bfcbd9; 27 | $menuActiveText:$mine; //Also see settings.sidebarTextTheme 28 | 29 | // Login page 30 | $lightGray: #080808; 31 | $darkGray:#889aa4; 32 | $loginCursorColor: #080808; 33 | 34 | // The :export directive is the magic sauce for webpack 35 | // https://mattferderer.com/use-sass-variables-in-typescript-and-javascript 36 | :export { 37 | menuBg: $menuBg; 38 | menuText: $menuText; 39 | menuActiveText: $menuActiveText; 40 | } 41 | 42 | .avatar-uploader .el-upload{ 43 | // border: dashed 2px #D8DDE3 !important; 44 | border-radius: 4px !important; 45 | background: #FCFCFC; 46 | } 47 | .avatar-uploader .avatar-uploader-icon{ 48 | background: #FCFCFC; 49 | } 50 | .avatar-uploader .el-icon-plus:before{ 51 | content: '上传图片' !important; 52 | font-size: 12px; 53 | color:#000; 54 | } 55 | 56 | 57 | // 弹框样式处理 58 | .el-dialog{ 59 | border-radius: 8px; 60 | } 61 | .el-dialog__header{ 62 | background: #FBFBFA; 63 | border-radius: 8px 8px 0 0; 64 | border: none; 65 | } 66 | -------------------------------------------------------------------------------- /src/styles/_variables.scss.d.ts: -------------------------------------------------------------------------------- 1 | export interface IScssVariables { 2 | menuBg: string 3 | menuText: string 4 | menuActiveText: string 5 | } 6 | 7 | export const variables: IScssVariables; 8 | 9 | export default variables; 10 | -------------------------------------------------------------------------------- /src/styles/element-variables.scss: -------------------------------------------------------------------------------- 1 | /* Element Variables */ 2 | 3 | // Override Element UI variables 4 | 5 | $--color-success: #13ce66; 6 | $--color-danger: #ff4949; 7 | $--color-info: #5d5d5d; 8 | $--button-font-weight: 400; 9 | $--border-color-light: #dfe4ed; 10 | $--border-color-lighter: #e6ebf5; 11 | $--table-border:1px solid #dfe6ec; 12 | $--color-primary:#FFC200; 13 | $--color-warning:#FFB302; 14 | $--color-text-primary:#20232A; 15 | $--color-text-regular:#818693; 16 | $--color-text-placeholder:#BAC0CD; 17 | $--border-color-base:#D8DDE3; 18 | $--background-color-base:#F3F4F7; 19 | 20 | // Icon font path, required 21 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 22 | 23 | // Apply overrided variables in Element UI 24 | @import '~element-ui/packages/theme-chalk/src/index'; 25 | -------------------------------------------------------------------------------- /src/styles/fonts/element-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/styles/fonts/element-icons.ttf -------------------------------------------------------------------------------- /src/styles/fonts/element-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/styles/fonts/element-icons.woff -------------------------------------------------------------------------------- /src/styles/icon/iconfont.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'iconfont'; /* Project id 3388893 */ 3 | src: url('//at.alicdn.com/t/font_3388893_e5ffotpqbv7.woff2?t=1654065128569') format('woff2'), 4 | url('//at.alicdn.com/t/font_3388893_e5ffotpqbv7.woff?t=1654065128569') format('woff'), 5 | url('//at.alicdn.com/t/font_3388893_e5ffotpqbv7.ttf?t=1654065128569') format('truetype'); 6 | } 7 | 8 | .iconfont { 9 | font-family: "iconfont" !important; 10 | font-size: 16px; 11 | font-style: normal; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | } 15 | .dashboard:before { 16 | content: "\e605"; 17 | } 18 | .icon-category:before { 19 | content: "\e60c"; 20 | } 21 | 22 | .icon-employee:before { 23 | content: "\e60b"; 24 | } 25 | 26 | .icon-user:before { 27 | content: "\e610"; 28 | } 29 | 30 | .icon-order:before { 31 | content: "\e607"; 32 | } 33 | 34 | .icon-combo:before { 35 | content: "\e609"; 36 | } 37 | 38 | .icon-lock:before { 39 | content: "\e60f"; 40 | } 41 | 42 | .icon-dish:before { 43 | content: "\e60a"; 44 | } 45 | .icon-inform:before { 46 | content: "\e60d"; 47 | } 48 | .icon-statistics:before { 49 | content: "\e608"; 50 | } 51 | .icon-clear:before { 52 | content: "\e611"; 53 | } 54 | .icon-download:before { 55 | content: "\e613"; 56 | } 57 | .icon-open:before { 58 | content: "\e614"; 59 | } 60 | .icon-stop:before { 61 | content: "\e615"; 62 | } 63 | .icon-waiting:before { 64 | content: "\e61a"; 65 | } 66 | 67 | .icon-staySway:before { 68 | content: "\e616"; 69 | } 70 | 71 | .icon-all:before { 72 | content: "\e617"; 73 | } 74 | 75 | .icon-cancel:before { 76 | content: "\e618"; 77 | } 78 | 79 | .icon-complete:before { 80 | content: "\e619"; 81 | } 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/styles/icon/iconfont.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2552591", 3 | "name": "rjwm", 4 | "font_family": "iconfont", 5 | "css_prefix_text": "icon-", 6 | "description": "", 7 | "glyphs": [ 8 | { 9 | "icon_id": "21641541", 10 | "name": "category", 11 | "font_class": "category", 12 | "unicode": "e606", 13 | "unicode_decimal": 58886 14 | }, 15 | { 16 | "icon_id": "21641542", 17 | "name": "employee", 18 | "font_class": "employee", 19 | "unicode": "e607", 20 | "unicode_decimal": 58887 21 | }, 22 | { 23 | "icon_id": "21641545", 24 | "name": "user", 25 | "font_class": "user", 26 | "unicode": "e608", 27 | "unicode_decimal": 58888 28 | }, 29 | { 30 | "icon_id": "21641546", 31 | "name": "order", 32 | "font_class": "order", 33 | "unicode": "e609", 34 | "unicode_decimal": 58889 35 | }, 36 | { 37 | "icon_id": "21641547", 38 | "name": "combo", 39 | "font_class": "combo", 40 | "unicode": "e60a", 41 | "unicode_decimal": 58890 42 | }, 43 | { 44 | "icon_id": "21641548", 45 | "name": "lock", 46 | "font_class": "lock", 47 | "unicode": "e60b", 48 | "unicode_decimal": 58891 49 | }, 50 | { 51 | "icon_id": "21641611", 52 | "name": "dish", 53 | "font_class": "dish", 54 | "unicode": "e60c", 55 | "unicode_decimal": 58892 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /src/styles/icon/iconfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/styles/icon/iconfont.ttf -------------------------------------------------------------------------------- /src/styles/icon/iconfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/styles/icon/iconfont.woff -------------------------------------------------------------------------------- /src/styles/icon/iconfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HeFeng-Lab/project-sky-admin-vue-ts/37d34c2fa5fb87b218672bb0f2f6a18861408f32/src/styles/icon/iconfont.woff2 -------------------------------------------------------------------------------- /src/styles/newRJWMsystem.scss: -------------------------------------------------------------------------------- 1 | // 登录页面-定制样式 2 | .login-ruiji { 3 | .el-form-item { 4 | // border: none; 5 | // border-bottom: 1px solid #ccc; 6 | border: 1px solid red; 7 | } 8 | 9 | .add-distance { 10 | margin-top: 68px; 11 | } 12 | } 13 | 14 | // .dashboard-container { 15 | // height: 100%; 16 | 17 | // .container { 18 | // height: 100%; 19 | // } 20 | // } 21 | .el-radio { 22 | .el-radio__label { 23 | color: #333; 24 | } 25 | } 26 | 27 | //统一修改单选按钮样式 28 | .el-radio__inner::after { 29 | background-color: #333; 30 | } 31 | 32 | //统一修改表单label的样式 33 | .el-form-item__label { 34 | color: #333; 35 | } 36 | 37 | .el-input__inner { 38 | border-color: #E5E4E4; 39 | } 40 | 41 | //统一修改输入框提示文字的样式 42 | .el-input__inner::-webkit-input-placeholder, 43 | .el-input__inner::-moz-placeholder, 44 | .el-input__inner::-ms-input-placeholder { 45 | color: #BDBDBD; 46 | } 47 | 48 | //el-image相关样式设置 49 | .dashboard-container { 50 | margin: 0 !important; 51 | padding: 20px !important; 52 | 53 | .el-table .cell { 54 | .el-image>img { 55 | width: 40px; 56 | border-radius: 5px; 57 | } 58 | } 59 | } 60 | 61 | //分页样式设置 62 | .el-pagination { 63 | .el-pagination__total { 64 | font-size: 14px; 65 | font-family: PingFangSC, PingFangSC-Regular; 66 | font-weight: 400; 67 | color: #333333; 68 | } 69 | 70 | .el-pagination__sizes { 71 | .el-input__inner { 72 | font-size: 14px; 73 | font-family: Helvetica; 74 | text-align: left; 75 | color: #333333; 76 | } 77 | } 78 | 79 | .el-pager { 80 | font-size: 14px; 81 | font-family: MicrosoftYaHei; 82 | font-weight: 400 !important; 83 | } 84 | 85 | .el-pagination__jump { 86 | font-size: 14px; 87 | font-family: PingFangSC, PingFangSC-Regular; 88 | font-weight: 400; 89 | color: #333333; 90 | 91 | .el-pagination__editor.el-input { 92 | font-family: Helvetica; 93 | } 94 | } 95 | } 96 | 97 | //弹框关闭按钮颜色 98 | .el-icon-circle-close { 99 | color: white; 100 | } 101 | 102 | //表格编辑列内容样式修正 103 | .dashboard-container .container .el-table tbody tr td .cell button span { 104 | padding: 0 10px; 105 | } 106 | 107 | .dashboard-container .container .el-table tbody tr td .cell button:first-child span { 108 | padding-left: 10px; 109 | } 110 | 111 | //按钮的背景色设置 112 | .el-button--primary { 113 | color: #333333; 114 | background: #FFC200; 115 | border-color: #FFC200; 116 | } 117 | 118 | .el-button--primary:hover { 119 | background: #fdd24c; 120 | border-color: #fdd24c; 121 | color: #333333; 122 | } 123 | 124 | .el-button--primary:active { 125 | background: #efb602; 126 | border-color: #efb602; 127 | color: #333333; 128 | } 129 | 130 | .el-button--text { 131 | font-weight: 400 !important; 132 | font-size: 13px !important; 133 | } 134 | 135 | .el-table td { 136 | font-size: 13px !important; 137 | } 138 | 139 | .el-table .cell, 140 | .el-table th div, 141 | .el-table--border td:first-child .cell, 142 | .el-table--border th:first-child .cell { 143 | padding-left: 30px !important; 144 | padding-right: 0 !important; 145 | } 146 | 147 | .el-table-column--selection .cell { 148 | padding-left: 12px !important; 149 | padding-right: 0 !important; 150 | text-overflow: clip !important; 151 | } 152 | 153 | .el-table-column--selection .cell .el-checkbox__inner { 154 | width: 13px; 155 | } 156 | 157 | .dashboard-container .container .el-table tbody tr td .cell button span { 158 | // padding: 0 10px; 159 | } 160 | -------------------------------------------------------------------------------- /src/styles/sidebar.scss: -------------------------------------------------------------------------------- 1 | .sidebar-container { 2 | .el-menu { 3 | border: none; 4 | height: 100%; 5 | width: 100% !important; 6 | 7 | .el-menu-item { 8 | color: #aeb5c4; 9 | height: 42px; 10 | line-height: 38px; 11 | padding: 0 0 0 17px !important; 12 | margin: 0 0 20px 0; 13 | 14 | // border-radius: 0 21px 21px 0 !important; 15 | &:hover { 16 | color: #ffffff !important; 17 | background: #4D4D4D !important; 18 | border-radius: 4px; 19 | 20 | span { 21 | color: #ffffff !important; 22 | } 23 | } 24 | 25 | &:active { 26 | color: #333333 !important; 27 | background-color: transparent !important; 28 | 29 | span { 30 | color: #333333 !important; 31 | } 32 | } 33 | } 34 | 35 | .router-link-active .el-menu-item { 36 | background-color: #E3E3E3 !important; 37 | color: #333333 !important; 38 | border-radius: 4px !important; 39 | 40 | span { 41 | color: #333333 !important; 42 | font-weight: 500 !important; 43 | } 44 | } 45 | 46 | .is-active { 47 | color: rgb(191, 203, 217) !important; 48 | } 49 | } 50 | 51 | .el-submenu.is-active { 52 | >.el-submenu__title { 53 | color: #f4f4f5 !important; 54 | } 55 | } 56 | 57 | .el-submenu { 58 | >.el-submenu__title { 59 | min-width: 190px !important; 60 | background-color: #272a36 !important; 61 | } 62 | 63 | .el-menu-item { 64 | min-width: 190px !important; 65 | background-color: #272a36 !important; 66 | } 67 | } 68 | 69 | .el-menu-item { 70 | i { 71 | color: inherit; 72 | font-size: 20px; 73 | margin-right: 9px; 74 | } 75 | 76 | position: relative; 77 | font-size: 14px !important; 78 | padding-left: 52px !important; 79 | } 80 | 81 | .simple-mode.first-level { 82 | .submenu-title-noDropdown { 83 | padding: 0 !important; 84 | position: relative; 85 | 86 | .el-tooltip { 87 | padding: 0 !important; 88 | } 89 | } 90 | 91 | .el-submenu { 92 | overflow: hidden; 93 | 94 | >.el-submenu__title { 95 | padding: 0px !important; 96 | 97 | .el-submenu__icon-arrow { 98 | display: none; 99 | } 100 | 101 | >span { 102 | visibility: hidden; 103 | } 104 | } 105 | } 106 | } 107 | 108 | .el-icon-arrow-down { 109 | &:before { 110 | color: #fff; 111 | } 112 | } 113 | 114 | .el-submenu__title { 115 | font-size: 16px !important; 116 | position: relative; 117 | z-index: 9; 118 | 119 | svg { 120 | margin-right: 5px !important; 121 | width: 28px !important; 122 | height: 28px !important; 123 | } 124 | } 125 | 126 | .horizontal-collapse-transition { 127 | transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out; 128 | } 129 | 130 | .scrollbar-wrapper { 131 | overflow-x: hidden !important; 132 | } 133 | 134 | .el-scrollbar__view { 135 | height: 100%; 136 | } 137 | 138 | .el-scrollbar__bar.is-vertical { 139 | right: 0px; 140 | } 141 | 142 | .el-scrollbar__bar.is-horizontal { 143 | display: none; 144 | } 145 | 146 | .svg-fill { 147 | margin-right: 7px; 148 | } 149 | } 150 | 151 | .hideSidebar { 152 | .el-tooltip { 153 | padding: 0 !important; 154 | text-align: center; 155 | } 156 | 157 | .svg-fill { 158 | margin-right: 0; 159 | } 160 | 161 | .sidebar-container .el-menu .el-menu-item { 162 | padding-left: 0 !important; 163 | 164 | &:hover { 165 | background-color: #4D4D4D !important; 166 | } 167 | 168 | i { 169 | margin-right: 0; 170 | } 171 | } 172 | 173 | .sidebar-container .router-link-active .el-menu-item, 174 | .el-menu-item.is-active { 175 | background-color: #E3E3E3 !important; 176 | } 177 | 178 | .sidebar-container .router-link-active .el-menu-item:hover, 179 | .el-menu-item.is-active { 180 | background-color: #FFC200 !important; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/utils/common.ts: -------------------------------------------------------------------------------- 1 | export const checkProcessEnv =() => { 2 | return process.env.VUE_APP_DELETE_PERMISSIONS==='true' 3 | } 4 | export const debounce=(fn, time)=> { 5 | time = time || 200 6 | // 定时器 7 | let timer = null 8 | return function(...args) { 9 | var _this = this 10 | if (timer) { 11 | clearTimeout(timer) 12 | } 13 | timer = setTimeout(function() { 14 | timer = null 15 | fn.apply(_this, args) 16 | }, time) 17 | } 18 | 19 | }; 20 | //节流 21 | export const throttle = (fn, time)=> { 22 | let timer = null 23 | time = time || 1000 24 | return function(...args) { 25 | if (timer) { 26 | return 27 | } 28 | const _this = this 29 | timer = setTimeout(() => { 30 | timer = null 31 | }, time) 32 | fn.apply(_this, args) 33 | } 34 | } 35 | // 判断正、负 36 | export const strIncrease = (str)=>{ 37 | if(str.slice(0,1) ==='-'){ 38 | return true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/utils/cookies.ts: -------------------------------------------------------------------------------- 1 | import Cookies from 'js-cookie'; 2 | 3 | // App 4 | const sidebarStatusKey = 'sidebar_status'; 5 | export const getSidebarStatus = () => Cookies.get(sidebarStatusKey); 6 | export const setSidebarStatus = (sidebarStatus: string) => Cookies.set(sidebarStatusKey, sidebarStatus); 7 | 8 | // User 9 | const storeId = 'storeId'; 10 | export const getStoreId = () => Cookies.get(storeId); 11 | export const setStoreId = (id: string) => Cookies.set(storeId, id); 12 | export const removeStoreId = () => Cookies.remove(storeId); 13 | 14 | // User 15 | const tokenKey = 'token'; 16 | export const getToken = () => Cookies.get(tokenKey); 17 | export const setToken = (token: string) => Cookies.set(tokenKey, token); 18 | export const removeToken = () => Cookies.remove(tokenKey); 19 | 20 | // userInfo 21 | 22 | const userInfoKey = 'userInfo'; 23 | export const getUserInfo = () => Cookies.get(userInfoKey); 24 | export const setUserInfo = (useInfor: Object) => Cookies.set(userInfoKey, useInfor); 25 | export const removeUserInfo = () => Cookies.remove(userInfoKey); 26 | 27 | // printinfo 28 | 29 | const printKey = 'print'; 30 | export const getPrint = () => Cookies.get(printKey); 31 | export const setPrint = (useInfor: Object) => Cookies.set(printKey, useInfor); 32 | export const removePrint = () => Cookies.remove(printKey); 33 | 34 | // 获取消息 35 | const newData = 'new'; 36 | export const getNewData = () => Cookies.get(newData); 37 | export const setNewData = (val: Object) => Cookies.set(newData, val); 38 | -------------------------------------------------------------------------------- /src/utils/formValidate.ts: -------------------------------------------------------------------------------- 1 | export const formatDate = () => { 2 | const now = new Date(); 3 | let hour: string | number = now.getHours(); 4 | let minute: string | number = now.getMinutes(); 5 | let second: string | number = now.getSeconds(); 6 | if (hour < 10) hour = `0${hour}`; 7 | if (minute < 10) minute = `0${minute}`; 8 | if (second < 10) second = `0${second}`; 9 | return `${hour}:${minute}:${second}`; 10 | }; 11 | 12 | function dateFormat(fmt: any, time: any) { 13 | let date = new Date(time); 14 | let ret; 15 | const opt = { 16 | // 年 17 | "Y+": date.getFullYear().toString(), 18 | // 月 19 | "m+": (date.getMonth() + 1).toString(), 20 | // 日 21 | "d+": date.getDate().toString() 22 | // 有其他格式化字符需求可以继续添加,必须转化成字符串 23 | } as any; 24 | for (const k in opt) { 25 | ret = new RegExp("(" + k + ")").exec(fmt); 26 | if (ret) { 27 | fmt = fmt.replace( 28 | ret[1], 29 | ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, "0") 30 | ); 31 | } 32 | } 33 | return fmt; 34 | } 35 | 36 | // js获取昨日的日期 37 | export const get1stAndToday = () => { 38 | let toData = new Date(new Date().toLocaleDateString()).getTime(); 39 | let yesterdayStart = toData - 3600 * 24 * 1000; 40 | let yesterdayEnd = yesterdayStart + 24 * 60 * 60 * 1000 - 1; 41 | let startDay1 = dateFormat("YYYY-mm-dd", yesterdayStart); 42 | let endDay1 = dateFormat("YYYY-mm-dd", yesterdayEnd); 43 | return [startDay1, endDay1]; 44 | }; 45 | // 获取昨日、今日日期 46 | export const getday = () => { 47 | let toData = new Date(new Date().toLocaleDateString()).getTime(); 48 | let yesterdays= toData - 3600 * 24 * 1000; 49 | let yesterday = dateFormat("YYYY.mm.dd", yesterdays); 50 | let today = dateFormat("YYYY.mm.dd", toData); 51 | return [yesterday,today]; 52 | }; 53 | 54 | // 获取近7日 55 | export const past7Day = () => { 56 | let toData = new Date(new Date().toLocaleDateString()).getTime(); 57 | let past7daysStart = toData - 7 * 3600 * 24 * 1000; 58 | let past7daysEnd = toData - 1; 59 | let days7Start = dateFormat("YYYY-mm-dd", past7daysStart); 60 | let days7End = dateFormat("YYYY-mm-dd", past7daysEnd); 61 | return [days7Start, days7End]; 62 | }; 63 | 64 | // 获取近30日 65 | export const past30Day = () => { 66 | let toData = new Date(new Date().toLocaleDateString()).getTime(); 67 | let past30daysStart = toData - 30 * 3600 * 24 * 1000; 68 | let past30daysEnd = toData - 1; 69 | let days30Start = dateFormat("YYYY-mm-dd", past30daysStart); 70 | let days30End = dateFormat("YYYY-mm-dd", past30daysEnd); 71 | return [days30Start, days30End]; 72 | }; 73 | // 获取本周 74 | export const pastWeek = () => { 75 | let toData = new Date(new Date().toLocaleDateString()).getTime(); 76 | var nowDayOfWeek = new Date().getDay(); 77 | const weekStartData = toData - (nowDayOfWeek - 1) * 24 * 60 * 60 * 1000; 78 | const weekEndData = toData + (7 - nowDayOfWeek) * 24 * 60 * 60 * 1000; 79 | let weekStart = dateFormat("YYYY-mm-dd", weekStartData); 80 | let weekEnd = dateFormat("YYYY-mm-dd", weekEndData); 81 | return [weekStart, weekEnd]; 82 | }; 83 | // 获取本月 84 | export const pastMonth = () => { 85 | let year = new Date().getFullYear() 86 | let month =new Date().getMonth() 87 | const monthStartData = new Date(year, month, 1).getTime() 88 | const monthEndData = new Date(year, month + 1, 0).getTime() + 24 * 60 * 60 * 1000 - 1 89 | let monthStart = dateFormat("YYYY-mm-dd", monthStartData); 90 | let monthEnd = dateFormat("YYYY-mm-dd", monthEndData); 91 | return [monthStart, monthEnd]; 92 | }; 93 | -------------------------------------------------------------------------------- /src/utils/request.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosAdapter } from 'axios' 2 | import { Message, MessageBox } from 'element-ui' 3 | import { UserModule } from '@/store/modules/user' 4 | import { 5 | getRequestKey, 6 | pending, 7 | checkPending, 8 | removePending 9 | } from './requestOptimize' 10 | import router from '@/router' 11 | const CancelToken = axios.CancelToken; 12 | 13 | const service = axios.create({ 14 | baseURL: process.env.VUE_APP_BASE_API, 15 | 'timeout': 600000 16 | }) 17 | 18 | // Request interceptors 19 | service.interceptors.request.use( 20 | (config: any) => { 21 | // console.log(config, 'config') 22 | // config.data = config.params 23 | // Add X-Access-Token header to every request, you can add other custom headers here 24 | if (UserModule.token) { 25 | config.headers['token'] = UserModule.token 26 | } else if (UserModule.token && config.url != '/login') { 27 | window.location.href = '/login' 28 | return false 29 | } 30 | 31 | // config.headers['Access-Control-Allow-Origin'] = '*' 32 | // config.headers['Content-Type'] = 'application/json;' 33 | // get请求映射params参数 34 | if (config.method === 'get' && config.params) { 35 | let url = config.url + '?'; 36 | for (const propName of Object.keys(config.params)) { 37 | const value = config.params[propName]; 38 | var part = encodeURIComponent(propName) + '='; 39 | if (value !== null && typeof (value) !== 'undefined') { 40 | if (typeof value === 'object') { 41 | for (const key of Object.keys(value)) { 42 | let params = propName + '[' + key + ']'; 43 | var subPart = encodeURIComponent(params) + '='; 44 | url += subPart + encodeURIComponent(value[key]) + '&'; 45 | } 46 | } else { 47 | url += part + encodeURIComponent(value) + '&'; 48 | } 49 | } 50 | } 51 | url = url.slice(0, -1); 52 | config.params = {}; 53 | config.url = url; 54 | } 55 | // 计算当前请求key值 56 | const key = getRequestKey(config); 57 | // console.log(pending,key,checkPending(key),'checkPending(key)') 58 | if (checkPending(key)) { 59 | // 重复请求则取消当前请求 60 | const source = CancelToken.source(); 61 | config.cancelToken = source.token; 62 | source.cancel('重复请求'); 63 | } else { 64 | // 加入请求字典 65 | pending[key] = true; 66 | } 67 | return config 68 | }, 69 | (error: any) => { 70 | Promise.reject(error) 71 | } 72 | ) 73 | 74 | // Response interceptors 75 | service.interceptors.response.use( 76 | (response: any) => { 77 | // console.log(response, 'response') 78 | if (response.data.status === 401) { 79 | router.push('/login') 80 | // const res = response.data 81 | // return response 82 | } 83 | //请求响应中的config的url会带上代理的api需要去掉 84 | response.config.url = response.config.url.replace('/api', '') 85 | // 请求完成,删除请求中状态 86 | const key = getRequestKey(response.config); 87 | removePending(key); 88 | // if (response.data.code === 0) { 89 | // Message.error(response.data.msg) 90 | // // if(response.data.msg === 'NOTLOGIN' || response.data.msg === '未登录'){ 91 | // // router.push('/login') 92 | // // } 93 | // // return window.location.href = '/login' 94 | // // window.location.href = '/login' 95 | // // return false 96 | // } else 97 | if (response.data.code === 1) { 98 | // const res = response.data 99 | return response 100 | } 101 | return response 102 | }, 103 | (error: any) => { 104 | // console.log(error.config, pending, 'error') 105 | if (error && error.response) { 106 | switch (error.response.status) { 107 | case 401: 108 | router.push('/login') 109 | break; 110 | case 405: 111 | error.message = '请求错误' 112 | } 113 | } 114 | //请求响应中的config的url会带上代理的api需要去掉 115 | error.config.url = error.config.url.replace('/api', '') 116 | // 请求完成,删除请求中状态 117 | const key = getRequestKey(error.config); 118 | removePending(key); 119 | // console.log(error, pending, 'error11') 120 | // Message({ 121 | // 'message': error.message, 122 | // 'type': 'error', 123 | // 'duration': 5 * 1000 124 | // }) 125 | // router.push('/login') 126 | return Promise.reject(error) 127 | } 128 | ) 129 | 130 | export default service 131 | -------------------------------------------------------------------------------- /src/utils/requestOptimize.ts: -------------------------------------------------------------------------------- 1 | import md5 from 'md5'; 2 | 3 | //根据请求的地址,方式,参数,统一计算出当前请求的md5值作为key 4 | const getRequestKey = (config) => { 5 | if (!config) { 6 | // 如果没有获取到请求的相关配置信息,根据时间戳生成 7 | return md5(+new Date()); 8 | } 9 | 10 | const data = typeof config.data === 'string' ? config.data : JSON.stringify(config.data); 11 | // console.log(config,pending,config.url,md5(config.url + '&' + config.method + '&' + data),'config') 12 | return md5(config.url + '&' + config.method + '&' + data); 13 | } 14 | 15 | // 存储key值 16 | const pending = {}; 17 | // 检查key值 18 | const checkPending = (key) => !!pending[key]; 19 | // 删除key值 20 | const removePending = (key) => { 21 | // console.log(key,'key') 22 | delete pending[key]; 23 | }; 24 | 25 | export { 26 | getRequestKey, 27 | pending, 28 | checkPending, 29 | removePending 30 | } 31 | -------------------------------------------------------------------------------- /src/utils/validate.ts: -------------------------------------------------------------------------------- 1 | export const isValidUsername = (str: string) => ['admin', 'editor'].indexOf(str.trim()) >= 0; 2 | 3 | export const isExternal = (path: string) => /^(https?:|mailto:|tel:)/.test(path); 4 | -------------------------------------------------------------------------------- /src/views/dashboard/components/cuisineStatistics.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 38 | -------------------------------------------------------------------------------- /src/views/dashboard/components/orderview.vue: -------------------------------------------------------------------------------- 1 | 59 | 60 | 73 | -------------------------------------------------------------------------------- /src/views/dashboard/components/overview.vue: -------------------------------------------------------------------------------- 1 | 36 | 49 | -------------------------------------------------------------------------------- /src/views/dashboard/components/setMealStatistics.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 36 | -------------------------------------------------------------------------------- /src/views/dashboard/index.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 118 | 119 | 121 | -------------------------------------------------------------------------------- /src/views/dish/components/SelectInput.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 62 | 63 | 101 | -------------------------------------------------------------------------------- /src/views/orderDetails/tabChange.vue: -------------------------------------------------------------------------------- 1 | 2 | 18 | 19 | 74 | 120 | -------------------------------------------------------------------------------- /src/views/statistics/components/orderStatistics.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 181 | -------------------------------------------------------------------------------- /src/views/statistics/components/overview.vue: -------------------------------------------------------------------------------- 1 | 122 | 135 | -------------------------------------------------------------------------------- /src/views/statistics/components/titleIndex.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 78 | -------------------------------------------------------------------------------- /src/views/statistics/components/top10.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 122 | -------------------------------------------------------------------------------- /src/views/statistics/components/turnoverStatistics.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 119 | -------------------------------------------------------------------------------- /src/views/statistics/components/userStatistics.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 150 | 152 | -------------------------------------------------------------------------------- /src/views/statistics/index.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 162 | 163 | 165 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | jest: true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/unit/components/Breadcrumb.spec.ts: -------------------------------------------------------------------------------- 1 | import { mount, createLocalVue } from '@vue/test-utils'; 2 | import VueRouter from 'vue-router'; 3 | import ElementUI from 'element-ui'; 4 | import Breadcrumb from '@/components/Breadcrumb/index.vue'; 5 | 6 | const localVue = createLocalVue(); 7 | localVue.use(VueRouter); 8 | localVue.use(ElementUI); 9 | 10 | const routes = [ 11 | { 12 | 'path': '/', 13 | 'children': [{ 14 | 'path': 'dashboard' 15 | }] 16 | }, 17 | { 18 | 'path': '/menu', 19 | 'children': [{ 20 | 'path': 'menu1', 21 | 'meta': { 'title': 'menu1' }, 22 | 'children': [{ 23 | 'path': 'menu1-1', 24 | 'meta': { 'title': 'menu1-1' } 25 | }, 26 | { 27 | 'path': 'menu1-2', 28 | 'redirect': 'noredirect', 29 | 'meta': { 'title': 'menu1-2' }, 30 | 'children': [{ 31 | 'path': 'menu1-2-1', 32 | 'meta': { 'title': 'menu1-2-1' } 33 | }, 34 | { 35 | 'path': 'menu1-2-2' 36 | }] 37 | }] 38 | }] 39 | }]; 40 | 41 | const router = new VueRouter({ 42 | routes 43 | }); 44 | 45 | describe('Breadcrumb.vue', () => { 46 | const wrapper = mount(Breadcrumb, { 47 | localVue 48 | 49 | }); 50 | 51 | it('dashboard', () => { 52 | router.push('/dashboard'); 53 | const len = wrapper.findAll('.el-breadcrumb__inner').length; 54 | expect(len).toBe(1); 55 | }); 56 | 57 | it('normal route', () => { 58 | router.push('/menu/menu1'); 59 | const len = wrapper.findAll('.el-breadcrumb__inner').length; 60 | expect(len).toBe(2); 61 | }); 62 | 63 | it('nested route', () => { 64 | router.push('/menu/menu1/menu1-2/menu1-2-1'); 65 | const len = wrapper.findAll('.el-breadcrumb__inner').length; 66 | expect(len).toBe(4); 67 | }); 68 | 69 | it('no meta.title', () => { 70 | router.push('/menu/menu1/menu1-2/menu1-2-2'); 71 | const len = wrapper.findAll('.el-breadcrumb__inner').length; 72 | expect(len).toBe(3); 73 | }); 74 | 75 | it('click link', () => { 76 | router.push('/menu/menu1/menu1-2/menu1-2-2'); 77 | const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner'); 78 | const second = breadcrumbArray.at(1); 79 | const href = second.find('a').text(); 80 | expect(href).toBe('menu1'); 81 | }); 82 | 83 | it('noredirect', () => { 84 | router.push('/menu/menu1/menu1-2/menu1-2-1'); 85 | const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner'); 86 | const redirectBreadcrumb = breadcrumbArray.at(2); 87 | expect(redirectBreadcrumb.contains('a')).toBe(false); 88 | }); 89 | 90 | it('last breadcrumb', () => { 91 | router.push('/menu/menu1/menu1-2/menu1-2-1'); 92 | const breadcrumbArray = wrapper.findAll('.el-breadcrumb__inner'); 93 | const redirectBreadcrumb = breadcrumbArray.at(3); 94 | expect(redirectBreadcrumb.contains('a')).toBe(false); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /tests/unit/utils/validate.spec.ts: -------------------------------------------------------------------------------- 1 | import { isValidUsername, isExternal } from '@/utils/validate'; 2 | 3 | describe('Utils:validate', () => { 4 | it('isValidUsername', () => { 5 | expect(isValidUsername('admin')).toBe(true); 6 | expect(isValidUsername('editor')).toBe(true); 7 | expect(isValidUsername('xxxx')).toBe(false); 8 | }); 9 | 10 | it('isExternal', () => { 11 | expect(isExternal('https://www.armour.com/')).toBe(true); 12 | expect(isExternal('mailto:someone@test.com')).toBe(true); 13 | expect(isExternal('123aBC')).toBe(false); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "strict": false, 6 | "jsx": "preserve", 7 | "importHelpers": true, 8 | "moduleResolution": "node", 9 | "experimentalDecorators": true, 10 | "esModuleInterop": true, 11 | "allowSyntheticDefaultImports": true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "skipLibCheck":true, 15 | "types": [ 16 | "node", 17 | "jest", 18 | "webpack-env" 19 | ], 20 | "paths": { 21 | "@/*": [ 22 | "src/*" 23 | ] 24 | }, 25 | "lib": [ 26 | "esnext", 27 | "dom", 28 | "dom.iterable", 29 | "scripthost" 30 | ] 31 | }, 32 | "include": [ 33 | "src/**/*.ts", 34 | "src/**/*.tsx", 35 | 36 | "tests/**/*.ts", 37 | "tests/**/*.tsx" 38 | ], 39 | "exclude": [ 40 | "node_modules", 41 | "src/**/*.vue" 42 | ] 43 | } 44 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const name = 'Vue Typescript Admin' 3 | const IS_PROD = ['production', 'development'].includes(process.env.NODE_ENV) 4 | 5 | module.exports = { 6 | 'publicPath': process.env.NODE_ENV === 'production' ? './' : '/', // TODO: Remember to change this to fit your need 7 | 'lintOnSave': process.env.NODE_ENV === 'development', 8 | 'pwa': { 9 | 'name': name 10 | }, 11 | 'pluginOptions': { 12 | 'style-resources-loader': { 13 | 'preProcessor': 'scss', 14 | 'patterns': [ 15 | path.resolve(__dirname, 'src/styles/_variables.scss'), 16 | path.resolve(__dirname, 'src/styles/_mixins.scss') 17 | ] 18 | } 19 | }, 20 | // 开启代理 21 | devServer: { 22 | host:'0.0.0.0', 23 | public: '0.0.0.0:8888', // 本地的ip:端口号 24 | port: 8888, 25 | open: true, 26 | disableHostCheck:true, 27 | hot:true,//自动保存 28 | overlay: { 29 | warnings: false, 30 | errors: true 31 | }, 32 | proxy: { 33 | '/api': { 34 | target: process.env.VUE_APP_URL, 35 | ws: false, 36 | secure: false, 37 | changeOrigin: true, 38 | pathRewrite:{ 39 | '^/api':'' 40 | } 41 | } 42 | } 43 | }, 44 | chainWebpack: (config) => { 45 | config.resolve.symlinks(true) // 修复热更新失效 46 | }, 47 | configureWebpack: { 48 | devtool: 'source-map' 49 | }, 50 | 51 | css: { 52 | // 是否使用css分离插件 ExtractTextPlugin 53 | extract: IS_PROD, 54 | // 开启 CSS source maps? 55 | sourceMap: false, 56 | // css预设器配置项 57 | loaderOptions: { 58 | }, 59 | // 启用 CSS modules for all css / pre-processor files. 60 | modules: false, 61 | }, 62 | }; 63 | --------------------------------------------------------------------------------