├── .browserslistrc ├── .eslintrc.js ├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── postcss.config.js ├── public ├── favicon.ico ├── img │ └── icons │ │ ├── android-chrome-192x192.png │ │ ├── android-chrome-512x512.png │ │ ├── android-chrome-maskable-192x192.png │ │ ├── android-chrome-maskable-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 └── robots.txt ├── src ├── App.vue ├── api │ └── index.ts ├── assets │ └── styles │ │ ├── index.scss │ │ ├── main.scss │ │ ├── mixin.scss │ │ └── reset.scss ├── components │ ├── confirm │ │ └── index.vue │ ├── menu │ │ ├── index.scss │ │ └── index.vue │ ├── modal │ │ └── index.vue │ ├── page-head │ │ ├── assets │ │ │ ├── index.scss │ │ │ ├── left-normal.png │ │ │ └── left-white.png │ │ └── index.vue │ ├── product-item │ │ ├── index.scss │ │ ├── index.vue │ │ └── product_default.jpg │ └── scroll │ │ └── index.vue ├── config │ └── index.ts ├── main.ts ├── mock │ └── data.ts ├── registerServiceWorker.ts ├── router │ └── index.ts ├── shims-tsx.d.ts ├── shims-vue.d.ts ├── store │ ├── home │ │ ├── index.ts │ │ └── interface.ts │ ├── index.ts │ └── product │ │ ├── index.ts │ │ └── interface.ts ├── utils │ └── http.ts └── views │ ├── home │ ├── components │ │ ├── floor │ │ │ ├── index.scss │ │ │ └── index.vue │ │ └── swiper │ │ │ └── swiper.vue │ └── index.vue │ ├── login │ ├── index.scss │ └── index.vue │ ├── product-detail │ ├── components │ │ ├── detail-con.vue │ │ └── head.vue │ ├── index.scss │ └── index.vue │ ├── product-list │ ├── component │ │ ├── list-item │ │ │ └── index.vue │ │ ├── search-head │ │ │ └── index.vue │ │ └── select-tab │ │ │ └── index.vue │ ├── index.scss │ └── index.vue │ ├── profile │ ├── component │ │ ├── profile-foot │ │ │ └── index.vue │ │ └── profile-info │ │ │ └── index.vue │ ├── index.scss │ └── index.vue │ ├── register │ ├── index.scss │ └── index.vue │ └── user │ ├── components │ ├── head-info │ │ └── index.vue │ └── tab-list │ │ └── index.vue │ ├── index.scss │ └── index.vue ├── tsconfig.json ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: [ 7 | "plugin:vue/essential", 8 | "eslint:recommended", 9 | "@vue/typescript/recommended", 10 | "@vue/prettier", 11 | "@vue/prettier/@typescript-eslint" 12 | ], 13 | parserOptions: { 14 | ecmaVersion: 2020 15 | }, 16 | rules: { 17 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 18 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off" 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 使用ts对shop项目进行重构 2 | 3 | 4 | vue2 + vuex + vue-router + webpack + ES6/7 + axios + sass 5 | 6 | 框架:使用了vue全家桶进行开发,路由跳转使用的是vue-router,数据请求使用了官方推荐的axios插件,使用es6/7进行开发。 7 | 8 | 移动端适配: 由于是web-app,因此需要兼容不同设备的屏幕的大小,在这里使用的手淘推荐的[flexible方案](https://www.w3cplus.com/mobile/lib-flexible-for-html5-layout.html),通过动态的设置根元素的font-size大小,使用rem来进行移动端适配。在这里由于使用rem进行布局,而通常给我们的设计稿是640px,750px为标准的,在编写的时候把px大小转换为rem也比较麻烦,因此这里使用了postcss-px2rem,在编译的时候会将px自动转换为rem。 9 | 10 | css预处理器:目前流行的css预处理主要是stylus,less,sass,个人感觉less和sass差别不大,stylus缩进式语法有点不太习惯,综上选择了sass进行样式的编写,通过预处理器可以以编程的方式书写css代码,添加变量,函数,样式继承等。 11 | 12 | 后台接口:在这里使用的是慕课网提供的接口:[接口文档](https://gitee.com/imooccode/happymmallwiki/wikis/Home) 13 | 14 | 跨域处理:由于使用的外部接口,前端项目运行地址与接口访问地址不同,浏览器的同源策略使得我们不得不处理跨域,因此需要对跨域进行简单处理。 15 | 1. 开发模式下:需要在config下的index.js进行配置 16 | proxyTable: { 17 | '/api': { 18 | target: 'http://test.happymmall.com', //源地址 19 | changeOrigin: true, //改变源 20 | pathRewrite: { 21 | '^/api': '' //路径重写 22 | } 23 | } 24 | } 25 | ``` 26 | 27 | 在进行接口请求时在接口路径前加/api即可,编译后会将/api重写为线上的接口地址 28 | 29 | 2. 生产模式下:在这里使用的是nignx,需要在远程服务器上安装nignx,然后在nginx.conf文件内配置location即可。 30 | 31 | # 功能介绍 32 | 33 | 在这里主要用vue是把之前慕课网上电商项目进行了重构,做了个移动版本的,功能基本相同,主要是包括4个模块: 34 | 35 | 用户模块:登录,注册,个人信息修改,密码找回,更新密码。 36 | 37 | 商品模块:首页,分类,搜索商品,商品详情 38 | 39 | 购物车模块:购物车商品增加,删除,全选,单选,多选 40 | 41 | 订单模块:包括地址的管理,提交订单,订单列表,详情,取消订单等 42 | 43 | 在这里本来想做支付模块,发现接口返回的二维码失效支付不了,因此只到支付详情这块。 44 |
45 | 46 | 后续: 47 | 48 | 1. 在这里只是简单实现了基本功能,当然作为web-app,用户体验是第一位的,后续会持续的对项目进行性能优化 49 | 2. 数据请求这块使用的axios插件,后续会用原生fecth进行实现 50 | 51 | #### 感觉不错的,反手就是一个赞👍,另外项目还要很多地方需要优化,欢迎大家issue! 52 | 53 | 54 | ## Project setup 55 | ``` 56 | yarn install 57 | ``` 58 | 59 | ### Compiles and hot-reloads for development 60 | ``` 61 | yarn serve 62 | ``` 63 | 64 | ### Compiles and minifies for production 65 | ``` 66 | yarn build 67 | ``` 68 | 69 | ### Lints and fixes files 70 | ``` 71 | yarn lint 72 | ``` 73 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"] 3 | }; 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shop-ts", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint --fix" 9 | }, 10 | "dependencies": { 11 | "@types/qs": "^6.9.4", 12 | "axios": "^0.19.0", 13 | "better-scroll": "^1.15.2", 14 | "core-js": "^3.6.5", 15 | "qs": "^6.9.4", 16 | "register-service-worker": "^1.7.1", 17 | "swiper": "^6.1.1", 18 | "vue": "^2.6.11", 19 | "vue-class-component": "^7.2.3", 20 | "vue-property-decorator": "^8.4.2", 21 | "vue-router": "^3.2.0", 22 | "vuex": "^3.4.0", 23 | "vuex-class": "^0.3.2" 24 | }, 25 | "devDependencies": { 26 | "@types/better-scroll": "^1.12.2", 27 | "@typescript-eslint/eslint-plugin": "^2.33.0", 28 | "@typescript-eslint/parser": "^2.33.0", 29 | "@vue/cli-plugin-babel": "~4.4.0", 30 | "@vue/cli-plugin-eslint": "~4.4.0", 31 | "@vue/cli-plugin-pwa": "~4.4.0", 32 | "@vue/cli-plugin-router": "~4.4.0", 33 | "@vue/cli-plugin-typescript": "~4.4.0", 34 | "@vue/cli-plugin-vuex": "~4.4.0", 35 | "@vue/cli-service": "~4.4.0", 36 | "@vue/eslint-config-prettier": "^6.0.0", 37 | "@vue/eslint-config-typescript": "^5.0.2", 38 | "compression-webpack-plugin": "^4.0.0", 39 | "eslint": "^6.7.2", 40 | "eslint-plugin-prettier": "^3.1.3", 41 | "eslint-plugin-vue": "^6.2.2", 42 | "postcss-px-to-viewport": "^1.1.1", 43 | "prettier": "^1.19.1", 44 | "sass": "^1.26.5", 45 | "sass-loader": "^8.0.2", 46 | "typescript": "~3.9.3", 47 | "vue-template-compiler": "^2.6.11" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | 'postcss-px-to-viewport': { 5 | unitToConvert: 'px', 6 | viewportWidth: 750, 7 | unitPrecision: 5, 8 | propList: ['*'], 9 | viewportUnit: 'vw', 10 | fontViewportUnit: 'vw', 11 | selectorBlackList: ['lx-toast', /^.lx-load*/, /^.loading_leafy*/], 12 | minPixelValue: 1, 13 | mediaQuery: false, 14 | replace: true, 15 | exclude: [], 16 | landscape: false, 17 | landscapeUnit: 'vw', 18 | landscapeWidth: 568, 19 | }, 20 | }, 21 | }; -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/favicon.ico -------------------------------------------------------------------------------- /public/img/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/android-chrome-maskable-192x192.png -------------------------------------------------------------------------------- /public/img/icons/android-chrome-maskable-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/android-chrome-maskable-512x512.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /public/img/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /public/img/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/favicon-16x16.png -------------------------------------------------------------------------------- /public/img/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/favicon-32x32.png -------------------------------------------------------------------------------- /public/img/icons/msapplication-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/msapplication-icon-144x144.png -------------------------------------------------------------------------------- /public/img/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/public/img/icons/mstile-150x150.png -------------------------------------------------------------------------------- /public/img/icons/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | <%= htmlWebpackPlugin.options.title %> 10 | 11 | 12 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 54 | -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | import HTTP from "../utils/http"; 2 | 3 | export interface LoginParamsProps { 4 | username: string; 5 | password: string; 6 | } 7 | 8 | export interface RegisterParamsProps { 9 | username: string; 10 | password: string; 11 | email: string; 12 | phone: string; 13 | question: string; 14 | answer: string; 15 | } 16 | 17 | export interface ProductListParams { 18 | categoryId?: number; 19 | keyword?: string; 20 | pageNum: number; 21 | pageSize: number; 22 | orderBy: string; 23 | } 24 | 25 | export interface ProductDetailParams { 26 | productId: number; 27 | } 28 | 29 | /** 30 | * 检查用户登录状态 31 | */ 32 | export const checkLogin = async () => HTTP.post("/user/get_user_info.do"); 33 | 34 | /** 35 | * 去登录 36 | */ 37 | export const userLogin = async (params: LoginParamsProps) => 38 | HTTP.post("/user/login.do", params); 39 | 40 | /** 41 | * 注册 42 | */ 43 | export const userRegister = async (params: RegisterParamsProps) => 44 | HTTP.post("/user/register.do", params); 45 | 46 | /** 47 | * 退出登录 48 | */ 49 | export const logout = async () => HTTP.post("/user/logout.do"); 50 | 51 | /** 52 | * 商品列表 53 | * keyword和category不能同时传,会报错500 54 | */ 55 | export const getProductList = (params: ProductListParams) => 56 | HTTP.post("/product/list.do", params); 57 | 58 | /** 59 | * 商品详情 60 | */ 61 | export const getProductDetail = (params: ProductDetailParams) => 62 | HTTP.post("/product/detail.do", params); 63 | -------------------------------------------------------------------------------- /src/assets/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import './main.scss'; 2 | @import './reset.scss'; 3 | @import './mixin.scss'; -------------------------------------------------------------------------------- /src/assets/styles/main.scss: -------------------------------------------------------------------------------- 1 | body { 2 | user-select: none; 3 | } 4 | 5 | img { 6 | vertical-align: middle; 7 | } 8 | 9 | #app { 10 | width: 100%; 11 | height: 100vh; 12 | display: flex; 13 | flex-direction: column; 14 | 15 | .page-container { 16 | width: 100%; 17 | height: 100%; 18 | background-color: #f7f7f7; 19 | overflow: scroll; 20 | } 21 | } 22 | 23 | .single-omit { 24 | overflow: hidden; 25 | text-overflow: ellipsis; 26 | white-space: nowrap; 27 | } 28 | 29 | .ele-overflow-hidden { 30 | overflow: hidden !important; 31 | } -------------------------------------------------------------------------------- /src/assets/styles/mixin.scss: -------------------------------------------------------------------------------- 1 | $orange: #FF6B01; 2 | $red: #F63515; 3 | $bc: #F7F7F7; 4 | $fc:#fff; 5 | 6 | @mixin border-top-1px($color){ 7 | &:before{ 8 | border-top: 1px solid $color; 9 | } 10 | } 11 | @mixin border-right-1px($color){ 12 | &:before{ 13 | border-right: 1px solid $color; 14 | } 15 | } 16 | @mixin border-bottom-1px($color){ 17 | &:before{ 18 | border-bottom: 1px solid $color; 19 | } 20 | } 21 | @mixin border-left-1px($color){ 22 | &:before{ 23 | border-left: 1px solid $color; 24 | } 25 | } 26 | @mixin border-1px($color){ 27 | position: relative; 28 | &:after{ 29 | content: ''; 30 | pointer-events: none; //解决iphone上的点击无效Bug 31 | position: absolute; 32 | left: 0; 33 | top: 0; 34 | transform-origin: 0 0; 35 | border: 1px solid $color; 36 | } 37 | } 38 | 39 | //圆角 40 | @mixin borderRadius($radius) { 41 | -webkit-border-radius: $radius; 42 | -moz-border-radius: $radius; 43 | -ms-border-radius: $radius; 44 | -o-border-radius: $radius; 45 | border-radius: $radius; 46 | } 47 | 48 | //1px底部边框 49 | @mixin border-1px($color){ 50 | position: relative; 51 | &:after{ 52 | display: block; 53 | position: absolute; 54 | left: 0; 55 | bottom: 0; 56 | width: 100%; 57 | border-top: 1px solid $color; 58 | content: ''; 59 | } 60 | } 61 | //定位全屏 62 | @mixin allcover{ 63 | position:absolute; 64 | top:0; 65 | right:0; 66 | } 67 | 68 | //定位上下左右居中 69 | @mixin center { 70 | position: absolute; 71 | top: 50%; 72 | left: 50%; 73 | transform: translate(-50%, -50%); 74 | } 75 | 76 | //定位上下居中 77 | @mixin ct { 78 | position: absolute; 79 | top: 50%; 80 | transform: translateY(-50%); 81 | } 82 | 83 | //定位左右居中 84 | @mixin cl { 85 | position: absolute; 86 | left: 50%; 87 | transform: translateX(-50%); 88 | } 89 | 90 | //宽高 91 | @mixin wh($width, $height){ 92 | width: $width; 93 | height: $height; 94 | } 95 | 96 | //字体大小、行高、字体 97 | @mixin font($size, $line-height, $family: 'Microsoft YaHei') { 98 | font: #{$size}/#{$line-height} $family; 99 | } 100 | 101 | //字体大小,颜色 102 | @mixin sc($size, $color){ 103 | font-size: $size; 104 | color: $color; 105 | } 106 | 107 | @mixin boxSizing{ 108 | -webkit-box-sizing: border-box; 109 | -moz-box-sizing: border-box; 110 | box-sizing: border-box; 111 | } 112 | 113 | //flex 布局和 子元素 对其方式 114 | @mixin fj($type: space-between){ 115 | display: flex; 116 | justify-content: $type; 117 | } 118 | -------------------------------------------------------------------------------- /src/assets/styles/reset.scss: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, 7 | body, 8 | div, 9 | span, 10 | applet, 11 | object, 12 | iframe, 13 | h1, 14 | h2, 15 | h3, 16 | h4, 17 | h5, 18 | h6, 19 | p, 20 | blockquote, 21 | pre, 22 | a, 23 | abbr, 24 | acronym, 25 | address, 26 | big, 27 | cite, 28 | code, 29 | del, 30 | dfn, 31 | em, 32 | img, 33 | ins, 34 | kbd, 35 | q, 36 | s, 37 | samp, 38 | small, 39 | strike, 40 | strong, 41 | sub, 42 | sup, 43 | tt, 44 | var, 45 | b, 46 | u, 47 | i, 48 | center, 49 | dl, 50 | dt, 51 | dd, 52 | ol, 53 | ul, 54 | li, 55 | fieldset, 56 | form, 57 | label, 58 | legend, 59 | table, 60 | caption, 61 | tbody, 62 | tfoot, 63 | thead, 64 | tr, 65 | th, 66 | td, 67 | article, 68 | aside, 69 | canvas, 70 | details, 71 | embed, 72 | figure, 73 | figcaption, 74 | footer, 75 | header, 76 | hgroup, 77 | menu, 78 | nav, 79 | output, 80 | ruby, 81 | section, 82 | summary, 83 | time, 84 | mark, 85 | audio, 86 | video { 87 | margin: 0; 88 | padding: 0; 89 | border: 0; 90 | font-size: 100%; 91 | font: inherit; 92 | vertical-align: baseline; 93 | } 94 | 95 | /* HTML5 display-role reset for older browsers */ 96 | article, 97 | aside, 98 | details, 99 | figcaption, 100 | figure, 101 | footer, 102 | header, 103 | hgroup, 104 | menu, 105 | nav, 106 | section { 107 | display: block; 108 | } 109 | 110 | body { 111 | line-height: 1; 112 | } 113 | 114 | ol, 115 | ul { 116 | list-style: none; 117 | } 118 | 119 | blockquote, 120 | q { 121 | quotes: none; 122 | } 123 | 124 | blockquote:before, 125 | blockquote:after, 126 | q:before, 127 | q:after { 128 | content: ''; 129 | content: none; 130 | } 131 | 132 | table { 133 | border-collapse: collapse; 134 | border-spacing: 0; 135 | } 136 | 137 | input { 138 | border: none; 139 | outline: none; 140 | -webkit-appearance: none; 141 | -webkit-appearance: none; 142 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 143 | } 144 | 145 | textarea { 146 | border: none; 147 | outline: none; 148 | } 149 | 150 | button { 151 | border: none; 152 | outline: none; 153 | } 154 | 155 | a { 156 | text-decoration: none; 157 | color: #333; 158 | } 159 | 160 | li { 161 | list-style-type: none; 162 | } -------------------------------------------------------------------------------- /src/components/confirm/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 27 | 28 | -------------------------------------------------------------------------------- /src/components/menu/index.scss: -------------------------------------------------------------------------------- 1 | .nav-bar { 2 | width: 100%; 3 | padding: 10px 0; 4 | z-index: 10; 5 | background: #fff; 6 | transform: translateZ(0); 7 | -webkit-transform: translateZ(0); 8 | 9 | .nav-list { 10 | width: 100%; 11 | @include fj; 12 | flex-direction: row; 13 | padding: 0; 14 | 15 | .nav-list-item { 16 | display: flex; 17 | flex: 1; 18 | flex-direction: column; 19 | text-align: center; 20 | color: #666; 21 | 22 | &.active { 23 | color: $red; 24 | } 25 | 26 | i { 27 | text-align: center; 28 | font-size: 44px; 29 | } 30 | 31 | span { 32 | font-size: 24px; 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/components/menu/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 61 | 62 | -------------------------------------------------------------------------------- /src/components/modal/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 13 | 14 | 25 | -------------------------------------------------------------------------------- /src/components/page-head/assets/index.scss: -------------------------------------------------------------------------------- 1 | .page-header { 2 | width: 100%; 3 | height: 88px; 4 | padding: 0 20px; 5 | display: flex; 6 | justify-content: space-between; 7 | align-items: center; 8 | box-sizing: border-box; 9 | background-color: #fff; 10 | 11 | &.white { 12 | background: $red; 13 | } 14 | 15 | .left img { 16 | display: block; 17 | width: 40px; 18 | height: 40px; 19 | border: none; 20 | } 21 | 22 | .title { 23 | font-size: 36px; 24 | color: #272727; 25 | 26 | &.white { 27 | color: #fff; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /src/components/page-head/assets/left-normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/src/components/page-head/assets/left-normal.png -------------------------------------------------------------------------------- /src/components/page-head/assets/left-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/src/components/page-head/assets/left-white.png -------------------------------------------------------------------------------- /src/components/page-head/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 30 | 31 | -------------------------------------------------------------------------------- /src/components/product-item/index.scss: -------------------------------------------------------------------------------- 1 | .product-item { 2 | @include fj; 3 | width: 100%; 4 | height: 240px; 5 | padding: 20px 0; 6 | border-bottom: 1px solid #dcdcdc; 7 | 8 | img { 9 | width: 280px; 10 | height: 240px; 11 | padding: 0 20px; 12 | @include boxSizing; 13 | } 14 | 15 | .product-info { 16 | width: 56%; 17 | height: 240px; 18 | padding: 10px; 19 | @include boxSizing; 20 | 21 | .name { 22 | width: 100%; 23 | max-height: 80px; 24 | line-height: 40px; 25 | font-size: 30px; 26 | color: #333; 27 | overflow: hidden; 28 | } 29 | 30 | .subtitle { 31 | width: 100%; 32 | max-height: 40px; 33 | padding: 20px 0; 34 | line-height: 50px; 35 | font-size: 26px; 36 | color: #999; 37 | overflow: hidden; 38 | } 39 | 40 | .price { 41 | color: $red; 42 | font-size: 32px; 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /src/components/product-item/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 27 | 28 | -------------------------------------------------------------------------------- /src/components/product-item/product_default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/src/components/product-item/product_default.jpg -------------------------------------------------------------------------------- /src/components/scroll/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 45 | 46 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/src/config/index.ts -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import "./registerServiceWorker"; 4 | import router from "./router"; 5 | import store from "./store"; 6 | import "./assets/styles/index.scss"; 7 | 8 | Vue.config.productionTip = false; 9 | 10 | new Vue({ 11 | router, 12 | store, 13 | render: (h) => h(App), 14 | }).$mount("#app"); 15 | -------------------------------------------------------------------------------- /src/mock/data.ts: -------------------------------------------------------------------------------- 1 | export interface DataProps { 2 | headList: Array; 3 | categoryList: Array; 4 | floorList: Array 5 | } 6 | export const homeData = { 7 | headList: [ 8 | { 9 | imgUrl: 10 | "//m.360buyimg.com/mobilecms/s1125x690_jfs/t29188/301/133996293/200131/61f42a01/5be8eed6Nda6a18a5.jpg!cr_1125x549_0_72!q70.jpg.dpg", 11 | categoryId: 100008, 12 | }, 13 | { 14 | imgUrl: 15 | "//m.360buyimg.com/mobilecms/s1125x690_jfs/t30757/316/208746402/123953/9fa18794/5beb7d13Ne77e9f29.jpg!cr_1125x549_0_72!q70.jpg.dpg", 16 | categoryId: 100016, 17 | }, 18 | { 19 | imgUrl: 20 | "//m.360buyimg.com/mobilecms/jfs/t1/3926/29/4138/254748/5b9b646dE45cbeb7f/f80c8f7c24273bc1.jpg!cr_1125x549_0_72", 21 | categoryId: 100035, 22 | }, 23 | ], 24 | categoryList: [ 25 | { 26 | name: "M超市", 27 | imgUrl: 28 | "//m.360buyimg.com/mobilecms/jfs/t25534/207/1767774998/8085/523157d6/5bbc800fN502129b8.png", 29 | categoryId: 100002, 30 | }, 31 | { 32 | name: "全球购", 33 | imgUrl: 34 | "//m.360buyimg.com/mobilecms/jfs/t21658/347/221358098/7461/f86e6f74/5b03b170Nc9e0ec7c.png", 35 | categoryId: 100002, 36 | }, 37 | { 38 | name: "M时尚", 39 | imgUrl: 40 | "//m.360buyimg.com/mobilecms/jfs/t1/8385/17/3537/17895/5bd6ca67E09d23550/32d965fe9a9087a2.png", 41 | categoryId: 100002, 42 | }, 43 | { 44 | name: "M生鲜", 45 | imgUrl: 46 | "//m.360buyimg.com/mobilecms/jfs/t17725/156/1767366877/17404/f45d418b/5ad87bf0N66c5db7c.png", 47 | categoryId: 100002, 48 | }, 49 | { 50 | name: "M到家", 51 | imgUrl: 52 | "//m.360buyimg.com/mobilecms/jfs/t16990/157/2001547525/17770/a7b93378/5ae01befN2494769f.png", 53 | categoryId: 100002, 54 | }, 55 | { 56 | name: "充值缴费", 57 | imgUrl: 58 | "//m.360buyimg.com/mobilecms/jfs/t18454/342/2607665324/6406/273daced/5b03b74eN3541598d.png", 59 | categoryId: 100002, 60 | }, 61 | { 62 | name: "9.9元拼", 63 | imgUrl: 64 | "//m.360buyimg.com/mobilecms/jfs/t22228/270/207441984/11564/88140ab7/5b03fae3N67f78fe3.png", 65 | categoryId: 100002, 66 | }, 67 | { 68 | name: "领劵", 69 | imgUrl: 70 | "//m.360buyimg.com/mobilecms/jfs/t25258/200/2527038521/14019/3d7a8470/5be92494N557a0c5b.png", 71 | categoryId: 100002, 72 | }, 73 | { 74 | name: "省钱", 75 | imgUrl: 76 | "//m.360buyimg.com/mobilecms/jfs/t22120/183/200496447/5553/91ed22e0/5b03b7b8Ncea08c5b.png", 77 | categoryId: 100002, 78 | }, 79 | { 80 | name: "全部", 81 | imgUrl: 82 | "//m.360buyimg.com/mobilecms/jfs/t21481/263/412160889/15938/4246b4f8/5b0cea29N8fb2865f.png", 83 | categoryId: 100002, 84 | }, 85 | ], 86 | floorList: [ 87 | { 88 | headUrl: 89 | "//m.360buyimg.com/mobilecms/jfs/t15526/56/2344102455/23273/d8e15d5a/5aa0dbc1Nefd2bd2a.png!q70.jpg.dpg", 90 | list: [ 91 | { 92 | title: "玩3C", 93 | desc: "黑鲨新品大爆炸", 94 | products: [ 95 | { 96 | imgUrl: 97 | "//m.360buyimg.com/n1/jfs/t25144/2/1278469398/101844/cb66b21/5b90f5bfNcd58f18b.png!q70.jpg.dpg", 98 | categoryId: 100001, 99 | }, 100 | { 101 | imgUrl: 102 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t22792/274/2587775944/59292/3666f607/5b87bf15N9409ba0e.png!q70.jpg.dpg", 103 | categoryId: 100002, 104 | }, 105 | ], 106 | }, 107 | { 108 | title: "MMall家电", 109 | desc: "家电狂欢 京彩有你", 110 | products: [ 111 | { 112 | imgUrl: 113 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t22363/318/1484948466/11138/8317fc9b/5b2a528eN7c6bde83.jpg!q70.jpg.dpg", 114 | categoryId: 100003, 115 | }, 116 | { 117 | imgUrl: 118 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t22666/293/257815171/29274/6b5605b1/5b2a5295N5a81ad96.jpg!q70.jpg.dpg", 119 | categoryId: 100004, 120 | }, 121 | ], 122 | }, 123 | { 124 | title: "MMall超市", 125 | desc: "超市大放价 抢超值好货", 126 | products: [ 127 | { 128 | imgUrl: 129 | "//m.360buyimg.com/n1/jfs/t16852/170/2693122660/56143/28da8c2a/5b063c63N5746e4e4.jpg!q70.jpg.dpg", 130 | categoryId: 100005, 131 | }, 132 | { 133 | imgUrl: 134 | "//m.360buyimg.com/n1/jfs/t18877/293/2631103430/85245/f220b40d/5b063c6fN7820b399.jpg!q70.jpg.dpg", 135 | categoryId: 100006, 136 | }, 137 | ], 138 | }, 139 | { 140 | title: "爱家", 141 | desc: "家纺热卖 部分低至9.9元", 142 | products: [ 143 | { 144 | imgUrl: 145 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t15244/283/356547262/47743/b6607b0f/5a2a6d51N8830e8b8.jpg!q70.jpg.dpg", 146 | categoryId: 100007, 147 | }, 148 | { 149 | imgUrl: 150 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t16057/38/102057973/36179/b957e4ec/5a2a6d55N83655f7f.jpg!q70.jpg.dpg", 151 | categoryId: 100008, 152 | }, 153 | ], 154 | }, 155 | { 156 | title: "家有萌宝", 157 | desc: "低至五折", 158 | products: [ 159 | { 160 | imgUrl: 161 | "//m.360buyimg.com/n1/jfs/t17929/299/899794385/17291/d90318f9/5ab4c680N7d291625.jpg!q70.jpg.dpg", 162 | categoryId: 100009, 163 | }, 164 | { 165 | imgUrl: 166 | "//m.360buyimg.com/n1/jfs/t18802/262/2452746578/333259/5dcdfb06/5af3ffb4N0c700b57.jpg!q70.jpg.dpg", 167 | categoryId: 100010, 168 | }, 169 | ], 170 | }, 171 | { 172 | title: "爱吃", 173 | desc: "9.9包邮,夏日也生动", 174 | products: [ 175 | { 176 | imgUrl: 177 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t14473/103/2042876100/41836/766c1953/5a697f47Nc952ed7f.jpg!q70.jpg.dpg", 178 | categoryId: 100011, 179 | }, 180 | { 181 | imgUrl: 182 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t17155/227/2678303408/52223/f7a950c/5b0535d6Ne4073be7.jpg!q70.jpg.dpg", 183 | categoryId: 100012, 184 | }, 185 | ], 186 | }, 187 | ], 188 | }, 189 | { 190 | headUrl: 191 | "//m.360buyimg.com/mobilecms/jfs/t19381/275/717008577/26686/e58b7ef4/5aa23f27Nfa6d6be3.png!q70.jpg.dpg", 192 | list: [ 193 | { 194 | title: "智能家电馆", 195 | desc: "满减狂欢", 196 | products: [ 197 | { 198 | imgUrl: 199 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t25183/247/391394027/33684/8e1af9dc/5b6d048aNd7dae520.jpg!q70.jpg.dpg", 200 | categoryId: 100013, 201 | }, 202 | { 203 | imgUrl: 204 | "//m.360buyimg.com/mobilecms/s240x240_jfs/t1/839/25/11121/19615/5bccb3a0E4b504f77/e55c2fb826efe911.jpg!q70.jpg.dpg", 205 | categoryId: 100014, 206 | }, 207 | ], 208 | }, 209 | { 210 | title: "珠宝馆", 211 | desc: "满减优惠", 212 | products: [ 213 | { 214 | imgUrl: 215 | "//m.360buyimg.com/n1/jfs/t19009/357/347377157/13661/d0d9e5fb/5a6e8bb3Nd6182f9f.jpg!q70.jpg.dpg", 216 | categoryId: 100015, 217 | }, 218 | { 219 | imgUrl: 220 | "//m.360buyimg.com/n1/jfs/t27889/231/477152414/149859/7a5fb585/5baf1f58N45c2ca6c.jpg!q70.jpg.dpg", 221 | categoryId: 100016, 222 | }, 223 | ], 224 | }, 225 | { 226 | title: "鞋靴箱包", 227 | desc: "低至五折", 228 | products: [ 229 | { 230 | imgUrl: 231 | "//m.360buyimg.com/n1/jfs/t21469/359/769885083/29352/68865ed/5b178e49Nc5db7341.jpg!q70.jpg.dpg", 232 | categoryId: 100017, 233 | }, 234 | { 235 | imgUrl: 236 | "//m.360buyimg.com/mobilecms/jfs/t1/7091/11/2919/441267/5bd578bfE03e7166a/c5d5222c1802fd21.jpg!q70.jpg.dpg", 237 | categoryId: 100018, 238 | }, 239 | ], 240 | }, 241 | { 242 | title: "童装馆", 243 | desc: "春夏小萌娃", 244 | products: [ 245 | { 246 | imgUrl: 247 | "//m.360buyimg.com/n1/jfs/t20614/194/808562651/89118/894d41a5/5b18ba8dN855ebe44.png!q70.jpg.dpg", 248 | categoryId: 100019, 249 | }, 250 | { 251 | imgUrl: 252 | "//m.360buyimg.com/n1/jfs/t12460/183/2350810548/47292/f4c5485/5a914202Nfc1b8a12.png!q70.jpg.dpg", 253 | categoryId: 100020, 254 | }, 255 | ], 256 | }, 257 | ], 258 | }, 259 | ], 260 | }; 261 | export const categoryData = [ 262 | { 263 | name: "手机数码", 264 | mainImgUrl: 265 | "//img20.360buyimg.com/mcoss/jfs/t16273/143/46476745/45673/cba0840c/5a28ef10N82ab81d3.jpg", 266 | list: [ 267 | { 268 | title: "手机通讯", 269 | productList: [ 270 | { 271 | title: "手机", 272 | imgUrl: 273 | "//img10.360buyimg.com/focus/s140x140_jfs/t11503/241/2246064496/4783/cea2850e/5a169216N0701c7f1.jpg", 274 | }, 275 | { 276 | title: "全面屏手机", 277 | imgUrl: 278 | "//img30.360buyimg.com/focus/s140x140_jfs/t18955/187/1309277884/11517/fe100782/5ac48d27N3f5bb821.jpg", 279 | }, 280 | { 281 | title: "游戏手机", 282 | imgUrl: 283 | "//img11.360buyimg.com/focus/s140x140_jfs/t11470/45/2362968077/2689/fb36d9a0/5a169238Nc8f0882b.jpg", 284 | }, 285 | { 286 | title: "拍照手机", 287 | imgUrl: 288 | "//img20.360buyimg.com/focus/s140x140_jfs/t12022/66/917351804/2257/7ddc58e5/5a169232Ndf76f53c.jpg", 289 | }, 290 | { 291 | title: "老年机", 292 | imgUrl: 293 | "//img12.360buyimg.com/focus/s140x140_jfs/t11461/339/2354953633/8254/8c8c50d3/5a169217N5d1b842e.jpg", 294 | }, 295 | { 296 | title: "女性手机", 297 | imgUrl: 298 | "//img12.360buyimg.com/focus/s140x140_jfs/t15790/6/2311892256/2742/5ed77924/5a9fa728Nbff29ad2.jpg", 299 | }, 300 | ], 301 | }, 302 | { 303 | title: "手机配件", 304 | productList: [ 305 | { 306 | title: "数据线", 307 | imgUrl: 308 | "//img12.360buyimg.com/focus/s140x140_jfs/t18055/312/1342501458/9462/4699ed8a/5ac48672N11cf61fe.jpg", 309 | }, 310 | { 311 | title: "手机壳", 312 | imgUrl: 313 | "//img30.360buyimg.com/focus/s140x140_jfs/t18502/160/1284774717/9251/feb8a496/5ac4878cN658cbb07.jpg", 314 | }, 315 | { 316 | title: "充电宝", 317 | imgUrl: 318 | "//img30.360buyimg.com/focus/s140x140_jfs/t19537/23/1276961949/9676/f4b5be0d/5ac48791Nb224f939.jpg", 319 | }, 320 | { 321 | title: "手机贴膜", 322 | imgUrl: 323 | "//img11.360buyimg.com/focus/s140x140_jfs/t17548/288/1331085893/6458/52545456/5ac486c6N0c8a93dc.jpg", 324 | }, 325 | { 326 | title: "耳机", 327 | imgUrl: 328 | "//img10.360buyimg.com/focus/s140x140_jfs/t17284/353/1280266808/3696/32c00915/5ac486ccN2d8031c1.jpg", 329 | }, 330 | { 331 | title: "充电器", 332 | imgUrl: 333 | "//img11.360buyimg.com/focus/s140x140_jfs/t16774/124/1318736793/3884/cb658723/5ac4874fN6bc007b0.jpg", 334 | }, 335 | ], 336 | }, 337 | { 338 | title: "摄影摄像", 339 | productList: [ 340 | { 341 | title: "单反相机", 342 | imgUrl: 343 | "//img14.360buyimg.com/focus/s140x140_jfs/t13765/295/926307178/7966/3228af24/5a1679f2Nc2f659b6.jpg", 344 | }, 345 | { 346 | title: "数码相机", 347 | imgUrl: 348 | "//img20.360buyimg.com/focus/s140x140_jfs/t12814/251/897168610/8107/60a873f/5a1679caNada7f827.jpg", 349 | }, 350 | { 351 | title: "镜头", 352 | imgUrl: 353 | "//img30.360buyimg.com/focus/s140x140_jfs/t12154/164/880046972/2880/86b45b51/5a1679b9N42a5f8e5.jpg", 354 | }, 355 | { 356 | title: "户外器材", 357 | imgUrl: 358 | "//img10.360buyimg.com/focus/s140x140_jfs/t12586/176/939117172/2550/d16b4b6c/5a1679c3N67e2b3f1.jpg", 359 | }, 360 | ], 361 | }, 362 | ], 363 | }, 364 | { 365 | name: "电脑办公", 366 | mainImgUrl: 367 | "//img30.360buyimg.com/mcoss/jfs/t14743/292/279117506/48503/9b7d9f2c/5a28ea97N36cb4d16.jpg", 368 | list: [ 369 | { 370 | title: "热门分类", 371 | productList: [ 372 | { 373 | title: "轻薄本", 374 | imgUrl: 375 | "//img13.360buyimg.com/focus/s140x140_jfs/t11071/195/2462134264/9117/cd0688bf/5a17ba79N18b9f3d4.png", 376 | }, 377 | { 378 | title: "游戏本", 379 | imgUrl: 380 | "//img30.360buyimg.com/focus/s140x140_jfs/t11155/36/2330310765/10690/eb6754c3/5a17ba96N49561fea.png", 381 | }, 382 | { 383 | title: "机械键盘", 384 | imgUrl: 385 | "//img12.360buyimg.com/focus/s140x140_jfs/t12304/245/965858782/6481/37cb5a9b/5a17ba5aN0406a1b5.jpg", 386 | }, 387 | { 388 | title: "组装电脑", 389 | imgUrl: 390 | "//img10.360buyimg.com/focus/s140x140_jfs/t14101/325/978287093/8836/e142aa53/5a17ba73N07b12f0c.jpg", 391 | }, 392 | { 393 | title: "移动硬盘", 394 | imgUrl: 395 | "//img12.360buyimg.com/focus/s140x140_jfs/t13228/333/989759736/4764/2a312c2e/5a17ba7fN0740c051.jpg", 396 | }, 397 | { 398 | title: "曲屏显示器", 399 | imgUrl: 400 | "//img30.360buyimg.com/focus/s140x140_jfs/t13798/322/988648789/3644/1adc5615/5a17ba6dNafc95373.jpg", 401 | }, 402 | ], 403 | }, 404 | { 405 | title: "外设产品", 406 | productList: [ 407 | { 408 | title: "鼠标", 409 | imgUrl: 410 | "//img20.360buyimg.com/focus/s140x140_jfs/t11881/31/2355374158/3676/22da94de/5a16a5f0Nc6b32dda.jpg", 411 | }, 412 | { 413 | title: "U盘", 414 | imgUrl: 415 | "//img20.360buyimg.com/focus/s140x140_jfs/t12112/355/904591745/4308/6201dffe/5a16a5aaNdac2fe89.jpg", 416 | }, 417 | { 418 | title: "插座", 419 | imgUrl: 420 | "//img14.360buyimg.com/focus/s140x140_jfs/t12031/206/932335399/3567/d6d59ad9/5a16a578N283a0f75.jpg", 421 | }, 422 | { 423 | title: "电脑工具", 424 | imgUrl: 425 | "//img11.360buyimg.com/focus/s140x140_jfs/t12292/173/915309013/5554/78f4ab5e/5a16a560Nc7626d33.jpg", 426 | }, 427 | { 428 | title: "摄像头", 429 | imgUrl: 430 | "//img20.360buyimg.com/focus/s140x140_jfs/t12499/54/910206832/5998/f91002f8/5a16a588Nff477d9d.jpg", 431 | }, 432 | { 433 | title: "手写板", 434 | imgUrl: 435 | "//img13.360buyimg.com/focus/s140x140_jfs/t11734/189/2377033150/4145/b78bfcf/5a16a594Ncb41c95a.jpg", 436 | }, 437 | ], 438 | }, 439 | ], 440 | }, 441 | { 442 | name: "家用电器", 443 | mainImgUrl: "", 444 | list: [ 445 | { 446 | title: "家用电器", 447 | productList: [ 448 | { 449 | title: "电饭煲", 450 | imgUrl: 451 | "//img11.360buyimg.com/focus/s140x140_jfs/t14185/134/950962305/3800/eb1bafb8/5a17f224Nea1d3f59.jpg", 452 | }, 453 | { 454 | title: "豆浆机", 455 | imgUrl: 456 | "//img11.360buyimg.com/focus/s140x140_jfs/t14065/132/988058896/1688/99cd0a3d/5a17f229Nc4c681fb.jpg", 457 | }, 458 | { 459 | title: "冰箱", 460 | imgUrl: 461 | "//img12.360buyimg.com/focus/s140x140_jfs/t13153/44/964603695/1011/21d660d2/5a17f6aeN280056ea.jpg", 462 | }, 463 | { 464 | title: "双开门冰箱", 465 | imgUrl: 466 | "//img20.360buyimg.com/focus/s140x140_jfs/t13588/266/943842715/1088/c4ae40e4/5a17f6c5Ne56d7e26.jpg", 467 | }, 468 | { 469 | title: "冷柜", 470 | imgUrl: 471 | "//img12.360buyimg.com/focus/s140x140_jfs/t12928/273/1007467483/3549/52dad666/5a17f69eN1d10e257.jpg", 472 | }, 473 | { 474 | title: "洗衣机", 475 | imgUrl: 476 | "//img12.360buyimg.com/focus/s140x140_jfs/t12481/129/1018625238/1840/43d49869/5a17f6eaN9ec936de.jpg", 477 | }, 478 | { 479 | title: "电热水器", 480 | imgUrl: 481 | "//img13.360buyimg.com/focus/s140x140_jfs/t11053/246/2459202669/4318/fd6bd8d1/5a17f356Nd692ab74.jpg", 482 | }, 483 | { 484 | title: "电视", 485 | imgUrl: 486 | "//img12.360buyimg.com/focus/s140x140_jfs/t11842/356/2416901099/2164/ab77fbaa/5a17f71eN25360979.jpg", 487 | }, 488 | { 489 | title: "4K超清电视", 490 | imgUrl: 491 | "//img30.360buyimg.com/focus/s140x140_jfs/t11386/179/2470866031/2353/dfc7d933/5a17f72cN97075084.jpg", 492 | }, 493 | ], 494 | }, 495 | ], 496 | }, 497 | { 498 | name: "美妆护肤", 499 | mainImgUrl: "", 500 | list: [ 501 | { 502 | title: "美妆护肤", 503 | productList: [ 504 | { 505 | title: "美白", 506 | imgUrl: 507 | "//img30.360buyimg.com/focus/s140x140_jfs/t19531/110/2538137867/14848/c3ec84ac/5afd3cc5N8aa4b7c8.jpg", 508 | }, 509 | { 510 | title: "防晒", 511 | imgUrl: 512 | "//img30.360buyimg.com/focus/s140x140_jfs/t17560/320/2504739891/10347/f04150c4/5afd3cbeN77d00886.jpg", 513 | }, 514 | { 515 | title: "控油", 516 | imgUrl: 517 | "//img14.360buyimg.com/focus/s140x140_jfs/t19291/98/2577005836/12222/7107fb29/5afd3cc9N3add85ae.jpg", 518 | }, 519 | ], 520 | }, 521 | ], 522 | }, 523 | { 524 | name: "男装男鞋", 525 | mainImgUrl: "", 526 | list: [ 527 | { 528 | title: "男装男鞋", 529 | productList: [ 530 | { 531 | title: "夹克", 532 | imgUrl: 533 | "//img13.360buyimg.com/focus/s140x140_jfs/t12514/85/571462957/6214/29cdf637/5a0e9496Necd5bd0e.jpg", 534 | }, 535 | { 536 | title: "风衣", 537 | imgUrl: 538 | "//img11.360buyimg.com/focus/s140x140_jfs/t11590/82/2013872051/5874/83b5772d/5a0e947eN67f0e537.jpg", 539 | }, 540 | { 541 | title: "西服", 542 | imgUrl: 543 | "//img13.360buyimg.com/focus/s140x140_jfs/t13489/68/552491077/2495/7b517e4b/5a0e9483Na6231535.jpg", 544 | }, 545 | ], 546 | }, 547 | ], 548 | }, 549 | { 550 | name: "女装女鞋", 551 | mainImgUrl: "", 552 | list: [ 553 | { 554 | title: "家用电器", 555 | productList: [ 556 | { 557 | title: "风衣", 558 | imgUrl: 559 | "//img12.360buyimg.com/focus/s140x140_jfs/t15313/145/2476673176/2856/879136d7/5a9fc012N86f3fb22.jpg", 560 | }, 561 | { 562 | title: "长袖T恤", 563 | imgUrl: 564 | "//img13.360buyimg.com/focus/s140x140_jfs/t16750/238/698160610/5156/5b1f25b/5a9fbfccN574cba12.jpg", 565 | }, 566 | { 567 | title: "卫衣", 568 | imgUrl: 569 | "//img30.360buyimg.com/focus/s140x140_jfs/t11953/286/2195921828/5728/75b86d5b/5a127fbaN2780918c.jpg", 570 | }, 571 | ], 572 | }, 573 | ], 574 | }, 575 | { 576 | name: "母婴童装", 577 | mainImgUrl: "", 578 | list: [ 579 | { 580 | title: "母婴童装", 581 | productList: [ 582 | { 583 | title: "套装", 584 | imgUrl: 585 | "//img14.360buyimg.com/focus/s140x140_jfs/t18790/111/673564180/2396/3d27d79a/5a9f94c1N5b0c8724.jpg", 586 | }, 587 | ], 588 | }, 589 | ], 590 | }, 591 | { 592 | name: "图书音像", 593 | mainImgUrl: "", 594 | list: [ 595 | { 596 | title: "图书音像", 597 | productList: [ 598 | { 599 | title: "文学", 600 | imgUrl: 601 | "//img12.360buyimg.com/focus/s140x140_jfs/t19882/234/705045012/17917/e840651a/5b07dfddN7d635871.jpg", 602 | }, 603 | { 604 | title: "童书", 605 | imgUrl: 606 | "//img30.360buyimg.com/focus/s140x140_jfs/t18520/109/1790543750/23751/a98be11f/5ad82a1cN400c11d1.jpg", 607 | }, 608 | ], 609 | }, 610 | ], 611 | }, 612 | { 613 | name: "户外运动", 614 | mainImgUrl: "", 615 | list: [ 616 | { 617 | title: "户外运动", 618 | productList: [ 619 | { 620 | title: "跑步鞋", 621 | imgUrl: 622 | "//img30.360buyimg.com/focus/s140x140_jfs/t13993/246/156269250/8757/12386c/5a1fb5e7N12a676b6.jpg", 623 | }, 624 | { 625 | title: "体育用品", 626 | imgUrl: 627 | "//img13.360buyimg.com/focus/s140x140_jfs/t12313/239/1414060687/10293/ca8ad748/5a1fb5e1Nd80abbc2.jpg", 628 | }, 629 | ], 630 | }, 631 | ], 632 | }, 633 | { 634 | name: "食品生鲜", 635 | mainImgUrl: "", 636 | list: [ 637 | { 638 | title: "家用电器", 639 | productList: [ 640 | { 641 | title: "休闲零食", 642 | imgUrl: 643 | "//img10.360buyimg.com/focus/s140x140_jfs/t19789/191/2614150696/17735/d2db9aca/5b02411eNb44a03f7.jpg", 644 | }, 645 | { 646 | title: "牛奶", 647 | imgUrl: 648 | "//img13.360buyimg.com/focus/s140x140_jfs/t17479/1/2588472716/22795/d676e57a/5b024117N06151eba.jpg", 649 | }, 650 | ], 651 | }, 652 | ], 653 | }, 654 | { 655 | name: "酒水饮料", 656 | mainImgUrl: "", 657 | list: [ 658 | { 659 | title: "酒水饮料", 660 | productList: [ 661 | { 662 | title: "酒品馆", 663 | imgUrl: 664 | "//img14.360buyimg.com/focus/s140x140_jfs/t19432/178/2607825443/22589/446a22a2/5b023705N12de0824.jpg", 665 | }, 666 | ], 667 | }, 668 | ], 669 | }, 670 | { 671 | name: "家居家装", 672 | mainImgUrl: "", 673 | list: [ 674 | { 675 | title: "家居家装", 676 | productList: [ 677 | { 678 | title: "实木餐桌", 679 | imgUrl: 680 | "//img13.360buyimg.com/focus/s140x140_jfs/t12772/3/1793374412/9705/e633967a/5a28e6c9N8aaaea3f.jpg", 681 | }, 682 | { 683 | title: "椅子", 684 | imgUrl: 685 | "//img10.360buyimg.com/focus/s140x140_jfs/t13888/280/1737354934/3036/f94ecdf8/5a28e0d6N80f30096.jpg", 686 | }, 687 | ], 688 | }, 689 | ], 690 | }, 691 | { 692 | name: "箱包手袋", 693 | mainImgUrl: "", 694 | list: [ 695 | { 696 | title: "箱包手袋", 697 | productList: [ 698 | { 699 | title: "箱子", 700 | imgUrl: 701 | "//img20.360buyimg.com/focus/s140x140_jfs/t12364/153/835832532/6803/5b58b137/5a152fb3Nb9f571ea.jpg", 702 | }, 703 | ], 704 | }, 705 | ], 706 | }, 707 | { 708 | name: "钟表珠宝", 709 | mainImgUrl: "", 710 | list: [ 711 | { 712 | title: "钟表珠宝", 713 | productList: [ 714 | { 715 | title: "瑞士手表", 716 | imgUrl: 717 | "//img10.360buyimg.com/focus/s140x140_jfs/t14155/271/890296038/3841/f1adee7f/5a166fe1Nc6bd2f12.jpg", 718 | }, 719 | ], 720 | }, 721 | ], 722 | }, 723 | { 724 | name: "玩具乐器", 725 | mainImgUrl: "", 726 | list: [ 727 | { 728 | title: "家用电器", 729 | productList: [ 730 | { 731 | title: "遥控车", 732 | imgUrl: 733 | "//img12.360buyimg.com/focus/s140x140_jfs/t17575/123/2617650159/16720/74c14629/5b02a0c6N3f2770d9.jpg", 734 | }, 735 | ], 736 | }, 737 | ], 738 | }, 739 | ]; 740 | export const hotData = [ 741 | { 742 | title: "小米手机", 743 | hot: 1, 744 | }, 745 | { 746 | title: "笔记本", 747 | hot: 1, 748 | }, 749 | { 750 | title: "电脑", 751 | hot: 0, 752 | }, 753 | { 754 | title: "平板", 755 | hot: 0, 756 | }, 757 | { 758 | title: "液晶电视", 759 | hot: 1, 760 | }, 761 | { 762 | title: "家电", 763 | hot: 0, 764 | }, 765 | { 766 | title: "玩具", 767 | hot: 0, 768 | }, 769 | ]; 770 | -------------------------------------------------------------------------------- /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( 27 | "No internet connection found. App is running in offline mode." 28 | ); 29 | }, 30 | error(error) { 31 | console.error("Error during service worker registration:", error); 32 | } 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/router/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter, { RouteConfig } from "vue-router"; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const routes: Array = [ 7 | { 8 | path: "/", 9 | name: "Home", 10 | component: () => 11 | import(/* webpackChunkName: "home" */ "../views/home/index.vue"), 12 | meta: { 13 | title: "首页", 14 | headColor: "white", 15 | }, 16 | }, 17 | { 18 | path: "/login", 19 | name: "Login", 20 | component: () => 21 | import(/* webpackChunkName: "login" */ "../views/login/index.vue"), 22 | meta: { 23 | title: "登录", 24 | }, 25 | }, 26 | { 27 | path: "/register", 28 | name: "Register", 29 | component: () => 30 | import(/* webpackChunkName: "register" */ "../views/register/index.vue"), 31 | meta: { 32 | title: "注册", 33 | }, 34 | }, 35 | { 36 | path: "/user", 37 | name: "User", 38 | component: () => 39 | import(/* webpackChunkName: "user" */ "../views/user/index.vue"), 40 | meta: { 41 | title: "个人中心", 42 | }, 43 | }, 44 | { 45 | path: "/profile", 46 | name: "Profile", 47 | component: () => 48 | import(/* webpackChunkName: "profile" */ "../views/profile/index.vue"), 49 | meta: { 50 | title: "账号管理", 51 | }, 52 | }, 53 | { 54 | path: "/product-list", 55 | name: "ProductList", 56 | component: () => 57 | import( 58 | /* webpackChunkName: "product-list" */ "../views/product-list/index.vue" 59 | ), 60 | meta: { 61 | isHidden: true, 62 | }, 63 | }, 64 | ]; 65 | 66 | const router = new VueRouter({ 67 | mode: "history", 68 | base: process.env.BASE_URL, 69 | routes, 70 | }); 71 | 72 | export default router; 73 | -------------------------------------------------------------------------------- /src/shims-tsx.d.ts: -------------------------------------------------------------------------------- 1 | import Vue, { VNode } from "vue"; 2 | 3 | declare global { 4 | namespace JSX { 5 | // tslint:disable no-empty-interface 6 | interface Element extends VNode {} 7 | // tslint:disable no-empty-interface 8 | interface ElementClass extends Vue {} 9 | interface IntrinsicElements { 10 | [elem: string]: any; 11 | } 12 | } 13 | interface Window { 14 | toLogin: () => void, 15 | router: any 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /src/store/home/index.ts: -------------------------------------------------------------------------------- 1 | import { Commit } from "vuex"; 2 | import { HomeState, UserInfoProps } from "./interface"; 3 | import { 4 | checkLogin, 5 | userLogin, 6 | LoginParamsProps, 7 | RegisterParamsProps, 8 | userRegister, 9 | logout 10 | } from "../../api/index"; 11 | 12 | interface RequestDataProps { 13 | status: number; 14 | msg?: string; 15 | data?: any; 16 | } 17 | 18 | const state: HomeState = { 19 | loginStatus: false, 20 | userInfo: { 21 | answer: "", 22 | createTime: 0, 23 | email: "", 24 | id: 0, 25 | password: "", 26 | phone: "", 27 | question: "", 28 | role: 0, 29 | updateTime: 0, 30 | username: "", 31 | }, 32 | }; 33 | 34 | const mutations = { 35 | setStatus(state: HomeState, status: boolean) { 36 | state.loginStatus = status; 37 | }, 38 | setUserInfo(state: HomeState, info: UserInfoProps) { 39 | state.userInfo = info; 40 | }, 41 | }; 42 | 43 | const getters = { 44 | loginStatus: (state: HomeState) => state.loginStatus, 45 | userInfo: (state: HomeState) => state.userInfo, 46 | }; 47 | 48 | const actions = { 49 | // 判断用户登录状态 50 | async judgeLoginStatus({ commit }: { commit: Commit }) { 51 | const requestData: RequestDataProps = await checkLogin(); 52 | commit("setStatus", requestData.status === 0); 53 | commit("setUserInfo", requestData.data); 54 | }, 55 | 56 | // 去登陆 57 | async toLogin({ commit }: { commit: Commit }, info: LoginParamsProps) { 58 | const requestData: RequestDataProps = await userLogin(info); 59 | commit("setUserInfo", requestData.data); 60 | if (requestData.status === 0) { 61 | window.router.back(); 62 | } else { 63 | alert(requestData.msg); 64 | } 65 | }, 66 | 67 | // 注册 68 | async toRegister({ commit }: { commit: Commit }, info: RegisterParamsProps) { 69 | const requestData: RequestDataProps = await userRegister(info); 70 | if (requestData.status === 0) { 71 | window.router.back(); 72 | } else { 73 | alert(requestData.msg); 74 | } 75 | }, 76 | 77 | // 退出登录 78 | async toLogout({commit}: {commit: Commit}) { 79 | const requestData: RequestDataProps = await logout(); 80 | if(requestData.status === 0) { 81 | commit('setStatus', false); 82 | window.router.push({name: 'Home'}); 83 | } 84 | } 85 | }; 86 | 87 | export default { 88 | state, 89 | getters, 90 | mutations, 91 | actions, 92 | }; 93 | -------------------------------------------------------------------------------- /src/store/home/interface.ts: -------------------------------------------------------------------------------- 1 | export interface HomeState { 2 | loginStatus: boolean; 3 | userInfo: UserInfoProps; 4 | } 5 | 6 | export interface UserInfoProps { 7 | answer: string; 8 | createTime: number; 9 | email: string; 10 | id: number; 11 | password: string; 12 | phone: string; 13 | question: string; 14 | role: number; 15 | updateTime: number; 16 | username: string; 17 | } 18 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import homeModule from "./home/index"; 4 | import productModule from './product/index'; 5 | 6 | Vue.use(Vuex); 7 | 8 | export default new Vuex.Store({ 9 | state: { 10 | ...homeModule.state, 11 | ...productModule.state 12 | }, 13 | mutations: { 14 | ...homeModule.mutations, 15 | ...productModule.mutations 16 | }, 17 | actions: { 18 | ...homeModule.actions, 19 | ...productModule.actions 20 | }, 21 | modules: {} 22 | }); 23 | -------------------------------------------------------------------------------- /src/store/product/index.ts: -------------------------------------------------------------------------------- 1 | import { Commit } from "vuex"; 2 | import { 3 | getProductList, 4 | ProductListParams, 5 | getProductDetail, 6 | } from "@/api/index"; 7 | import { 8 | ProductState, 9 | ProductItemProps, 10 | ProductDetailProps, 11 | } from "./interface"; 12 | 13 | const state: ProductState = { 14 | productList: [], 15 | currentProduct: { 16 | categoryId: 0, 17 | createTime: "", 18 | detail: "", 19 | id: 0, 20 | imageHost: "", 21 | mainImage: "", 22 | name: "", 23 | parentCategoryId: 0, 24 | price: 0, 25 | status: 0, 26 | stock: 0, 27 | subImages: "", 28 | subtitle: "", 29 | updateTime: "", 30 | }, 31 | detailShow: false, 32 | }; 33 | 34 | const mutations = { 35 | setProductList(state: ProductState, list: Array) { 36 | state.productList = list; 37 | }, 38 | setProductInfo(state: ProductState, info: ProductDetailProps) { 39 | state.currentProduct = info; 40 | }, 41 | setDetailShow(state: ProductState, bool: boolean) { 42 | state.detailShow = bool; 43 | }, 44 | }; 45 | 46 | const getters = { 47 | productList: (state: ProductState) => state.productList, 48 | }; 49 | 50 | const actions = { 51 | // 获取商品列表 52 | async fetchProductList( 53 | { state, commit }: { state: any; commit: Commit }, 54 | { params, loadMore }: { params: ProductListParams; loadMore?: boolean } 55 | ) { 56 | const requestData = await getProductList(params); 57 | const currentData = loadMore 58 | ? state.productList.concat(requestData.data.list) 59 | : requestData.data.list; 60 | commit("setProductList", currentData); 61 | }, 62 | 63 | // 获取商品详情 64 | async fetchProductInfo({ commit }: { commit: Commit }, productId: number) { 65 | const requestData = await getProductDetail({ productId }); 66 | commit("setDetailShow", true); 67 | commit("setProductInfo", requestData.data); 68 | }, 69 | }; 70 | 71 | export default { 72 | state, 73 | mutations, 74 | getters, 75 | actions, 76 | }; 77 | -------------------------------------------------------------------------------- /src/store/product/interface.ts: -------------------------------------------------------------------------------- 1 | export interface ProductItemProps { 2 | categoryId?: number; 3 | id: number; 4 | status: number; 5 | imageHost: string; 6 | mainImage: string; 7 | name: string; 8 | subtitle: string; 9 | price: number; 10 | } 11 | 12 | export interface ProductDetailProps { 13 | categoryId: number; 14 | createTime: string; 15 | detail: string; 16 | id: number; 17 | imageHost: string; 18 | mainImage: string; 19 | name: string; 20 | parentCategoryId: number; 21 | price: number; 22 | status: number; 23 | stock: number; 24 | subImages: string; 25 | subtitle: string; 26 | updateTime: string; 27 | } 28 | 29 | export interface ProductState { 30 | productList: Array; 31 | currentProduct: ProductDetailProps; 32 | detailShow: boolean; 33 | } 34 | -------------------------------------------------------------------------------- /src/utils/http.ts: -------------------------------------------------------------------------------- 1 | import qs from "qs"; 2 | import Axios from "axios"; 3 | import Vue from "vue"; 4 | 5 | // API请求配置 6 | const HTTP = Axios.create({ 7 | baseURL: "", 8 | headers: { 9 | "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8" 10 | }, 11 | timeout: 30000, 12 | withCredentials: false 13 | }); 14 | 15 | // 全局请求发送信息拦截 16 | HTTP.interceptors.request.use( 17 | config => { 18 | if (config.method === "post") { 19 | // if (!config.data.token) { 20 | // config.data.token = window.localStorage.getItem('token'); 21 | // } 22 | config.data = qs.stringify(config.data); 23 | } 24 | return config; 25 | }, 26 | error => { 27 | throw new Error(error); 28 | } 29 | ); 30 | 31 | // 全局请求返回信息拦截 32 | HTTP.interceptors.response.use( 33 | response => { 34 | const data = response.data; 35 | // 未登录or登录超时 36 | // if(data.status == 10) { 37 | // console.log('用户未登录或已超时,请先登录!'); 38 | // window.toLogin && window.toLogin(); 39 | // } 40 | return data; 41 | }, 42 | error => { 43 | if (Vue.prototype.$loading) { 44 | Vue.prototype.$loading.close(); 45 | } 46 | throw new Error(error); 47 | } 48 | ); 49 | 50 | export default HTTP; 51 | -------------------------------------------------------------------------------- /src/views/home/components/floor/index.scss: -------------------------------------------------------------------------------- 1 | .home-header { 2 | position: fixed; 3 | left: 0; 4 | top: 0; 5 | @include fj; 6 | width: 100%; 7 | height: 100px; 8 | line-height: 100px; 9 | padding: 0 30px; 10 | @include boxSizing; 11 | font-size: 30px; 12 | color: #fff; 13 | z-index: 10000; 14 | 15 | &.active { 16 | background: $red; 17 | } 18 | 19 | .icon-caidan { 20 | font-size: 50px; 21 | } 22 | 23 | .header-search { 24 | display: flex; 25 | width: 74%; 26 | height: 40px; 27 | line-height: 40px; 28 | margin: 20px 0; 29 | padding: 10px 0; 30 | color: #232326; 31 | background: #fff; 32 | @include borderRadius(40px); 33 | 34 | .app-name { 35 | padding: 0 20px; 36 | color: $red; 37 | font-size: 40px; 38 | font-weight: bold; 39 | border-right: 1px solid #666; 40 | } 41 | 42 | .icon-search { 43 | padding: 0 20px; 44 | font-size: 34px; 45 | } 46 | 47 | .search-title { 48 | font-size: 24px; 49 | color: #666; 50 | } 51 | } 52 | 53 | .icon-iconyonghu { 54 | font-size: 44px; 55 | } 56 | } 57 | 58 | .home-wrap { 59 | width: 100%; 60 | height: 100%; 61 | overflow-y: scroll; 62 | } 63 | 64 | .swiper-container .swiper_img { 65 | height: 400px; 66 | } 67 | 68 | .category-list { 69 | display: flex; 70 | flex-shrink: 0; 71 | flex-wrap: wrap; 72 | width: 100%; 73 | padding-bottom: 26px; 74 | 75 | div { 76 | display: flex; 77 | flex-direction: column; 78 | width: 20%; 79 | text-align: center; 80 | 81 | span { 82 | font-size: 28px; 83 | color: #999; 84 | } 85 | 86 | img { 87 | width: 80px; 88 | height: 80px; 89 | margin: 26px auto 16px auto; 90 | } 91 | } 92 | } 93 | 94 | .floor-list { 95 | width: 100%; 96 | 97 | .floor-head { 98 | width: 100%; 99 | height: 80px; 100 | background: #F6F6F6; 101 | } 102 | 103 | .floor-content { 104 | display: flex; 105 | flex-shrink: 0; 106 | flex-wrap: wrap; 107 | width: 100%; 108 | @include boxSizing; 109 | 110 | .floor-category { 111 | width: 50%; 112 | padding: 20px; 113 | border-right: 1px solid #f5f5f5; 114 | border-bottom: 1px solid #f5f5f5; 115 | @include boxSizing; 116 | 117 | &:nth-child(2n) { 118 | border-right: none; 119 | } 120 | 121 | p { 122 | font-size: 28px; 123 | color: #333; 124 | line-height: 34px; 125 | 126 | &:nth-child(2) { 127 | padding: 10px 0; 128 | font-size: 26px; 129 | color: $red; 130 | } 131 | } 132 | 133 | .floor-products { 134 | display: flex; 135 | justify-content: space-around; 136 | width: 100%; 137 | 138 | img { 139 | width: 130px; 140 | height: 130px; 141 | } 142 | } 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /src/views/home/components/floor/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/views/home/components/swiper/swiper.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 47 | 48 | 63 | -------------------------------------------------------------------------------- /src/views/home/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | -------------------------------------------------------------------------------- /src/views/login/index.scss: -------------------------------------------------------------------------------- 1 | .login-wrap { 2 | width: 100%; 3 | padding: 40px 60px 0 60px; 4 | box-sizing: border-box; 5 | 6 | .login-content { 7 | width: 100%; 8 | margin: 0 auto; 9 | } 10 | 11 | .login-text { 12 | @include fj; 13 | width: 100%; 14 | height: 60px; 15 | line-height: 60px; 16 | padding: 20px 0; 17 | margin-top: 20px; 18 | border-bottom: 1px solid #f5f5f5; 19 | 20 | .iconfont { 21 | font-size: 26px; 22 | color: #CCCCCC; 23 | 24 | &.eye { 25 | padding: 0 30px; 26 | font-size: 40px; 27 | border-right: 1px solid #f5f5f5; 28 | } 29 | } 30 | 31 | input { 32 | width: 100%; 33 | height: 100%; 34 | margin-right: 20px; 35 | line-height: 60px; 36 | color: #222; 37 | font-size: 32px; 38 | 39 | &.login-password { 40 | width: 50%; 41 | } 42 | } 43 | 44 | span { 45 | padding-left: 20px; 46 | font-size: 30px; 47 | } 48 | 49 | div { 50 | display: flex; 51 | } 52 | } 53 | } 54 | 55 | .login-error { 56 | width: 100%; 57 | height: 40px; 58 | line-height: 40px; 59 | padding: 30px 0; 60 | color: $red; 61 | font-size: 26px; 62 | } 63 | 64 | .login-button { 65 | width: 100%; 66 | height: 100px; 67 | text-align: center; 68 | line-height: 100px; 69 | color: #fff; 70 | font-size: 32px; 71 | background: rgba(246, 53, 21, .5); 72 | @include borderRadius(60px); 73 | 74 | &.active { 75 | background: rgb(246, 53, 21) 76 | } 77 | } 78 | 79 | .quick-nav { 80 | @include fj; 81 | padding: 40px 0; 82 | width: 100%; 83 | 84 | span { 85 | color: #999; 86 | font-size: 28px; 87 | } 88 | } 89 | 90 | .other-login { 91 | width: 100%; 92 | margin-top: 100px; 93 | 94 | .other-head { 95 | @include fj; 96 | 97 | i { 98 | flex: 1; 99 | height: 1px; 100 | margin-top: 18px; 101 | background: #dcdcdc; 102 | } 103 | 104 | span { 105 | flex: 1; 106 | text-align: center; 107 | font-size: 28px; 108 | color: #dcdcdc; 109 | } 110 | } 111 | 112 | .other-con { 113 | display: flex; 114 | width: 320px; 115 | padding: 40px 0; 116 | margin: 0 auto; 117 | 118 | .login-icon { 119 | display: flex; 120 | flex-direction: column; 121 | width: 96px; 122 | margin: 0 30px; 123 | text-align: center; 124 | 125 | .iconfont { 126 | width: 100%; 127 | height: 96px; 128 | line-height: 96px; 129 | font-size: 60px; 130 | color: #15B8F5; 131 | background: #E7F7FE; 132 | @include borderRadius(50%); 133 | 134 | &.icon-weixin1 { 135 | font-size: 50px; 136 | color: #09BB07; 137 | background: #E6F8E6; 138 | } 139 | } 140 | 141 | span { 142 | padding-top: 20px; 143 | color: #999; 144 | } 145 | } 146 | } 147 | 148 | p { 149 | margin-top: 20px; 150 | width: 100%; 151 | text-align: center; 152 | color: #999; 153 | 154 | a { 155 | color: #409eff; 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /src/views/login/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/views/product-detail/components/detail-con.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 71 | 72 | -------------------------------------------------------------------------------- /src/views/product-detail/components/head.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 42 | 43 | -------------------------------------------------------------------------------- /src/views/product-detail/index.scss: -------------------------------------------------------------------------------- 1 | .product-detail { 2 | display: flex; 3 | flex-direction: column; 4 | position: fixed; 5 | left: 0; 6 | top: 0; 7 | right: 0; 8 | bottom: 0; 9 | background-color: #fff; 10 | z-index: 20; 11 | overflow: hidden; 12 | 13 | .detail-nav { 14 | @include fj; 15 | width: 100%; 16 | height: 88px; 17 | padding: 0 20px; 18 | line-height: 88px; 19 | box-sizing: border-box; 20 | z-index: 1000; 21 | color: #252525; 22 | background: #fff; 23 | border-bottom: 1px solid #dcdcdc; 24 | 25 | i { 26 | font-size: 50px; 27 | color: #000; 28 | } 29 | 30 | div span { 31 | padding: 0 20px; 32 | font-size: 28px; 33 | 34 | &.active { 35 | color: $red; 36 | } 37 | 38 | .iconfont { 39 | padding-right: 8px; 40 | font-size: 28px; 41 | color: $red; 42 | } 43 | } 44 | } 45 | 46 | .detail-con { 47 | width: 100%; 48 | height: 100%; 49 | overflow-y: scroll; 50 | } 51 | 52 | .detail-slider img { 53 | height: 700px; 54 | } 55 | 56 | 57 | .detail-info { 58 | width: 100%; 59 | padding: 20px 30px; 60 | font-size: 30px; 61 | box-sizing: border-box; 62 | 63 | .detail-info-name { 64 | font-size: 40px; 65 | color: #333; 66 | } 67 | 68 | .detail-info-subtitle { 69 | padding: 10px 0; 70 | font-size: 28px; 71 | color: #999; 72 | } 73 | 74 | div { 75 | @include fj; 76 | padding: 10px 0; 77 | font-size: 32px; 78 | color: #999; 79 | 80 | .detail-info-price { 81 | color: $red; 82 | font-size: 44px; 83 | } 84 | } 85 | } 86 | 87 | .detail-content { 88 | width: 100%; 89 | 90 | .detail-gap { 91 | width: 100%; 92 | height: 20px; 93 | background: #eee; 94 | } 95 | 96 | ul { 97 | @include fj; 98 | width: 100%; 99 | margin: 20px 0; 100 | 101 | li { 102 | flex: 1; 103 | padding: 10px 0; 104 | text-align: center; 105 | font-size: 30px; 106 | border-right: 1px solid #999; 107 | box-sizing: border-box; 108 | 109 | &:last-child { 110 | border-right: none; 111 | } 112 | } 113 | } 114 | 115 | div { 116 | width: 100%; 117 | overflow: hidden; 118 | 119 | p { 120 | width: 100%; 121 | font-size: 40px; 122 | text-align: center; 123 | } 124 | 125 | img { 126 | width: 100%; 127 | } 128 | } 129 | } 130 | 131 | .detail-cart { 132 | @include fj; 133 | position: fixed; 134 | left: 0; 135 | bottom: 0; 136 | width: 100%; 137 | height: 100px; 138 | line-height: 100px; 139 | font-size: 30px; 140 | background: #FEFBF9; 141 | z-index: 1000; 142 | transform: translateZ(0); 143 | -webkit-transform: translateZ(0); 144 | @include boxSizing; 145 | 146 | .detail-cart-left { 147 | @include fj; 148 | width: 46%; 149 | padding: 0 30px; 150 | @include boxSizing; 151 | 152 | .like { 153 | width: 40%; 154 | display: flex; 155 | flex-direction: column; 156 | text-align: center; 157 | 158 | .iconfont { 159 | font-size: 34px; 160 | line-height: 60px; 161 | color: #000000; 162 | font-weight: bold; 163 | 164 | &.icon-love { 165 | color: $red; 166 | } 167 | } 168 | 169 | span { 170 | line-height: 30px; 171 | font-size: 26px; 172 | } 173 | } 174 | 175 | .cart { 176 | @extend .like; 177 | position: relative; 178 | 179 | .cart-num { 180 | position: absolute; 181 | right: 20px; 182 | top: 0; 183 | width: 30px; 184 | height: 30px; 185 | text-align: center; 186 | line-height: 30px; 187 | color: #fff; 188 | font-size: 22px; 189 | background: $red; 190 | @include borderRadius(50%); 191 | } 192 | 193 | .iconfont { 194 | font-size: 40px; 195 | font-weight: normal; 196 | } 197 | } 198 | } 199 | 200 | .detail-cart-right { 201 | width: 54%; 202 | 203 | button { 204 | width: 100%; 205 | height: 100px; 206 | color: #fff; 207 | font-size: 30px; 208 | background: $red; 209 | 210 | &:nth-child(1) { 211 | margin-right: -10px; 212 | background: rgba(246, 53, 21, .9); 213 | } 214 | } 215 | } 216 | } 217 | 218 | .modal { 219 | position: fixed; 220 | left: 0; 221 | top: 0; 222 | width: 100%; 223 | height: 100%; 224 | z-index: 1000; 225 | background: rgba(0, 0, 0, .6); 226 | } 227 | 228 | .cart-wrap { 229 | position: fixed; 230 | left: 0; 231 | bottom: 0; 232 | width: 100%; 233 | height: 60%; 234 | z-index: 99999999; 235 | 236 | .cart-content { 237 | position: absolute; 238 | left: 0; 239 | bottom: 0; 240 | width: 100%; 241 | height: 100%; 242 | font-size: 30px; 243 | background: #fff; 244 | 245 | .cart-head { 246 | display: flex; 247 | width: 100%; 248 | padding: 30px; 249 | @include boxSizing; 250 | 251 | img { 252 | width: 180px; 253 | height: 180px; 254 | margin-top: -60px; 255 | border: 1px solid #999; 256 | } 257 | 258 | div { 259 | display: flex; 260 | flex-direction: column; 261 | justify-content: space-between; 262 | width: 64%; 263 | margin-left: 20px; 264 | 265 | .price { 266 | display: block; 267 | font-size: 34px; 268 | color: $red; 269 | padding-bottom: 10px; 270 | } 271 | 272 | p { 273 | max-height: 40px; 274 | overflow: hidden; 275 | } 276 | } 277 | 278 | .iconfont { 279 | position: absolute; 280 | top: 30px; 281 | right: 30px; 282 | font-size: 28px; 283 | color: #dcdcdc; 284 | } 285 | } 286 | 287 | .cart-config { 288 | @extend .cart-head; 289 | padding: 0 30px 30px 30px; 290 | 291 | .subtitle { 292 | width: 100%; 293 | margin-left: 0; 294 | padding: 20px 0; 295 | 296 | span { 297 | &:first-child { 298 | color: #999; 299 | padding-bottom: 20px; 300 | } 301 | } 302 | } 303 | } 304 | 305 | .cart-count { 306 | @include fj; 307 | width: 100%; 308 | padding: 0 30px 30px 30px; 309 | @include boxSizing; 310 | 311 | .cart-quantity { 312 | @include fj; 313 | width: 210px; 314 | height: 60px; 315 | line-height: 60px; 316 | color: #999; 317 | background: #fff; 318 | 319 | span { 320 | width: 80px; 321 | height: 100%; 322 | text-align: center; 323 | line-height: 60px; 324 | background: #F7F7F7; 325 | } 326 | 327 | i { 328 | width: 60px; 329 | height: 100%; 330 | text-align: center; 331 | line-height: 60px; 332 | font-style: normal; 333 | font-size: 50px; 334 | background: #F7F7F7; 335 | 336 | &.active { 337 | color: #dcdcdc; 338 | } 339 | } 340 | } 341 | } 342 | 343 | .add-cart { 344 | position: absolute; 345 | left: 0; 346 | bottom: 0; 347 | width: 100%; 348 | height: 100px; 349 | text-align: center; 350 | line-height: 100px; 351 | color: #fff; 352 | font-size: 30px; 353 | background: $red; 354 | } 355 | } 356 | } 357 | 358 | .slide-up-enter-active, 359 | .slide-up-leave-active { 360 | transition: all 0.5s; 361 | } 362 | 363 | .slide-up-enter, 364 | .slide-up-leave-to { 365 | transform: translate3d(0, 100%, 0); 366 | } 367 | } -------------------------------------------------------------------------------- /src/views/product-detail/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | 33 | -------------------------------------------------------------------------------- /src/views/product-list/component/list-item/index.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rosen97/web-shop/93dfb95e2fd232ae46363f51f69fcbe25e6fb357/src/views/product-list/component/list-item/index.vue -------------------------------------------------------------------------------- /src/views/product-list/component/search-head/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/views/product-list/component/select-tab/index.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/views/product-list/index.scss: -------------------------------------------------------------------------------- 1 | .product-wrap { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | height: 100vh; 6 | overflow: hidden; 7 | } 8 | 9 | .category-header { 10 | @include fj; 11 | width: 100%; 12 | height: 100px; 13 | line-height: 100px; 14 | padding: 0 30px; 15 | @include boxSizing; 16 | font-size: 30px; 17 | color: #656771; 18 | z-index: 10; 19 | 20 | &.active { 21 | background: $red; 22 | } 23 | 24 | .icon-left { 25 | font-size: 50px; 26 | font-weight: bold; 27 | } 28 | 29 | .header-search { 30 | display: flex; 31 | width: 76%; 32 | height: 40px; 33 | line-height: 40px; 34 | margin: 20px 0; 35 | padding: 10px 0; 36 | color: #232326; 37 | background: #F7F7F7; 38 | @include borderRadius(40px); 39 | 40 | .icon-search { 41 | padding: 0 10px 0 40px; 42 | font-size: 34px; 43 | } 44 | 45 | .search-title { 46 | font-size: 24px; 47 | color: #666; 48 | background: #F7F7F7; 49 | } 50 | } 51 | 52 | .icon-More { 53 | font-size: 40px; 54 | } 55 | 56 | .search-btn { 57 | height: 70px; 58 | margin: 15px 0; 59 | line-height: 70px; 60 | padding: 0 20px; 61 | color: #fff; 62 | background: $red; 63 | @include borderRadius(10px); 64 | } 65 | } 66 | 67 | .select-menu { 68 | display: flex; 69 | justify-content: space-around; 70 | width: 100%; 71 | height: 100px; 72 | background: #fff; 73 | 74 | &.isFixed { 75 | position: fixed; 76 | left: 0; 77 | top: 0; 78 | } 79 | 80 | .select-item { 81 | flex: 1; 82 | height: 100%; 83 | text-align: center; 84 | line-height: 100px; 85 | font-size: 30px; 86 | border-top: 1px solid #dcdcdc; 87 | border-bottom: 1px solid #dcdcdc; 88 | 89 | &.active { 90 | color: $red; 91 | } 92 | 93 | .iconfont { 94 | &.icon-down1 { 95 | color: #999; 96 | padding-left: 10px; 97 | font-size: 22px; 98 | } 99 | 100 | &.icon-up1 { 101 | @extend .icon-down1; 102 | font-size: 34px; 103 | } 104 | 105 | &.icon-shaixuan { 106 | @extend .icon-down1; 107 | font-size: 28px; 108 | } 109 | 110 | &.active { 111 | color: $red; 112 | } 113 | } 114 | } 115 | } 116 | 117 | .loading { 118 | position: relative; 119 | width: 100%; 120 | height: 100%; 121 | 122 | img { 123 | position: absolute; 124 | left: 50%; 125 | top: 50%; 126 | width: 100px; 127 | height: 100px; 128 | transform: translate(-50%, -50%); 129 | } 130 | } 131 | 132 | .product-list { 133 | width: 100%; 134 | height: 100%; 135 | overflow-y: scroll; 136 | 137 | .product-item { 138 | @include fj; 139 | width: 100%; 140 | height: 240px; 141 | padding: 20px 0; 142 | border-bottom: 1px solid #dcdcdc; 143 | 144 | img { 145 | width: 280px; 146 | height: 240px; 147 | padding: 0 20px; 148 | @include boxSizing; 149 | } 150 | 151 | .product-info { 152 | width: 56%; 153 | height: 240px; 154 | padding: 10px; 155 | @include boxSizing; 156 | 157 | .name { 158 | width: 100%; 159 | max-height: 80px; 160 | line-height: 40px; 161 | font-size: 30px; 162 | color: #333; 163 | overflow: hidden; 164 | } 165 | 166 | .subtitle { 167 | width: 100%; 168 | max-height: 40px; 169 | padding: 20px 0; 170 | line-height: 50px; 171 | font-size: 26px; 172 | color: #999; 173 | overflow: hidden; 174 | } 175 | 176 | .price { 177 | color: $red; 178 | font-size: 32px; 179 | } 180 | } 181 | } 182 | } 183 | 184 | .fade-enter-active, 185 | .fade-leave-active { 186 | transition: opacity .5s; 187 | } 188 | 189 | .fade-enter, 190 | .fade-leave-to { 191 | opacity: 0; 192 | } -------------------------------------------------------------------------------- /src/views/product-list/index.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /src/views/profile/component/profile-foot/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /src/views/profile/component/profile-info/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/views/profile/index.scss: -------------------------------------------------------------------------------- 1 | .user-head { 2 | @include fj; 3 | width: 100%; 4 | height: 88px; 5 | padding: 0 20px; 6 | line-height: 88px; 7 | font-size: 30px; 8 | @include boxSizing; 9 | border-bottom: 1px solid #f7f7f7; 10 | 11 | .iconfont { 12 | font-size: 44px; 13 | } 14 | } 15 | 16 | .profile-content { 17 | width: 100%; 18 | 19 | .profile-title { 20 | width: 100%; 21 | padding: 30px; 22 | @include boxSizing; 23 | font-weight: normal; 24 | color: #999; 25 | border-bottom: 1px solid #f7f7f7; 26 | } 27 | 28 | .profile-item { 29 | @extend .profile-title; 30 | @include fj; 31 | color: #999; 32 | font-size: 30px; 33 | 34 | &.info { 35 | justify-content: left; 36 | 37 | img { 38 | width: 100px; 39 | height: 100px; 40 | @include borderRadius(50%); 41 | } 42 | 43 | div { 44 | display: flex; 45 | flex-direction: column; 46 | justify-content: space-between; 47 | padding-left: 30px; 48 | font-size: 30px; 49 | color: #666; 50 | 51 | span:last-child { 52 | color: #999; 53 | } 54 | } 55 | } 56 | 57 | .iconfont { 58 | font-size: 38px; 59 | } 60 | } 61 | } 62 | 63 | .profile-footer { 64 | width: 100%; 65 | margin-top: 200px; 66 | 67 | .footer-con { 68 | display: flex; 69 | width: 100%; 70 | 71 | div { 72 | flex: 1; 73 | height: 40px; 74 | text-align: center; 75 | line-height: 34px; 76 | font-size: 26px; 77 | color: #333; 78 | 79 | &.client { 80 | line-height: 50px; 81 | } 82 | 83 | &.logout { 84 | line-height: 44px; 85 | } 86 | 87 | i { 88 | font-style: normal; 89 | font-size: 30px; 90 | color: #999; 91 | 92 | &.iconfont { 93 | font-size: 44px; 94 | } 95 | } 96 | } 97 | } 98 | 99 | .mmall { 100 | width: 100%; 101 | margin-top: 140px; 102 | text-align: center; 103 | font-size: 50px; 104 | color: $red; 105 | } 106 | } -------------------------------------------------------------------------------- /src/views/profile/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/views/register/index.scss: -------------------------------------------------------------------------------- 1 | .register-page { 2 | width: 100%; 3 | padding: 0 30px; 4 | margin-top: 60px; 5 | box-sizing: border-box; 6 | 7 | .register-wrap { 8 | width: 100%; 9 | 10 | .register-text { 11 | @include fj; 12 | width: 100%; 13 | height: 60px; 14 | line-height: 60px; 15 | padding: 20px 0; 16 | margin-top: 20px; 17 | border-bottom: 1px solid #dcdcdc; 18 | 19 | .iconfont { 20 | font-size: 26px; 21 | color: #CCCCCC; 22 | 23 | &.eye { 24 | padding: 0 30px; 25 | font-size: 40px; 26 | border-right: 1px solid #dcdcdc; 27 | } 28 | } 29 | 30 | input { 31 | width: 100%; 32 | height: 100%; 33 | margin-right: 20px; 34 | line-height: 60px; 35 | color: #222; 36 | font-size: 32px; 37 | } 38 | 39 | span { 40 | padding-left: 20px; 41 | font-size: 30px; 42 | } 43 | 44 | div { 45 | display: flex; 46 | } 47 | } 48 | } 49 | } 50 | 51 | .register-error { 52 | width: 100%; 53 | height: 40px; 54 | line-height: 40px; 55 | padding: 30px 0; 56 | color: $red; 57 | font-size: 26px; 58 | } 59 | 60 | .register-button { 61 | width: 100%; 62 | height: 100px; 63 | text-align: center; 64 | line-height: 100px; 65 | color: #fff; 66 | font-size: 32px; 67 | background: rgba(246, 53, 21, .5); 68 | @include borderRadius(60px); 69 | 70 | &.active { 71 | background: rgb(246, 53, 21) 72 | } 73 | } 74 | 75 | .set-security { 76 | position: fixed; 77 | left: 0; 78 | top: 0; 79 | width: 100%; 80 | height: 100%; 81 | padding: 0 30px; 82 | z-index: 100; 83 | background: #fff; 84 | @include boxSizing; 85 | 86 | p { 87 | font-size: 30px; 88 | color: #999; 89 | padding-top: 30px; 90 | } 91 | 92 | .set-security-head { 93 | position: relative; 94 | width: 100%; 95 | height: 88px; 96 | text-align: center; 97 | line-height: 88px; 98 | font-size: 34px; 99 | @include boxSizing; 100 | 101 | .iconfont { 102 | position: absolute; 103 | left: 0; 104 | top: 0; 105 | font-size: 50px; 106 | font-weight: bold; 107 | } 108 | } 109 | } 110 | 111 | .slide-enter-active, 112 | .slide-leave-active { 113 | transition: all 0.5s; 114 | } 115 | 116 | .slide-enter, 117 | .slide-leave-to { 118 | transform: translate3d(100%, 0, 0); 119 | } -------------------------------------------------------------------------------- /src/views/register/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/views/user/components/head-info/index.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/views/user/components/tab-list/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/views/user/index.scss: -------------------------------------------------------------------------------- 1 | .user-wrap { 2 | background: #F7F7F7; 3 | 4 | .user-head { 5 | @include fj; 6 | width: 100%; 7 | height: 88px; 8 | padding: 0 20px; 9 | line-height: 88px; 10 | font-size: 30px; 11 | @include boxSizing; 12 | border-bottom: 1px solid #dcdcdc; 13 | 14 | .iconfont { 15 | font-size: 44px; 16 | } 17 | } 18 | 19 | .user-info { 20 | width: 94%; 21 | margin: 20px 3%; 22 | height: 230px; 23 | background: linear-gradient(90deg, #eb3c3c, #ff7459); 24 | box-shadow: 0 2px 5px rgba(255, 98, 98, .4); 25 | @include borderRadius(12px); 26 | 27 | .info { 28 | position: relative; 29 | display: flex; 30 | width: 100%; 31 | height: 100%; 32 | padding: 50px 30px; 33 | @include boxSizing; 34 | 35 | img { 36 | width: 120px; 37 | height: 120px; 38 | } 39 | 40 | div { 41 | display: flex; 42 | flex-direction: column; 43 | margin-left: 20px; 44 | line-height: 40px; 45 | font-size: 28px; 46 | color: #fff; 47 | 48 | .name { 49 | color: hsla(0, 0%, 100%, .7); 50 | font-size: 26px; 51 | padding: 5px 0; 52 | } 53 | 54 | span { 55 | &:nth-child(1) { 56 | color: #999; 57 | font-size: 26px; 58 | } 59 | } 60 | } 61 | 62 | .account-management { 63 | position: absolute; 64 | top: 20px; 65 | right: 40px; 66 | font-size: 26px; 67 | color: rgba(76, 0, 0, .7); 68 | 69 | .iconfont { 70 | padding-right: 10px; 71 | font-size: 28px; 72 | color: rgba(76, 0, 0, .7); 73 | } 74 | } 75 | } 76 | } 77 | 78 | .user-menu { 79 | display: flex; 80 | justify-content: space-around; 81 | width: 100%; 82 | height: 150px; 83 | background: #fff; 84 | 85 | .menu-item { 86 | display: flex; 87 | flex-direction: column; 88 | flex: 1; 89 | height: 100px; 90 | text-align: center; 91 | margin: 25px 0; 92 | 93 | .iconfont { 94 | font-size: 50px; 95 | color: #DD9E58; 96 | 97 | &.icon-money { 98 | font-size: 44px; 99 | padding-top: 6px; 100 | } 101 | 102 | &.icon-icon1 { 103 | @extend .icon-money; 104 | color: $red; 105 | } 106 | } 107 | 108 | span { 109 | padding-top: 10px; 110 | font-size: 24px; 111 | color: #000; 112 | } 113 | } 114 | } 115 | 116 | .user-fork { 117 | margin: 30px 0; 118 | @extend .user-menu; 119 | 120 | .fork-item { 121 | display: flex; 122 | flex-direction: column; 123 | flex: 1; 124 | height: 100px; 125 | margin: 30px 0; 126 | text-align: center; 127 | 128 | i { 129 | font-style: normal; 130 | font-weight: bold; 131 | padding: 10px 0; 132 | font-size: 28px; 133 | } 134 | } 135 | } 136 | 137 | .recommend-title { 138 | width: 100%; 139 | height: 90px; 140 | padding-left: 20px; 141 | line-height: 90px; 142 | background: #fff; 143 | @include boxSizing; 144 | } 145 | 146 | .recommend-list { 147 | display: flex; 148 | flex-shrink: 0; 149 | flex-wrap: wrap; 150 | width: 100%; 151 | margin-top: 30px; 152 | padding-bottom: 150px; 153 | @include boxSizing; 154 | background: #fff; 155 | 156 | .recommend-item { 157 | display: flex; 158 | flex-direction: column; 159 | width: 50%; 160 | padding: 20px; 161 | @include boxSizing; 162 | border-bottom: 1px solid #dcdcdc; 163 | 164 | &:nth-child(2n-1) { 165 | border-right: 1px solid #dcdcdc; 166 | } 167 | 168 | img { 169 | width: 100%; 170 | height: 290px; 171 | margin: 0 auto; 172 | } 173 | 174 | p { 175 | height: 60px; 176 | padding: 20px 0; 177 | font-size: 26px; 178 | line-height: 40px; 179 | color: #333; 180 | overflow: hidden; 181 | } 182 | 183 | i { 184 | font-style: normal; 185 | font-size: 32px; 186 | color: $red; 187 | } 188 | } 189 | } 190 | } -------------------------------------------------------------------------------- /src/views/user/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "strictPropertyInitialization": false, 4 | "target": "esnext", 5 | "module": "esnext", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "importHelpers": true, 9 | "moduleResolution": "node", 10 | "experimentalDecorators": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "sourceMap": true, 14 | "baseUrl": ".", 15 | "types": [ 16 | "webpack-env" 17 | ], 18 | "paths": { 19 | "@/*": [ 20 | "src/*" 21 | ] 22 | }, 23 | "lib": [ 24 | "esnext", 25 | "dom", 26 | "dom.iterable", 27 | "scripthost" 28 | ] 29 | }, 30 | "include": [ 31 | "src/**/*.ts", 32 | "src/**/*.tsx", 33 | "src/**/*.vue", 34 | "tests/**/*.ts", 35 | "tests/**/*.tsx" 36 | ], 37 | "exclude": [ 38 | "node_modules" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const CompressionPlugin = require('compression-webpack-plugin'); 4 | 5 | const isProduction = process.env.NODE_ENV !== 'development'; 6 | 7 | const setPath = url => path.resolve(__dirname, url); 8 | 9 | let url = ''; // 请求接口地址 10 | let buriedPointUrl = ''; // 埋点接口地址 11 | 12 | module.exports = { 13 | publicPath: '/', 14 | outputDir: 'dist', 15 | assetsDir: 'static', 16 | productionSourceMap: false, 17 | chainWebpack: (config) => { 18 | // 修复HMR 19 | config.resolve.symlinks(true); 20 | // 删除预加载 21 | config.plugins.delete('prefetch'); 22 | config.plugins.delete('preload'); 23 | }, 24 | configureWebpack: { 25 | entry: { 26 | app: './src/main.ts' 27 | }, 28 | plugins: [ 29 | new CompressionPlugin({ 30 | test: /\.js$|\.html$|.\css/, // 匹配文件名 31 | threshold: 10240, // 对超过10k的数据压缩 32 | deleteOriginalAssets: false, // 不删除源文件 33 | }), 34 | (compiler) => { 35 | compiler.hooks.done.tap('vue-cli-service build', (stats) => { 36 | // 打包时把配置文件抽成单独的json文件 37 | const userConfig = require(`./src/config/index.ts`); 38 | userConfig.url = { 39 | // 接口请求地址 40 | http: url, 41 | // 埋点请求地址 42 | buriedPoint: buriedPointUrl, 43 | }; 44 | userConfig.debug = !!process.env.npm_config_debug; 45 | try { 46 | fs.accessSync(setPath('dist'), fs.constants.F_OK); 47 | } catch (e) { 48 | fs.mkdirSync(setPath('dist')); 49 | } 50 | fs.writeFileSync( 51 | path.join(__dirname, 'dist', 'config.json'), 52 | JSON.stringify(userConfig, null, '\t'), 53 | 'utf8', 54 | ); 55 | }); 56 | } 57 | ] 58 | }, 59 | css: { 60 | loaderOptions: { 61 | sass: { 62 | prependData: `@import "src/assets/styles/index.scss";` 63 | } 64 | } 65 | }, 66 | // css: { 67 | // extract: isProduction, 68 | // sourceMap: false, 69 | // requireModuleExtension: false, 70 | // // 全局引入mixin.scss 71 | // loaderOptions: { 72 | // sass: { 73 | // prependData: `@import "src/assets/styles/index.scss";` 74 | // } 75 | // }, 76 | // }, 77 | parallel: require('os').cpus().length > 1, 78 | devServer: { 79 | port: 8008, 80 | open: false, 81 | compress: false, 82 | overlay: { 83 | warnings: false, 84 | errors: true, 85 | }, 86 | proxy: 'http://test.happymmall.com', 87 | }, 88 | }; --------------------------------------------------------------------------------