项目【小U商城】
一、项目初始化
安装步骤
1.打开数据库服务
wampserve
2.新建数据库 导入shop_db root 没有密码
3.打开后台数据接口 shop_api
npm install
npm start
4.打开后台管理系统 shop_vue_admin
npm install
npm start
admin 123123
5.导入静态页面
运行 h5
微信小程序
二、封装网络请求
1.封装config.js 管理常量
// 常量
let baseUrl = ""
if(process.env.NODE_ENV === 'development'){
// console.log('开发环境')
baseUrl = "http://localhost:3000"
}else{
// console.log('生产环境')
baseUrl = "https://uapi.xqb.ink"
}
export default {
baseUrl
}
2.封装 http.js 网络请求
request 异步请求(不能直接在调用http方法时拿到请求的结果)
所以 要通过promise形式返回值,但是request方法 有返回的默认值,不会直接返回promise
所以 只能自己封装一个 返回 promise 的请求方法,
// 封装网络请求
import config from "./config.js"
let http = (option={})=>{
// url data method header timeout
return new Promise((resolve,reject)=>{
uni.request({
url: config.baseUrl + option.url,
data: option.data || {},
method: option.method || "GET",
header: option.header || {
"content-type":"application/json"
},
timeout: option.timeout || 60000,
// success:res=>{
// resolve(res)
// },
success:resolve,
fail:err=>{
reject(err)
}
})
})
}
export default http
3.封装api.js
可以统一管理所有接口,
// 管理所有请求接口
import http from "./http.js"
// 1.获取一级分类
const _getFirstCate=()=>{
let option={
url:"/api/getcate"
}
return http(option)
}
// 2.获取banner
export default {
_getFirstCate
}
4.挂载到原型上
main.js
import api from "./utils/api.js"
Vue.prototype.$api = api
三、项目
2.首页数据
2.1获取首页一级分类
接口
api.js
// 1.获取一级分类数据
const _getFirstCate=()=>{
let option={
url:"/api/getcate"
}
return http(option)
}
// 2.获取banner
export default {
_getFirstCate
}
数据请求
(1)获取一级分类
代码案例
index.vue
// 1.获取一级分类
async getFirstCate(){
// let res = await this.$http({url:"/api/getcate"})
// console.log(res);
let res = await this.$api._getFirstCate()
// console.log(res);
this.firstCate = res.data.list
},
(2)选项卡切换
代码案例
<scroll-view id="tab-bar" class="scroll-h" scroll-x="{true}">
<view class="uni-tab-item">
<text
@click="changeIndex(0)"
class="uni-tab-item-title "
:class="activeIndex == 0?'uni-tab-item-title-active' :''">
推荐
</text>
</view>
<view class="uni-tab-item " v-for="(item,index) in firstCate">
<text
@click="changeIndex(index+1)"
class="uni-tab-item-title "
:class="activeIndex == index+1 ?'uni-tab-item-title-active' :''"> {{item.catename}}
</text>
</view>
</scroll-view>
// 2.一级分类 切换选项卡
changeIndex(index){
this.activeIndex = index
}
2.2首页轮播图
接口
// 2.获取banner
const _getBanner = ()=>{
let option={
url:"/api/getbanner"
}
return http(option)
}
export default {
_getFirstCate,
_getBanner
}
数据请求
代码案例
data() {
return {
firstCate:[],
activeIndex:2,
baseUrl:"",
banner:[]
}
},
onShow() {
this.getFirstCate()
this.getBanner()
this.baseUrl = this.$config.baseUrl
},
methods: {
// 3.获取banner 轮播
async getBanner(){
let res = await this.$api._getBanner()
console.log(res);
this.banner = res.data.list
}
结构:
<!-- 推荐轮播图 -->
<view class="banner-container">
<swiper :indicator-dots="true" :autoplay="true" :interval="2000" :duration="1000">
<swiper-item v-for="(item,index) in banner" :key="index">
<view class="swiper-item">
<image :src="baseUrl+item.img" mode=""></image>
</view>
</swiper-item>
</swiper>
</view>
2.3限时秒杀
接口
// 3.获取秒杀活动
const _getSeckill = ()=>{
let option={
url:"/api/getseckill"
}
return http(option)
}
export default {
_getFirstCate,
_getBanner,
_getSeckill
}
(1)获取活动信息
代码案例
async getSeckill(){
let res = await this.$api._getSeckill()
console.log(res);
this.secimg = res.data.list[0].img
}
(2)处理倒计时
代码案例
async getSeckill(){
let res = await this.$api._getSeckill()
console.log(res);
// 页面倒计时活动 图片
this.secimg = res.data.list[0].img
// 获取倒计时 结束的时间戳
let endTime = res.data.list[0].endtime
// 倒计时显示
this.timeObj = this.actionTime(endTime)
this.time = setInterval(()=>{
//console.log(111);
this.timeObj = this.actionTime(endTime)
},1000)
},
// 处理时间
actionTime(endTime){
// 获取现在的时间 结束的时间戳-现在的时间戳
var nowTime = new Date().getTime()
// 时间差--秒 3670 1h 1min 10s
var sTime = parseInt((endTime- nowTime)/1000)
var h = parseInt(sTime/3600)
var m = parseInt((sTime%3600)/60)
var s = sTime % 60
h < 10 ? h="0"+ h : h = h
m < 10 ? m="0"+ m : m = m
s < 10 ? s="0"+ s : s = s
return {h,m,s}
}
(3)优化—离开页面关闭定时器
代码案例
onHide(){
clearInterval(this.time)
},
2.4底部商品信息
(1)底部商品选项卡切换
代码案例
// 7.底部商品 选项卡切换
changeMenuIndex(index){
this.menuIndex = index
}
<!-- 商品标签 -->
<view class="tags">
<view class="tag_list" :class="menuIndex == 0 ?'active_tag_list' :''" @click="changeMenuIndex(0)">
<label for="">热门推荐</label>
</view>
<view class="tag_list" :class="menuIndex == 1 ?'active_tag_list' :''" @click="changeMenuIndex(1)">
<label for="">最新好货</label>
</view>
<view class="tag_list" :class="menuIndex == 2 ?'active_tag_list' :''" @click="changeMenuIndex(2)">
<label for="">所有商品</label>
</view>
</view>
<!-- 标签对应的商品 -->
<view class="tags_product">
<block v-if="indexGoods.length>0">
<view class="product" v-for="(item,index) in indexGoods[menuIndex].content">
<view class="GoodsLeft">
<image class="GoodsImg" :src="baseUrl+item.img" alt />
</view>
<view class="GoodsCont">
<view class="GoodConTit">{{item.goodsname}}</view>
<view class="GoodsPrice">¥{{item.price}}</view>
<view class="yimai">已售50件</view>
<view class="Immed">立即抢购</view>
</view>
</view>
</block>
</view>
(2)数据获取
接口:
// 4.获取底部商品
const _getIndexGoods = ()=>{
let option={
url:"/api/getindexgoods"
}
return http(option)
}
export default {
_getFirstCate,_getBanner,_getSeckill,_getIndexGoods
}
代码案例
// 6.获取底部商品
async getIndexGoods(){
let res = await this.$api._getIndexGoods()
console.log(res);
this.indexGoods = res.data.list
},
2.5搜索框聚焦跳转到搜索页面
代码案例
@focus
// 8.顶部 聚焦 跳转到搜索页面
toSearchPage(){
uni.navigateTo({
url:"../search/search"
})
}
3.搜索页
参数:
参数名 | 说明 |
---|---|
keywords | 搜索关键字,必填项 |
接口
// 5.搜索
const _search=(keywords)=>{
let option={
url:"/api/search",
data:{
keywords
}
}
return http(option)
}
export default {
_getFirstCate,_getBanner,_getSeckill,_getIndexGoods,_search
}
代码案例
data() {
return {
list:[],
baseUrl:""
}
},
onShow(){
this.baseUrl = this.$config.baseUrl
},
methods:{
// 键盘输入事件
async search(e){
//console.log(e)
let keywords = e.value
if(keywords == "") return;
let res = await this.$api._search(keywords)
//console.log(res);
this.list = res.data.list || []
}
}
<uni-search-bar :radius="100" bgColor="#ffffff" class="input_" @input="search"></uni-search-bar>
<view class="list" v-if="list.length>0">
<view class="row" v-for="(item,index) in list">
<image :src="baseUrl+item.img" mode="widthFix"></image>
<view class="info">
<text>{{item.goodsname}}</text>
<text>原价¥{{item.price}}</text>
<text>现价¥{{item.market_price}}</text>
<text>3565评论</text>
</view>
</view>
</view>
<view class="tishi" v-else>
没有数据亲!
</view>
4.商品分类
(1)跳转到商品分类页面
代码案例
index.vue
// 9.全部分类
toCatePage(){
uni.navigateTo({
url:"../classify/classify"
})
}
(2)获取信息
接口:
// 6.获取全部分类
const _getCates = ()=>{
let option={
url:"/api/getcates"
}
return http(option)
}
export default {
_getFirstCate,_getBanner,_getSeckill,_getIndexGoods,_search,_getCates
}
代码案例
classify.vue
<template>
<view class="container">
<!-- left左侧列表 -->
<view class="left">
<!-- 循环遍历的 -->
<view @click="changeIndex(index)" v-for="(item,index) in cates" class="left_list" :class="activeIndex == index ? 'activeList' : '' ">
<label for="">{{item.catename}}</label>
</view>
</view>
<!-- right右侧详细分类商品 -->
<view class="right">
<!-- 每一个小类 -->
<view class="right_list">
<!-- 商品 -->
<view class="bottom" v-if="cates.length>0">
<view class="bottom_list" v-for="(item,index) in cates[activeIndex].children">
<view>
<image :src="baseUrl+item.img" alt="">
<span>{{item.catename}}</span>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
cates:[],
activeIndex:0,
baseUrl:""
}
},
onShow(){
this.getCates()
this.baseUrl = this.$config.baseUrl
},
methods: {
async getCates(){
let res = await this.$api._getCates()
console.log(res);
this.cates = res.data.list
},
changeIndex(index){
this.activeIndex = index
}
}
}
</script>
<style>
/* 导入外部的样式文件 */
@import url("../../common/css/classify.css");
/* 点击左侧导航,显示动态样式 */
.activeList {
border-left: 6rpx solid #f26b11;
color: #f26b11;
}
.bottom {
text-align: center;
display: flex;
flex-direction: row;
}
</style>