小米商城app端项目

小米商城

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'
import axios from 'axios'
import { Toast } from 'vant'

Vue.use(VueRouter)

const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}
const router = new VueRouter({
  routes: [
    {
      path: '/',
      redirect: '/index/content'
    },
    {
      path: '/index',
      name: 'Index',
      component: () => import('../views/Index'),
      children: [
        {
          path: 'content',
          name: 'Content',
          component: () => import('../views/Content'),
          meta: {
            title: '小米商城'
          }
        },
        {
          path: 'cart',
          name: 'Cart',
          component: () => import('../views/Cart'),
          beforeEnter: (to, from, next) => {
            axios.get("/user/detail", {
              params: {
                token: localStorage.getItem("miUserToken")
                  ? JSON.parse(localStorage.getItem("miUserToken")).token
                  : "",
              },
            }).then((res) => {
              if (res.data.code == 2000) {
                Toast(res.data.msg);
                router.app.$router.push("/login");
              } else {
                next()
              }
            })
          },
          meta: {
            title: '购物车'
          }
        },
        {
          path: 'home',
          name: 'Home',
          component: () => import('../views/Home'),
          meta: {
            title: '个人中心',
            keepAlive: true,
          },
        }
      ]
    },
    {
      path: '/detail',
      name: 'Detail',
      component: () => import('../views/Detail'),
      meta: {
        title: '商品详情'
      }
    },
    {
      path: '/login',
      name: 'Login',
      component: () => import('../views/Login'),
      meta: {
        title: '登录'
      }
    },
    {
      path: '/register',
      name: 'Register',
      component: () => import('../views/Register'),
      meta: {
        title: '注册'
      }
    }
  ]
})

router.beforeEach((to, from, next) => {
  to.meta.title ? document.title = to.meta.title : document.title = 'undefined'
  next()
})

export default router

store/index.js

import Vue from 'vue'
import Vuex from 'vuex'
import local from './local'

Vue.use(Vuex)
import { Toast } from 'vant'

export default new Vuex.Store({
  state: {
    cartArr: local.get('miCart'),
    allChecked: true
  },
  mutations: {
    add2Cart(state, obj) {
      obj.checked = true
      obj.date = new Date().getTime()
      let index = state.cartArr.findIndex(val => val.id == obj.id)
      if (index == -1) {
        obj.num = 1
        state.cartArr.push(obj)
      } else {
        state.cartArr[index].num < state.cartArr[index].stores ? state.cartArr[index] += 1 : Toast('没货了!')
      }
      local.set('miCart', state.cartArr)
    },
    checkAll(state, allChecked) {
      state.allChecked = allChecked
      state.cartArr.map(val => val.checked = allChecked)
      local.set('miCart', state.cartArr)
    },
    checkItem(state) {
      state.cartArr.findIndex(val => val.checked == false) == -1 ? state.allChecked = true : state.allChecked = false
      local.set('miCart', state.cartArr)
    },
    minusItem(state, id) {
      let index = state.cartArr.findIndex(val => val.id == id)
      state.cartArr[index].num > 1 ? state.cartArr[index].num -= 1 : Toast('如果不要请点删除')
      local.set('miCart', state.cartArr)
    },
    plusItem(state, id) {
      let index = state.cartArr.findIndex(val => val.id == id)
      state.cartArr[index].num < state.cartArr[index].stores ? state.cartArr[index].num += 1 : Toast('最大库存了')
      local.set('miCart', state.cartArr)
    },
    del(state, id) {
      state.cartArr.splice(state.cartArr.findIndex(val => val.id == id), 1)
      local.set('miCart', state.cartArr)
    }
  },
  actions: {
  },
  modules: {
  },
  getters: {
    totalPrice(state) {
      let sum = 0
      state.cartArr.map(val => val.checked == true ? sum += val.minPrice * val.num : sum)
      return sum
    }
  }
})

store/local.js

export default {
    get(key) {
        return localStorage.getItem(key) ? JSON.parse(localStorage.getItem(key)) : []
    },
    set(key, value) {
        localStorage.setItem(key, JSON.stringify(value))
    }
}

App.vue

<template>
  <div id="app">
    <router-view v-if="flag" />
  </div>
</template>

<script>
export default {
  // errorCaptured: (err, vm, info) => {
  //   return false;
  // },
  data() {
    return {
      flag: true,
    };
  },
  // mounted() {
  //   this.$http
  //     .get("/list")
  //     .then((res) => console.log(res));
  // },
  methods: {
    reload() {
      this.flag = false;
      this.$nextTick(() => {
        this.flag = true;
      });
    },
  },
  provide() {
    return {
      reload: this.reload,
    };
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  list-style: none;
}
</style>

main.js

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'

import Vant from 'vant'
import { Lazyload } from 'vant'
import 'vant/lib/index.css'

import axios from 'axios'
axios.defaults.baseURL = 'https://api.it120.cc/xiaochengxu'

Vue.prototype.$http = axios

Vue.use(Vant)
Vue.use(Lazyload)

Vue.config.productionTip = false

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

views/Cart.vue

<template>
  <div class="cart">
    <div>
      <input type="checkbox" :checked="allChecked" @change="checkAll" />
      <span>全选</span>
    </div>
    <div class="cart-content">
      <div class="cart-item" v-for="item in cartArr" :key="item.id">
        <input type="checkbox" v-model="item.checked" @change="checkItem" />
        <img :src="item.pic" alt="" />
        <div class="cart-item-text">
          <p>{{ item.name }}</p>
          <p>{{ item.minPrice }}</p>
          <p>
            <button @click="minusItem(item.id)">-</button>{{ item.num
            }}<button @click="plusItem(item.id)">+</button>
          </p>
        </div>
        <button @click="del(item.id)">删除</button>
      </div>
    </div>
    <van-submit-bar :price="totalPrice * 100" button-text="提交订单" />
  </div>
</template>

<script>
import { Dialog, Toast } from "vant";
import { mapState, mapMutations, mapGetters } from "vuex";
export default {
  computed: {
    ...mapState({
      cartArr: (state) => state.cartArr,
    }),
    ...mapState(["allChecked"]),
    ...mapGetters(["totalPrice"]),
  },
  methods: {
    checkAll(event) {
      this.$store.commit("checkAll", event.target.checked);
    },
    ...mapMutations(["checkItem", "minusItem", "plusItem"]),
    ...mapMutations({
      del: (commit, id) =>
        Dialog.confirm({ title: "确定删除" })
          .then(() => commit("del", id))
          .catch(() => {
            return true;
          }),
    }),
  },
};
</script>

<style scoped>
.cart {
  height: 100%;
  display: flex;
  flex-direction: column;
}
.cart-content {
  flex: 1;
  overflow: auto;
}
.van-submit-bar {
  position: static;
}
.cart-item {
  display: flex;
  justify-content: space-around;
  align-items: center;
  border: 1px solid rgb(255, 153, 0);
  margin: 10px 3px;
  height: 20vh;
}
.cart-item img {
  height: 100%;
  width: auto;
}
.cart-item-text {
  width: 30vw;
}
</style>

views/Content.vue

<template>
  <div class="content">
    <van-swipe :autoplay="3000">
      <van-swipe-item v-for="(item, index) in swiperArr" :key="index">
        <img
          class="img"
          v-lazy="item.picUrl"
          @click="$router.push('/detail?id=' + item.id)"
      /></van-swipe-item>
    </van-swipe>
    <van-grid :column-num="3">
      <van-grid-item
        v-for="value in 6"
        :key="value"
        icon="photo-o"
        text="文字"
      />
    </van-grid>
    <van-divider>推荐</van-divider>
    <van-card
      v-for="item in hotGoodsArr"
      :key="item.id"
      :num="item.stores"
      :tag="item.recommendStatusStr"
      :price="item.minPrice"
      :desc="item.characteristic"
      :title="item.name"
      :thumb="item.pic"
      :origin-price="item.originalPrice"
      @click="$router.push('/detail?id=' + item.id)"
    ></van-card>
    <van-divider>砍价商品</van-divider>
    <van-card
      v-for="item in haggleGoodsArr"
      :key="item.id"
      :num="item.stores"
      :tag="item.tags"
      :price="item.minPrice"
      :desc="item.characteristic"
      :title="item.name"
      :thumb="item.pic"
      :origin-price="item.originalPrice"
      @click="$router.push('/detail?id=' + item.id)"
    ></van-card>
    <van-divider>拼团商品</van-divider>
    <van-card
      v-for="item in groupGoodsArr"
      :key="item.id"
      :num="item.stores"
      :tag="item.recommendStatusStr"
      :price="item.minPrice"
      :desc="item.characteristic"
      :title="item.name"
      :thumb="item.pic"
      :origin-price="item.originalPrice"
      @click="$router.push('/detail?id=' + item.id)"
    ></van-card>
  </div>
</template>

<script>
export default {
  data() {
    return {
      swiperArr: [],
      hotGoodsArr: [],
      haggleGoodsArr: [],
      groupGoodsArr: [],
    };
  },
  mounted() {
    this.getSwipe();
    this.getHotGoods();
    this.getHaggleGoods();
    this.getGroupGoods();
  },
  methods: {
    async getSwipe() {
      this.swiperArr = (await this.$http.get("/banner/list")).data.data;
    },
    async getHotGoods() {
      this.hotGoodsArr = (
        await this.$http.get("/shop/goods/list", {
          params: {
            kanjia: false,
            pingtuan: false,
          },
        })
      ).data.data;
    },
    async getHaggleGoods() {
      this.haggleGoodsArr = (
        await this.$http.get("/shop/goods/list", {
          params: {
            kanjia: true,
            pingtuan: false,
          },
        })
      ).data.data;
    },
    async getGroupGoods() {
      this.groupGoodsArr = (
        await this.$http.get("/shop/goods/list", {
          params: {
            pingtuan: true,
            kanjia: false,
          },
        })
      ).data.data;
    },
  },
};
</script>

<style scoped>
.img {
  width: 100%;
}
</style>

views/Detail.vue

<template>
  <div>
    <van-nav-bar
      title="商品详情"
      left-text="返回"
      left-arrow
      @click-left="onClickLeft"
    ></van-nav-bar>
    <van-swipe :autoplay="3000">
      <van-swipe-item v-for="(image, index) in pics" :key="index"
        ><img v-lazy="image.pic"
      /></van-swipe-item>
    </van-swipe>
    <van-divider />
    <div v-if="basicInfo">
      <p>商品名称:{{ basicInfo.name }}</p>
      <p>商品价格:{{ basicInfo.minPrice }}</p>
      <p>上架时间:{{ basicInfo.dateAdd }}</p>
    </div>
    <van-divider />
    <div v-html="content" class="contentBox"></div>
    <div class="box"></div>
    <van-goods-action>
      <van-goods-action-icon icon="chat-o" text="客服"></van-goods-action-icon>
      <van-goods-action-icon
        icon="cart-o"
        text="购物车"
      ></van-goods-action-icon>
      <van-goods-action-icon icon="shop-o" text="店铺"></van-goods-action-icon>
      <van-goods-action-button
        type="warning"
        text="加入购物车"
        @click="add2Cart"
      ></van-goods-action-button>
    </van-goods-action>
  </div>
</template>

<script>
import { Toast } from "vant";
export default {
  data() {
    return {
      basicInfo: null,
      content: "",
      pics: [],
    };
  },
  mounted() {
    this.getInfo(this.$route.query.id);
  },
  methods: {
    onClickLeft() {
      this.$router.back();
    },
    async getInfo(id) {
      let { basicInfo, content, pics } = (
        await this.$http.get("/shop/goods/detail", { params: { id } })
      ).data.data;
      [this.basicInfo, this.content, this.pics] = [basicInfo, content, pics];
    },
    add2Cart() {
      Toast("已添加到购物车");
      let obj = {
        id: this.basicInfo.id,
        pic: this.basicInfo.pic,
        name: this.basicInfo.name,
        minPrice: this.basicInfo.minPrice,
        stores: this.basicInfo.stores,
      };
      this.$store.commit("add2Cart", obj);
      this.$router.push('/index/cart')
    },
  },
};
</script>

<style scoped>
>>> img {
  width: 100%;
  height: auto;
}
.box {
  height: 50px;
}
p {
  text-indent: 2em;
}
</style>

views/Home.vue

<template>
  <div>
    <header class="hd">
      <div class="user ui-flex align-center">
        <div class="img">
          <img src="https://m.mi.com/static/img/avatar.76a75b8f17.png" alt="" />
        </div>
        <div class="name">昵称:{{ nick }} <br />手机号:{{ mobile }}</div>
        <div class="userType">{{ isSeller ? "金牌卖家" : "金牌买家" }}</div>
      </div>
    </header>
    <van-cell-group>
      <van-cell title="用户ID" :value="id" />
      <van-cell title="注册时间" :value="dateAdd" />
      <van-cell title="注册方式" :value="sourceStr" />
      <van-cell title="银行卡号" :value="cardNumber" />
      <van-cell title="用户状态" :value="statusStr" />
    </van-cell-group>
  </div>
</template>

<script>
import { Toast, Cell, CellGroup } from "vant";
export default {
  data() {
    return {
      cardNumber: "",
      dateAdd: "",
      id: "",
      mobile: "",
      nick: "",
      sourceStr: "",
      statusStr: "",
      isSeller: "",
    };
  },
  mounted() {
    this.userInfo();
  },
  methods: {
    userInfo() {
      this.$http
        .get("/user/detail", {
          params: {
            token: localStorage.getItem("miUserToken")
              ? JSON.parse(localStorage.getItem("miUserToken")).token
              : "",
          },
        })
        .then((res) => {
          if (res.data.code == 2000) {
            Toast(res.data.msg);
            this.$router.push("/login");
          } else {
            let infoObj = res.data.data.base;
            this.nick = infoObj.nick;
            this.mobile = infoObj.mobile;
            this.id = infoObj.id;
            this.dateAdd = infoObj.dateAdd;
            this.cardNumber = infoObj.cardNumber;
            this.statusStr = infoObj.statusStr;
            this.sourceStr = infoObj.sourceStr;
            this.isSeller = infoObj.isSeller;
          }
        });
    },
  },
};
</script>

<style scoped>
.hd {
  background: url(https://m.mi.com/static/img/bg.63c8e19851.png) center 0
    #f37d0f;
  background-size: auto 100%;
  padding: 1em 0;
}
.align-center {
  align-items: center;
  -webkit-box-align: center;
}
.ui-flex {
  display: flex;
  justify-content: space-around;
}
.img {
  width: 4em;
  height: 4em;
  /* margin: 0 1em; */
  overflow: hidden;
  border-radius: 100%;
  border: 3px solid hsla(0, 0%, 100%, 0.4);
}
img {
  width: 100%;
}
.name a,
.name {
  margin-left: -3em;
  color: blanchedalmond;
}
.userType {
  color: rgb(255, 217, 0);
  font-weight: bold;
}
</style>

views/Index.vue

<template>
  <div class="index">
    <div class="index-head">
      <h2 @click="$router.push('/')">小米商城</h2>
    </div>
    <div class="index-content">
      <keep-alive>
        <router-view></router-view>
      </keep-alive>
    </div>
    <van-tabbar route>
      <van-tabbar-item to="/index/content" icon="home-o">首页</van-tabbar-item>
      <van-tabbar-item to="/index/cart" icon="shopping-cart-o"
        >购物车</van-tabbar-item
      >
      <van-tabbar-item to="/index/home" icon="manager-o">我的</van-tabbar-item>
    </van-tabbar>
  </div>
</template>

<script>
export default {};
</script>

<style scoped>
.index {
  height: 100vh;
  display: flex;
  flex-direction: column;
}
.index-content {
  flex: 1;
  overflow: auto;
}
.index-head > h2 {
  line-height: 6vh;
  color: white;
  background: rgb(253, 104, 33);
  text-align: center;
}
.van-tabbar--fixed {
  position: static;
}
</style>

views/Login.vue

<template>
  <div>
    <van-nav-bar title="登录" />
    <van-form @submit="onSubmit">
      <van-field
        v-model="mobile"
        name="mobile"
        label="手机号"
        placeholder="请输入手机号"
        :rules="[{ required: true, message: '请输入手机号' }]"
      />
      <van-field
        v-model="pwd"
        label="密码"
        name="pwd"
        type="password"
        placeholder="请输入密码"
        :rules="[{ required: true, message: '请输入密码' }]"
      />
      <div style="margin: 16px">
        <van-button round block type="info" native-type="submit"
          >登录</van-button
        >
        <router-link to="/register">我要注册</router-link>
      </div>
    </van-form>
  </div>
</template>

<script>
import qs from "qs";
import { Toast } from "vant";
export default {
  inject: ["reload"],
  data() {
    return {
      mobile: "",
      pwd: "",
    };
  },
  methods: {
    onSubmit(values) {
      values.deviceId = "0529";
      values.deviceName = "小米note3";
      let parmStr = qs.stringify(values);
      this.$http.post("/user/m/login", parmStr).then((res) => {
        if (res.data.code == 0) {
          Toast("登录成功");
          localStorage.setItem("miUserToken", JSON.stringify(res.data.data));
          this.$router.push("/index/home");
        } else {
          Toast(res.data.msg);
          this.reload();
        }
      });
    },
  },
};
</script>

<style scoped>
a {
  line-height: 3em;
  color: blue;
  font-size: 0.8em;
}
</style>

views/Register.vue

<template>
  <div>
    <van-nav-bar
      title="注册"
      left-text="返回"
      left-arrow
      @click-left="onClickLeft"
    />
    <van-form @submit="onSubmit">
      <van-field
        v-model="mobile"
        name="mobile"
        label="手机号"
        placeholder="请输入手机号"
        :rules="[{ required: true }]"
      />
      <van-field
        v-model="nick"
        label="用户名"
        name="nick"
        placeholder="请输入用户名"
        :rules="[{ required: true }]"
      />
      <van-field
        v-model="code"
        center
        clearable
        label="短信验证码"
        name="code"
        placeholder="请输入短信验证码"
      >
        <template #button>
          <van-button size="small" type="primary" @click.prevent="sendCode"
            >发送验证码</van-button
          >
        </template>
      </van-field>
      <van-field
        v-model="pwd"
        type="password"
        name="pwd"
        label="密码"
        placeholder="请输入密码"
        :rules="[{ required: true }]"
      />
      <van-field name="autoLogin" label="自动登录">
        <template #input>
          <van-checkbox v-model="autoLogin" shape="square" />
        </template>
      </van-field>
      <div style="margin: 16px">
        <van-button round block type="info" native-type="submit"
          >注册</van-button
        >
      </div>
    </van-form>
  </div>
</template>

<script>
import qs from "qs";
import { Toast } from "vant";
export default {
  inject: ["reload"],
  data() {
    return {
      mobile: "",
      pwd: "",
      nick: "",
      code: "",
      autoLogin: "",
    };
  },
  methods: {
    onSubmit(values) {
      let parmStr = qs.stringify(values);
      this.$http.post("/user/m/register", parmStr).then((res) => {
        if (res.data.code == 0) {
          Toast("注册成功");
          this.$router.push("/login");
        } else if (res.data.code == 10000) {
          Toast("用户已存在,请登录");
          this.$router.push("/login");
        } else {
          Toast(res.data.msg);
          this.reload();
        }
      });
    },
    onClickLeft() {
      this.$router.back();
    },
    sendCode() {
      this.mobile
        ? this.$http
            .get("/verification/sms/get", { params: { mobile: this.mobile } })
            .then((res) =>
              res.data.code === 0
                ? Toast("验证码发送成功")
                : Toast(res.data.msg)
            )
        : Toast("请输入手机号");
    },
  },
};
</script>

<style scoped>
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值