4. Project page — Product details page

Click through the home page and category page to enter the product details page

Obtain product details

GoodsListItem.vue >>

<template> <! -- Product list item --> <! --1-1Click on the route redirect with ID: @click="itemClick(product.id)" -->
  <div class="good-item" @click="itemClick">
    <img v-lazy="product.cover_url" alt="">
    <div class="goods-info">
      <p>{{product.title}}</p>
      <span class="price"><small>RMB</small>{{product.price}}</span>
      <span class="collect">{{product.collects_count}}</span>
    </div>
  </div>
</template>

<script >
  import {useRouter} from 'vue-router';  // 1-2 Import router


  export default {
    name: "GoodsListItem".props: {// Receive the value passed by the parent component
      product:Object.default(){
        return{}}},setup(props) {
      const router = useRouter() // 1-3 Declare the router

      return {
        itemClick: () = > {
          router.push({path:'/detail'.query: {id:props.product.id}}) // This page has id, upload here; The page is cached
          // console.log(props.product.id)}}}}</script>

<style scoped lang="scss">
  .good-item{
    width: 46%;
    padding-bottom: 40px;
    position: relative; // The inner layer is positioned relative to the outer layerimg{
      width: 100%;
      border-radius: 5%;

    }
    .goods-info{
      font-size: 12px;
      bottom: 5px;
      overflow: hidden;
      text-align: center;

      Centered / * * /
      position: absolute;
      left: 0;
      right: 0;


      p{
        overflow: hidden; // Hide the title out of scopetext-overflow: ellipsis; // Display text out of range...white-space: nowrap; // The text does not wrapmargin-bottom: 3px;

      }
      .price{
        color: darkred;
        margin-right: 20px;

      }
      .collect{
        position: relative;

      }
      .collect::before{
        center:' ';
        position: absolute;
        left: -15px;
        width: 14px;
        height: 14px;
        top: -1;
        background:url('~assets/images/collect.png')0 0 /14px 14px; }}}</style>
Copy the code

category.vue >>

<template>
  <div>
    <nav-bar>
      <template v-slot:default>分类详情</template>
    </nav-bar>
    <div id="mainbox">
      <!--6 不同排序方式-->
      <div class="ordertab">
        <van-tabs v-model="active" @click="tabClick">
          <van-tab title="销量排序"></van-tab>
          <van-tab title="价格排序"></van-tab>
          <van-tab title="评价排序"></van-tab>
        </van-tabs>

      </div>
      <!--5 折叠-->
      <van-sidebar class="leftmenu" v-model="activeKey">
        <van-collapse v-model="activeName" accordion><!--手风琴折叠-->
          <van-collapse-item
            v-for="item in categories" :key="item.id"
            :title="item.name"
            :name="item.name">

<!--            子菜单-->
          <van-sidebar-item
            v-for="sub in item.children"
            :title="sub.name"
            :key="sub.id"
            @click="getGoods(sub.id)"
          /><!--    4.变量categories.value中数据-->

          </van-collapse-item>
        </van-collapse>
      </van-sidebar>
<!--商品列表-->
      <div class="goodslist">
        <div class="content">
          <van-card
            v-for="item in showGoods" :key="item.id"
            @click="itemClick(item.id)"
            :num="item.comments_count"
            :tag="item.comments_count >=0 ? '流行':'标签'"
            :price="item.price"
            :desc="item.update_at"
            :title="item.title"
            :thumb="item.cover_url"
            :lazy-load="true"
          />

        </div>
      </div>
    </div>

    <back-top @bTop="bTop" v-show="isShowBackTop"></back-top>

  </div>
</template>

<script>
  import NavBar from "../../components/common/navbar/NavBar";
  import BackTop from "../../components/common/backtop/BackTop";// ️ 2-1 回到顶部
  import {useRouter} from 'vue-router'
  import {ref, reactive, onMounted, computed, watchEffect, nextTick} from 'vue';
  import {getCategory,getCategoryGoods} from "../../network/category";
  import BScroll from "better-scroll";


  export default {
    name: "Category",
    setup(){
      const router = useRouter()
      let active = ref(1)
      let activeKay = ref(0)
      let activeName = ref(1)
      //2.申明数组
      let categories = ref([])

      let isShowBackTop =ref(false)

      //8当前的排序条件
      let currentOrder = ref(['sales'])
      //11 当前的分类id
      let currentCid = ref(0)

      //12数据模型
      const goods = reactive({
        sales:{page:1,list:[]},
        price:{page:1,list:[]},
        comments_count:{page:1,list:[]}
      })
      //13响应式的,更新数据
      const showGoods = computed(()=>{
        return goods[currentOrder.value].list
      })

      //13 初始化数据,将接口里的数据放到数据模型中去
      const init =()=> {
        getCategoryGoods('sales',currentCid.value).then(res=>{
          goods.sales.list = res.goods.data
        })
        getCategoryGoods('price',currentCid.value).then(res=>{
          goods.price.list = res.goods.data
        })
        getCategoryGoods('comments_count',currentCid.value).then(res=>{
          goods.comments_count.list = res.goods.data
        })
      }

      let bscroll = reactive({}); //   声明在外层 共用

      onMounted(()=>{
        //1 获取分类
        getCategory().then(res=>{
          // console.log(res)
          // 3.获取res.categories的值,并赋给categories.value
          categories.value  = res.categories
          // console.log(categories.value)
        })
        //只需一个,因为默认就是「sales」
        getCategoryGoods('sales',currentCid.value).then(res=>{
          goods.sales.list = res.goods.data
        })

        //  6 创建 BetterScroll 对象
        bscroll = new BScroll(document.querySelector(".goodslist"), {
          // 获取到最外层元素
          probeType: 3, // 0,1,2,3, 3 只要在运行就触发 scroll 事件
          click: true, // 是否允许点击
          pullUpLoad: true, // 上拉加载更多,默认 false
        });
        //注册滚动事件
        bscroll.on('scroll',(position)=>{
         isShowBackTop.value = (-position.y)>30
        })
        // 10 上拉加载更多数据,触发 pullingUp
        bscroll.on("pullingUp", () => {
          console.log('上拉加载更多......')

          const pages = goods[currentOrder.value].page +1

          getCategoryGoods(currentOrder.value ,currentCid.value).then(res=>{
            goods[currentOrder.value].list.push(...res.goods.data)
            goods[currentOrder.value].page += 1

          })


          // 完成上拉,等数据请求完成,要将新数据展示出来
          bscroll.finishPullUp();

          //延迟效果
          nextTick(() => {
            // 当 DOM 渲染完了执行方法
            // 重新计算高度
            bscroll && bscroll.refresh();
          });

          // 刷新 重新计算高度
          bscroll.refresh();
          console.log('centerHeight:' + document.querySelector('.content').clientHeight)
          console.log('当前类型:' + currentType.value + ',当前页:' + page)
        });
      });


      //7 排序选项卡
      const tabClick = (index)=>{
        let orders = ['sales','price','comment_count']

        //9
        currentOrder.value = orders[index]
        //14 更新,重新排序
        getCategoryGoods(currentOrder.value ,currentCid.value).then(res=>{
          goods[currentOrder.value].list = res.goods.data

          //延迟效果
          nextTick(() => {
            // 当 DOM 渲染完了执行方法
            // 重新计算高度
            bscroll && bscroll.refresh();
          });
        })

        console.log('当前分类id'+ currentCid.value)
        console.log('排序的序号'+currentOrder.value)

      }
      //10 通过分类得到商品
      const getGoods = (cid) =>{
        currentCid.value = cid
        init()//重新赋值

        console.log('当前分类Id'+currentCid.value)
        console.log('排序的序号'+ currentCid.value)

      }

      //   监听 任何一个变量有变化就会被触发
      watchEffect(() => {
        nextTick(() => {
          // 当 DOM 渲染完了执行方法
          // 重新计算高度
          bscroll && bscroll.refresh();
        });
      });

      const bTop =()=>{
        bscroll.scrollTo(0,0,300)
      }

      return{
        activeKay,
        categories,
        activeName,
        active,
        tabClick,
        getGoods,
        currentCid,
        goods,
        showGoods,
        bscroll,
        isShowBackTop,
        bTop,
        itemClick:(id)=>{
          // console.log(id)
          router.push({path:'/detail',query:{id}})
        }

      }
    },
    components: {
      NavBar,
      BackTop
    }
  }
</script>

<style scoped lang="scss">
  #mainbox{
    margin-top: 45px;
    display: flex;

    .ordertab{
      height: 50px;
      flex: 1;
      float: right;
      z-index: 9;
      position: fixed;
      top: 45px;
      right: 0;
      left: 130px;

    }
    .leftmenu{
      width: 130px;
      position: fixed;
      top: 95px;
      left: 0;
      width: 130px;
    }
    .goodslist{
      flex: 1;
      position: absolute;
      top: 100px;
      left: 130px;
      right: 0;
      height: 100vh; /*  占领整个可用大小*/
      padding: 10px;
      text-align: left !important;
      /*.content{*/
      /*  !*background-color: darkred;*!*/
      /*  padding-top: 10px;*/
      /*}*/
    }
  }

  .van-card__thumb{
    width: 68px !important;
  }

</style>
Copy the code

Detail.vue>>

<template>
  <div>
    <nav-bar>
      <template v-slot:default>{{id}}</template>
    </nav-bar>
  </div>
</template>

<script>
  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {useRoute} from 'vue-router'
  import {ref} from 'vue'
  export default {
    name: "Detail".components: {
      NavBar
    },
    setup(){
      const route  = useRoute()
      let id = ref(0)
      id.value = route.query.id
      return{
        id
      }
    }
  }
</script>

<style scoped>

</style>
Copy the code

detail.js >>

import {request} from "./request";

export function getDetail(id) {
  return request({
    url:'/api/goods/'+id,
  })
}
Copy the code

detail.vue>>

<template>
  <div>
    <nav-bar>
      <template v-slot:default>{{id}}</template>
    </nav-bar>


   
  </div>

</template>

<script>
  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {useRoute} from 'vue-router'
  import {ref,reactive,onMounted,toRefs} from 'vue'
  import {getDetail} from ".. /.. /network/detail";

  export default {
    name: "Detail".components: {
      NavBar
    },
    setup(){
      const route  = useRoute()
      let id = ref(0)
      let book = reactive({
        detail: {},like_goods: []// Similar books
      })
      //1 Mount the route
      onMounted(() = >{
        id.value = route.query.id

      // 2 Add method, getDetail(), can receive id
        getDetail(id.value).then(res= >{
          // console.log(res)
          book.detail = res.goods
          book.like_goods = res.like_goods
        })

      })
      return{ id, ... toRefs(book)/ / deconstruction}}}</script>

<style scoped>

</style>
Copy the code

2 Render product data to the template

The Image is introduced into

The enhanced IMG tag provides multiple image filling modes, and supports lazy image loading, loading prompt, and loading failure prompt.

import Vue from 'vue';
import { Image as VanImage } from 'vant';

Vue.use(VanImage);
Copy the code

Lazy image loading

To enable lazy image loading, set the lazy-load property in conjunction with the Lazyload component.

<van-image
  width="100"
  height="100"
  lazy-load
  src="https://img01.yzcdn.cn/vant/cat.jpeg"
/>
Copy the code

Card custom content

The Card component provides multiple slots for flexible customization of content.

< van - card num = "2" price = "2.00" desc = "description" title = "commodity title" thumb = "https://img01.yzcdn.cn/vant/ipad.jpeg" > < # template tags > </van-tag plain type="danger"> </van-tag plain type="danger"> </van-tag> </van-tag> </template> <template #footer> </van-button size="mini"> button </van-button> </van-button size="mini"> button </van-button> </template> </van-card>Copy the code

bass.css

@import "normalize.css";

:root{-color-text:# 666;
    --color-high-text:pink;
    --color-tint:pink;
    --color-background:#FFFFFF;
    --font-size:14px;
    --line-height: 1.5; } *, *::before, *::after{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}
body{
    user-select: none;
    /*background-color: #FFFFFF; * /
    background: var(--color-background);
    color: var(--color-text);
    width: 100vw;
}
a{
    color: var(--color-text);
    text-decoration: none;
}
.left{
    float: left;
}
.right{
    float: right;
}

/* Here set the size of the picture in the product details */
img{
    max-width: 100%;
    height: auto ;
}
Copy the code

detail.vue

<template>
  <div>
    <nav-bar>
      <template v-slot:default>{{id}}</template>
    </nav-bar>

    <van-image style="margin-top: 50px"
      width="100%"
      lazy-load
      :src="detail.cover_url"
    />

    <van-card style="text-align: left"
      :num="detail.stock"
      :price="detail.price+'.00'"
      :desc="detail.description"
      :title="detail.title"
    >
      <template #tags>
        <van-tag plain type="danger">A new book</van-tag>
        <van-tag plain type="danger">recommended</van-tag>
      </template>
      <template #footer>
        <van-button type="warning">Add to cart</van-button>
        <van-button type="danger">Buy now</van-button>
      </template>
    </van-card>

    <van-tabs v-model="active">
      <van-tab title="Summary">
        <div class="content" v-html="detail.details"><! {{detail. Details}}--> {{detail. Details}}-->
<! -- {{detail.details}}-->
        </div>
      </van-tab>
      <van-tab title="Buzz">

      </van-tab>
      <van-tab title="Related Books">
        <goods-list :goods="like_goods"></goods-list>

      </van-tab>
    </van-tabs>



  </div>

</template>

<script>
  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import GoodsList from ".. /.. /components/content/goods/GoodsList";
  import {useRoute} from 'vue-router'
  import {ref,reactive,onMounted,toRefs} from 'vue'
  import {getDetail} from ".. /.. /network/detail";

  export default {
    name: "Detail".components: {
      NavBar,
      GoodsList
    },
    setup(){
      const route  = useRoute()
      let id = ref(0)
      let book = reactive({
        detail: {},like_goods: []// Similar books
      })
      //1 Mount the route
      onMounted(() = >{
        id.value = route.query.id

      // 2 Add method, getDetail(), can receive id
        getDetail(id.value).then(res= >{
          // console.log(res)
          book.detail = res.goods
          book.like_goods = res.like_goods
          // console.log(book.like_goods)})})return{ id, ... toRefs(book)/ / deconstruction}}}</script>

<style scoped lang="scss">
.content{
  padding: 10px;
}
</style>
Copy the code

5 Project page – User interface

Authorization required (token)

1 user registered component development

Introduction of Form

It is used for data entry and verification, and supports input box, single box, check box, file upload and other types. It needs to be used with Field input box component. This component is supported in version 2.5.

import Vue from 'vue';
import { Form } from 'vant';
import { Field } from 'vant';

Vue.use(Form);
Vue.use(Field);
Copy the code

Basic usage

In the form, each Field component represents a form item, and the Field’s Rules property defines the validation rules.

<van-form @submit="onSubmit"> <van-field v-model="username" name=" username" label=" username" "placeholder=" username" :rules="[{ required: true, message: }]" /> < vain-field v-model="password" type="password" name=" password" label=" password" "placeholder=" password" :rules="[{required: "/> <div style="margin: 16px; </div> </van-form> </van-form>Copy the code

register.vue

<template>
  <div>
    <nav-bar>
      <template v-slot:default>New user registration</template>
    </nav-bar>
    <div style="margin-top: 50px">
      <div style="text-align: center; padding: 50px">
        <van-image
          width="10rem"
          height="5rem"
          fit="contain"
          src="https://avatars.githubusercontent.com/u/49421343?s=60&v=4"
        />
      </div>
      <van-form @submit="onSubmit">
        <van-field
          v-model="name"
          name="User name"
          label="User name"
          placeholder="User name"
          :rules="[{required: true, message: 'Please fill in user name'}]"
        />
        <van-field
          v-model="password"
          type="password"
          name="Password"
          label="Password"
          placeholder="Password"
          :rules="[{required: true, message: 'Please fill in password'}]"
        />
        <van-field
          v-model="password_confirmation"
          type="password"
          name="Confirm password"
          label="Confirm password"
          placeholder="Confirm password"
          :rules="[{required: true, message: 'Please fill in the same password'}]"
        />
        <van-field
          v-model="email"
          name="E-mail"
          label="E-mail"
          placeholder="Please enter the correct E-mail format"
          :rules="[{required: true, message: 'Please fill in email'}]"
        />
        <div style="margin: 16px;">
          <van-button round block type="info" color="pink" native-type="submit">submit</van-button>
        </div>
      </van-form>
    </div>
  </div>
</template>

<script>
  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {ref,reactive,toRefs} from 'vue'
  export default {
    name: "Register".components:{
      NavBar
    },
    setup(){
      const userinfo = reactive({
        name:' '.password:' '.password_confirmation:' '.email:' '

      })
      const onSubmit = () = >{
        console.log('# # # #')// Front-end validation
      }

      return{
        ...toRefs(userinfo),
        onSubmit
      }
    }
  }
</script>

<style scoped>

</style>
Copy the code

2 User registration and authentication

register.vue

<template>
  <div>
    <nav-bar>
      <template v-slot:default>New user registration</template>
    </nav-bar>
    <div style="margin-top: 50px">
      <div style="text-align: center; padding: 50px">
        <van-image
          width="10rem"
          height="5rem"
          fit="contain"
          src="https://avatars.githubusercontent.com/u/49421343?s=60&v=4"
        />
      </div>
      <van-form @submit="onSubmit">
        <van-field
          v-model="name"
          name="User name"
          label="User name"
          placeholder="User name"
          :rules="[{required: true, message: 'Please fill in user name'}]"
        />
        <van-field
          v-model="password"
          type="password"
          name="Password"
          label="Password"
          placeholder="Password"
          :rules="[{required: true, message: 'Please fill in password'}]"
        />
        <van-field
          v-model="password_confirmation"
          type="password"
          name="Confirm password"
          label="Confirm password"
          placeholder="Confirm password"
          :rules="[{required: true, message: 'Please fill in the same password'}]"
        />
        <van-field
          v-model="email"
          name="E-mail"
          label="E-mail"
          placeholder="Please enter the correct E-mail format"
          :rules="[{required: true, message: 'Please fill in email'}]"
        />
        <div style="margin: 16px;">
          <van-button round block type="info" color="pink" native-type="submit">submit</van-button>
        </div>
      </van-form>
    </div>
  </div>
</template>

<script>

  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {ref,reactive,toRefs} from 'vue';
  import {register} from ".. /.. /network/user";
  import {Notify,Toast} from 'vant';// The notification method is Toast
  import {useRouter} from 'vue-router'

  export default {
    name: "Register".components:{
      NavBar
    },
    setup(){
      const router = useRouter()
      const userinfo = reactive({
        name:' '.password:' '.password_confirmation:' '.email:' '
      })
      const onSubmit = () = >{
        // console.log('####')// Response intercept
      // 1
        if(userinfo.password ! = userinfo.password_confirmation) { Notify('Two passwords don't match... ');
        }else {
          // Submit again, return all submitted data, and
          register(userinfo).then((res) = > {
            if (res.status === 201) {
              Toast.success("Registration successful");
              setTimeout(() = > {
                // After successful registration, the login page will be redirected to 1 second
                router.push({ path: "/login" });
              }, 1000);
            }
            // Clear the password form
            userinfo.password = "";
            userinfo.password_confirmation = "";
          });
          
        }

      // 2 Submit to the backend
      }

      return{
        ...toRefs(userinfo),
        onSubmit,


      }
    }
  }
</script>

<style scoped>

</style>
Copy the code

request.js

import axios from 'axios';
import { Notify } from 'vant';// Message notification method

export function request(config) {
  const instance = axios.create({
    baseURL:'https://api.shop.eduwork.cn'.timeout:5000
  })
// Request interception
  instance.interceptors.request.use(config= >{
    // If there is an interface that requires authentication to access, set it in a unified way, (token)

    // Direct release
    return config
  },error= >{})// Response intercept
  instance.interceptors.response.use(res= >{
    // console.log(res)
    return res.data ? res.data : res
  },err= > {
  // If the interfaces that can be accessed only by authorization, disable login authorization

  // If there is an error, handle it here and determine the type of error based on the status code.
  // console.log(err.response.data.errors[Object.keys(err.response.data.errors)][0])
    Notify(err.response.data.errors[Object.keys(err.response.data.errors)[0]] [0]);

  })
  return instance(config)

}
Copy the code

3 User login development

Login.vue

<template>
  <div>
    <nav-bar>
      <template v-slot:default>The user login</template>
    </nav-bar>
    <div style="margin-top: 50px">
      <div style="text-align: center; padding: 50px">
        <van-image
          width="10rem"
          height="5rem"
          fit="contain"
          src="https://avatars.githubusercontent.com/u/49421343?s=60&v=4"
        />
      </div>
      <van-form @submit="onSubmit">
        <van-field
          v-model="email"
          name="email"
          label="email"
          placeholder="email"
          :rules="[{required: true, message: 'Email please'}]"
        />
        <van-field
          v-model="password"
          type="password"
          name="Password"
          label="Password"
          placeholder="Password"
          :rules="[{required: true, message: 'Please fill in password'}]"
        />

        <div style="margin: 16px;">
          <div class="link_login" @click="$router.push({path:'/register'})">No account, sign up now</div>
          <van-button round block type="info" color="pink" native-type="submit">submit</van-button>
        </div>
      </van-form>
    </div>
  </div>
</template>

<script>
   // Username: eduwork2 Password: user123 Email: [email protected]

  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {ref,reactive,toRefs} from 'vue';
  import {login} from ".. /.. /network/user";
  import {Notify,Toast} from 'vant';// The notification method is Toast
  import {useRouter} from 'vue-router'

  export default {
    name: "Login".components:{
      NavBar
    },
    setup(){
      const router = useRouter()
      const userinfo = reactive({
        email:' '.password:' ',})const onSubmit = () = >{
        // console.log('####')// Response intercept
        // Bidirectional binding
        login(userinfo).then(res= >{
        LocalStorage setItem(key,value) getItem(key); //1, the server will return token: acess -token, save the token to local, window.localstorage setItem(key,value) getItem(key)
          window.localStorage.setItem('token',res.access_token)
        // (or) in vuex islogin judge save
          Toast.success('Login successful')
          
          userinfo.email=' '
          userinfo.password=' '

          setTimeout(() = >{
            router.go(-1)},500)
        //})}return{
        ...toRefs(userinfo),
        onSubmit
      }
    }
  }
</script>

<style scoped>
.link_login{
  font-size: 14px;
  margin-bottom: 20px;
  color: # 990055;
  display: inline-block;
  text-align: left;
  float: left;
}
</style>
Copy the code

user.js

// All is related to the user
/ / register
/ / login
/ / verification

import {request} from "./request";


export function register(data) {
  return request({
    url:'/api/auth/register'.method:'post',
    data
  })
}


export function login(data) {
  return request({
    url:'/api/auth/login'.method:'post',
    data
  })
}
Copy the code

4 User Login Authorization Scheme Processing

There are two authorization schemes:

1. Page level authorization

2. Interface-level authorization

Profile. vue builds the personal interface first

<template>
  <div>
    <nav-bar>
      <template v-slot:default>Personal home page</template>
    </nav-bar>

    <div style="margin:15px; margin-top: 100px">
      <van-button @click="tologout" round block color="pink">Log out</van-button>
    </div>
  </div>
</template>

<script>
  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {logout} from ".. /.. /network/user";
  import {Toast} from 'vant'
  import {useRouter} from 'vue-router'
  import {useStore} from 'vuex'

  export default {
    name: "Profile".components: {
      NavBar
    },
    setup(){
      const router = useRouter()

      const tologout =() = >{
        logout().then(res= >{
          if (res.status === 204) {
            Toast.success('Exit successfully')
          // Clear the token window. localStorage
            window.localStorage.setItem('token'.' ')

            store.commit('setIsLogin'.false)

            setTimeout(() = >{
              router.push({path:'/login'})},500)}}}return{
        tologout,
      }
    }
  }
</script>

<style scoped>

</style>
Copy the code

router/index.js

import { createRouter, createWebHistory } from 'vue-router'
const Home = () = > import('.. /views/home/Home.vue')
const Category = () = > import('.. /views/category/Category.vue')
const Detail = () = > import('.. /views/detail/Detail.vue')
const Profile = () = > import('.. /views/profile/Profile.vue')
const ShopCart = () = > import('.. /views/shopcart/ShopCart.vue')
const Register =() = >import('.. /views/profile/Register.vue')
const Login =() = >import('.. /views/profile/Login.vue')
import store from '.. /store'
import {Notify, Toast} from 'vant'

/ / write routing
const routes = [
  {
    path: ' '.name: 'DefaultHome'.component: Home,
    meta: {title:'fy-shop'}}, {path: '/'.name: 'Home'.component: Home,
    meta: {title:'fy-shop'}}, {path: '/category'.name: 'Category'.component: Category,
    meta: {title:'Classification'}}, {path: '/detail'.name: 'Detail'.component: Detail,
    meta: {title:'Product Details'
    }
  },{
    path: '/profile'.name: 'Profile'.component: Profile,
    meta: {title:'Personal center'.isAuthRequired:true
    }
  },{
    path: '/register'.name: 'Register'.component: Register,
    meta: {title:'User registration',}}, {path: '/login'.name: 'Login'.component: Login,
    meta: {title:'User Login'
    }
  },{
    path: '/shopcart'.name: 'ShopCart'.component: ShopCart,
    meta: {title:'Shopping cart'.isAuthRequired:true}}]const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

router.beforeEach((to,from,next) = >{
    // If there is no login, go to login here
  if (to.meta.isAuthRequired && store.state.user.isLogin === false) {// Give a hint
    Notify('Not logged in yet, please log in')
    return next('/login')}else {
    next()
  }

  document.title = to.meta.title
}
  )
export default router
Copy the code

Login.vue

<template>
  <div>
    <nav-bar>
      <template v-slot:default>The user login</template>
    </nav-bar>
    <div style="margin-top: 50px">
      <div style="text-align: center; padding: 50px">
        <van-image
          width="10rem"
          height="5rem"
          fit="contain"
          src="https://avatars.githubusercontent.com/u/49421343?s=60&v=4"
        />
      </div>
      <van-form @submit="onSubmit">
        <van-field
          v-model="email"
          name="email"
          label="email"
          placeholder="email"
          :rules="[{required: true, message: 'Email please'}]"
        />
        <van-field
          v-model="password"
          type="password"
          name="Password"
          label="Password"
          placeholder="Password"
          :rules="[{required: true, message: 'Please fill in password'}]"
        />

        <div style="margin: 16px;">
          <div class="link_login" @click="$router.push({path:'/register'})">No account, sign up now</div>
          <van-button round block type="info" color="pink" native-type="submit">submit</van-button>
        </div>
      </van-form>
    </div>
  </div>
</template>

<script>
   // Username: eduwork2 Password: user123 Email: [email protected]

  import NavBar from ".. /.. /components/common/navbar/NavBar";
  import {ref,reactive,toRefs} from 'vue';
  import {login} from ".. /.. /network/user";
  import {Notify,Toast} from 'vant';// The notification method is Toast
  import {useRouter} from 'vue-router'
  import {useStore} from 'vuex'

  export default {
    name: "Login".components:{
      NavBar
    },
    setup(){
      const router = useRouter()
      const store = useStore()
      const userinfo = reactive({
        email:' '.password:' ',})const onSubmit = () = >{
        // Bidirectional binding
        login(userinfo).then(res= >{
          LocalStorage setItem(key,value) getItem(key); //1, the server will return token: acess -token, save the token to local, window.localstorage setItem(key,value) getItem(key)
          window.localStorage.setItem('token',res.access_token)
        // (or) in vuex islogin judge save
          store.commit('setIsLogin'.true)

          Toast.success('Login successful')

          userinfo.email=' '
          userinfo.password=' '

          setTimeout(() = >{
            router.go(-1)},500)
        //})}return{
        ...toRefs(userinfo),
        onSubmit
      }
    }
  }
</script>

<style scoped>
.link_login{
  font-size: 14px;
  margin-bottom: 20px;
  color: # 990055;
  display: inline-block;
  text-align: left;
  float: left;
}
</style>
Copy the code

request.js

import axios from 'axios';
import { Notify,Toast } from 'vant';
import router from ".. /router";

// Message notification method

export function request(config) {
  const instance = axios.create({
    baseURL:'https://api.shop.eduwork.cn'.timeout:5000
  })
// Request interception
  instance.interceptors.request.use(config= >{
    // If there is an interface that requires authentication to access, set it in a unified way, (token)
    const token = window.localStorage.getItem('token')

    if (token) {
      / / headers
      config.headers.Authorization = 'Beater'+token
    }
    // Direct release
    return config
  },error= >{})// Response intercept
  instance.interceptors.response.use(res= >{
    // console.log(res)
    return res.data ? res.data : res
  },err= > {
  // If the interfaces that can be accessed only by authorization, disable login authorization
    if (err.response.status == '401'){
      Toast.fail('Please login first')
      router.push({path:'/login'})}// If there is an error, handle it here and determine the type of error based on the status code.
  // console.log(err.response.data.errors[Object.keys(err.response.data.errors)][0])
    Notify(err.response.data.errors[Object.keys(err.response.data.errors)[0]] [0]);

  })
  return instance(config)

}
Copy the code

store

store/index.js

import { createStore } from 'vuex'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

const state = {
  user: {isLogin:window.localStorage.getItem('token')? true:false}}export default createStore({
  state,
  mutations,
  actions,
  getters

})
Copy the code

mutations.js

const mutations ={
  setIsLogin(state,payload){
    state.user.isLogin = payload
  }
}

export default mutations
Copy the code

actions.js

const actions = {


}

export default actions
Copy the code

getters.js

const getters = {

}

export default getters
Copy the code