1.小程序的第三方框架
- 腾讯 wepy 类似于vue
- 美团 mpvue 类似于vue
- 京东 tao 类似于react
- 滴滴 chameleon
- uni-app 类似于vue
- 原生框架MINA
2. 帮助文件
接口文档地址:https://www.showdoc.com.cn/128719739414963/2513235043485226 阿里巴巴字体iconfont:https://www.iconfont.cn/ 即本项目的后台已经写好了,直接调用接口即可
3.项目的搭建
3.1新建小程序项目
填入自己的openID
3.2搭建目录结构
?录名 | 作? |
---|
styles | 存放公共样式 | components | 存放组件 | lib | 存放第三?库 | utils | ??的帮助库 | request | ??的接?帮助库 |
3.3. 搭建项目的页面
??名称 | 名称 |
---|
?? | index | 分类?? | category | 商品列表?? | goods_list | 商品详情?? | goods_detail | 购物??? | cart | 收藏?? | collect | 订单?? | order | 搜索?? | search | 个?中??? | user | 意?反馈?? | feedback | 登录?? | login | 授权?? | auth | 结算?? | pay |
3.4. 引?字体图标
1. 打开阿?巴巴字体图标 ?站 https://www.iconfont.cn/ 2. 选择的图标 选择自己想要的图标添加入库
3. 添加?项? 选择购物车图标 点击添加至项目(我选择了三个购物车的图标) 可以将其添加到一个已有的项目或者新建一个项目 本次项目所需要的图标 点击Font class生成代码 会生成一个class地址 4. 点击链接,复制样式,将其放入styles目录下的iconfont.wxss中
@font-face {
font-family: "iconfont";
src: url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.woff2?t=1640609932945') format('woff2'),
url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.woff?t=1640609932945') format('woff'),
url('//at.alicdn.com/t/font_3078102_um8ol0i9ny.ttf?t=1640609932945') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-weibiaoti2fuzhi08:before {
content: "\e625";
}
.icon-shoucangxuanzhong:before {
content: "\e62b";
}
.icon-fenxiang:before {
content: "\e86e";
}
.icon-kefu:before {
content: "\e88f";
}
.icon-dingdan:before {
content: "\e645";
}
.icon-fukuantongzhi:before {
content: "\e60c";
}
.icon-tuihuotuikuan_dianpu:before {
content: "\e773";
}
.icon-shoucang:before {
content: "\e8b9";
}
.icon-gouwucheman:before {
content: "\e600";
}
.icon-gouwuchekong:before {
content: "\e601";
}
.icon-gouwucheman1:before {
content: "\e602";
}
5. 在app.wxss中导入要使用的样式
@import "./styles/iconfont.wxss"
3.5 创建tabbar页面
1. 在项目中创建icon文件夹,并将素材中的相关图标拷贝进入 2. 常见tabbar相关页面和样式 app.json中
{
"pages":[
"pages/index/index",
"pages/category/index",
"pages/goods_list/index",
"pages/goods_detail/index",
"pages/cart/index",
"pages/collect/index",
"pages/order/index",
"pages/search/index",
"pages/user/index",
"pages/feedback/index",
"pages/login/index",
"pages/auth/index",
"pages/pay/index"
],
"tabBar": {
"color": "#999",
"selectedColor": "#ff2d4a",
"backgroundColor": "#fafafa",
"position": "bottom",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "icons/home.png",
"selectedIconPath": "icons/home-o.png"
},
{
"pagePath": "pages/category/index",
"text": "分类",
"iconPath": "icons/category.png",
"selectedIconPath": "icons/category-o.png"
},
{
"pagePath": "pages/cart/index",
"text": "购物车",
"iconPath": "icons/cart.png",
"selectedIconPath": "icons/cart-o.png"
},
{
"pagePath": "pages/user/index",
"text": "我的",
"iconPath": "icons/my.png",
"selectedIconPath": "icons/my-o.png"
}
]
},
"window":{
"backgroundTextStyle":"light",
"navigationBarBackgroundColor": "#eb4450",
"navigationBarTitleText": "黑马优购",
"navigationBarTextStyle":"white"
},
"style": "v2",
"sitemapLocation": "sitemap.json"
}
2.初始化页面 在app.wxss中
@import "./styles/iconfont.wxss";
page,view,text,swiper-item,image,navigator{
padding: 0;
margin: 0;
box-sizing: border-box;
}
page{
--themeColor:#eb4450;
font-size: 20rpx;
}
4. ??
4.1. 效果
4.2 使??定义组件的?式实现头部搜索框
1. 在components下面创建一个SearchInput组件,实现搜索的功能 2. 在SearchInput.wxml创建搜索导航
<view class="search_input">
<navigator url="/pages/search/index" open-type="navigate">
搜索
</navigator>
</view>
3. SearchInput.wxss中的样式
.search_input {
height: 90rpx;
padding: 10rpx;
background-color: var(--themeColor);
}
.search_input navigator {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: white;
border-radius: 15rpx;
color: #666;
}
4.引入所使用的组件 在pages/index/index.json中引入所使用的组件
{
"usingComponents": {
"SearchInput":"../../components/SearchInput/SearchInput"
},
"navigationBarTitleText": "优购首页"
}
5.使用组件 在pages/index/index.wxml
<view class="pyg_index">
<SearchInput></SearchInput>
</view>
4.3 首页轮播图
1. 网络请求获取轮播图的数据 在pages/index/index.js下
Page({
data: {
swipperList:[]
},
onLoad: function (options) {
wx.request({
url: 'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata',
success:(result)=>{
this.setData({
swipperList:result.data.message
})
}
});
}
})
接口调用结果显示 没有结果显示解决办法
- 没有添加appid,点击详情按钮,选择不校验合法域名
- 为了后面的开发管理和部署上线,选择开发管理中的服务设置,设置服务器的域名
2. 轮播图动态渲染 在pages/index/index/wxml中
<view class="index_swiper">
<swiper autoplay indicator-dots circular>
<swiper-item wx:for="{{swipperList}}" wx:key="goods_id">
<navigator>
<image mode="widthfix" src="{{item.image_src}}"></image>
</navigator>
</swiper-item>
</swiper>
</view>
相应的wxss
.index_swiper swiper {
width: 750rpx;
height: 340rpx;
}
.index_swiper swiper image {
width: 100%;
}
3. 将原生的请求改为promise方式
- 在request的目录下创建index.js
export const request=(params)=>{
return new Promise((resolve,reject)=>{
wx.request({
...params,
success:(result)=>{
resolve(result);
},
fail: (err)=>{
reject(err)
}
});
} )
}
- 在pages/index/index.js中加入request.js的路径
import {request} from "../../request/index.js"
- 请求路径的改写
onLoad: function (options) {
request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/swiperdata'})
.then(result=>{
this.setData({
swipperList:result.data.message
})
})
},
4.4 首页分类导航模块
1. 请求接口数据 在pages/index/index.js中获取导航数据
getCatesList(){
request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/catitems'})
.then(result=>{
this.setData({
catesList:result.data.message
})
})
}
2. 页面进行导航数据的显示 在pages/index/indedx.wxml中进行导航数据的显示
<view class="index_cate">
<navigator
wx:for="{{catesList}}"
wx:key="name"
>
<image mode="widthFix" src="{{item.image_src}}">
</image>
</navigator>
</view>
3. css样式
.index_cate {
display: flex;
padding: 20rpx;
}
.index_cate navigator {
flex: 1;
}
.index_cate navigator image {
width: 100%;
}
4.5 首页楼层模块
1. 获取接口数据 pages/index/index.js
getFloorList(){
request({url:'https://api-hmugo-web.itheima.net/api/public/v1/home/floordata'})
.then(result=>{
this.setData({
floorList:result.data.message
})
})
}
2. 内容显示 pages/index/index.wxml
<view class="index_floor">
<view >
<view wx:for="{{floorList}}"
wx:for-item="item1"
wx:for-index="index1"
wx:key="floor_title"
class="floor_group"
>
<view class="floor_title" >
<image src="{{item1.floor_title.image_src}}" mode="widthFix" ></image>
</view>
<view class="floor_list">
<navigator
wx:for="{{item1.product_list}}"
wx:for-item="item2"
wx:for-index="index2"
wx:key="name">
<image src="{{item2.image_src}}" mode="{{index2===0?'widthFix':'scaleToFill'}}"/>
</navigator>
</view>
</view>
</view>
</view>
3. 导入样式 pages/index/index.wxss
.index_floor .floor_group .floor_title {
padding: 10rpx 0;
}
.index_floor .floor_group .floor_title image {
width: 100%;
}
.index_floor .floor_group .floor_list {
overflow: hidden;
}
.index_floor .floor_group .floor_list navigator {
float: left;
width: 33.3%;
}
.index_floor .floor_group .floor_list navigator:nth-last-child(-n+4) {
height: 27.72711207vw;
border-left: 10rpx solid #fff;
}
.index_floor .floor_group .floor_list navigator:nth-child(2),
.index_floor .floor_group .floor_list navigator:nth-child(3) {
border-bottom: 10rpx solid #fff;
}
.index_floor .floor_group .floor_list navigator image {
width: 100%;
height: 100%;
}
首页效果
5. 分类页面
5.1 请求相关的数据
在pages/category/index.js
import {request} from "../../request/index.js";
Page({
data: {
leftMenuList:[],
rightContent:[],
currentIndex:0
},
Cates:[],
onLoad:function(options){
this.getCates()
},
getCates(){
request({
url:"https://api-hmugo-web.itheima.net/api/public/v1/categories"
})
.then(result=>{
this.Cates=result.data.message
let leftMenuList=this.Cates.map(v=>v.cat_name)
let rightContent=this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
})
},
handleItemTap(e)
{
const {index}=e.currentTarget.dataset;
let rightContent=this.Cates[index].children;
this.setData({
currentIndex:index,
rightContent
})
}
})
5.2 页面的编写
在pages/category/index.wxml
<view class="cates">
<SearchInput></SearchInput>
<view class="cates_container">
<scroll-view class="left_menu" scroll-y>
<view class="menu_item {{index===currentIndex?'active':''}}"
wx:for="{{leftMenuList}}"
wx:key="this"
bindtap="handleItemTap"
data-index="{{index}}"
>
{{item}}
</view>
</scroll-view>
<scroll-view class="right_content" scroll-y>
<view class="goods_group"
wx:for="{{rightContent}}"
wx:for-index="index1"
wx:for-item="item1"
>
<view class="goods_title">
<text class="delimiter"></text>
<text class="title">{{item1.cat_name}}</text>
<text class="delimiter"></text>
</view>
<view class="goods_list">
<navigator
wx:for="{{item1.children}}"
wx:for-index="index2"
wx:for-item="item2"
wx:key="cat_id"
>
<image src="{{item2.cat_icon}}" mode="widthFix"></image>
<view class="goods_name">{{item2.cat_name}}</view>
</navigator>
</view>
</view>
</scroll-view>
</view>
</view>
5.3 相关的样式
在pages/category/index.wxss
page {
height: 100%;
}
.cates {
height: 100%;
}
.cates .cates_container {
display: flex;
height: cac(100vh - 90rpx);
}
.cates .cates_container .left_menu {
flex: 2;
}
.cates .cates_container .left_menu .menu_item {
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
font-size: 30rpx;
}
.cates .cates_container .left_menu .active {
color: var(--themeColor);
border-left: 5rpx solid currentColor;
}
.cates .cates_container .right_content {
flex: 5;
}
.cates .cates_container .right_content .goods_group .goods_title {
height: 80rpx;
display: flex;
justify-content: center;
align-items: center;
}
.cates .cates_container .right_content .goods_group .goods_title .delimiter {
color: #ccc;
padding: 0 10rpx;
}
.cates .cates_container .right_content .goods_group .goods_list {
display: flex;
flex-wrap: wrap;
}
.cates .cates_container .right_content .goods_group .goods_list navigator {
width: 33.33%;
text-align: center;
}
.cates .cates_container .right_content .goods_group .goods_list navigator image {
width: 50%;
}
5.3 添加缓存
先判断本地是否有没有过期的旧数据,如果有就使用旧数据,没有就重新从接口中获取 在pages/category/index.js中修改的部分代码
onLoad:function(options){
const Cates=wx.getStorage("cates");
this.getCates()
if(!Cates)
{
this.getCates();
}
else
{
if(Date.now()-Cates.time>1000*10)
{
this.getCates()
}
else{
this.Cates=Cates.data;
let leftMenuList=this.Cates.map(v=>v.cat_name)
let rightContent=this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
}
}
},
getCates(){
request({
url:"https://api-hmugo-web.itheima.net/api/public/v1/categories"
})
.then(result=>{
this.Cates=result.data.message
wx.setStorageSync("cates", {time:Date.now(),data:this.Cates});
let leftMenuList=this.Cates.map(v=>v.cat_name)
let rightContent=this.Cates[0].children;
this.setData({
leftMenuList,
rightContent
})
})
},
5.4 点击菜单左侧列表置顶
在scrocll-view中有设置滚动条的位置 在页面中添加属性scroll-top并绑定值 在data中添加scrollTop 修改点击事件,点击一次距离顶部的距离为0
handleItemTap(e)
{
const {index}=e.currentTarget.dataset;
let rightContent=this.Cates[index].children;
this.setData({
currentIndex:index,
scrollTop:0,
rightContent
})
}
5.5 提取公共接口路径
发现这些接口有一些共同的路径,我们将其提取处理 在request/index.js下提取公共路径 然后在其他的请求接口中删除https://api-hmugo-web.itheima.net/api/public/v1/即可
6.商品列表
6.1 定义tab组件
1. tab/tab.wxml
<view class="tab">
<view class="tab_title">
<view class="title_item {{item.isActive?'active':''}}"
wx:for="{{tab}}"
wx:key="id"
bindtap="handleItemTap"
data-index="{{index}}"
>
{{item.value}}
</view>
</view>
<view class="tab_content">
<slot></slot>
</view>
</view>
2. tab/tab.wxss
.tab{}
.tab_title{
display: flex;
}
.title_item{
display: flex;
justify-content: center;
align-items: center;
flex: 1;
padding: 15rpx 0;
}
.active{
color: var(--themeColor);
border-bottom: 5rpx solid currentColor;
}
.tab_content{}
3. tab/tab.js
Component({
properties: {
tab:{
type:Array,
value:[]
}
},
data: {
},
methods: {
handleItemTap(e)
{
const {index}=e.currentTarget.dataset;
this.triggerEvent("tabItemChange",{index})
}
}
})
6.2 商品列表中使用组件
1. pages/gods_list/index.wxml
<SearchInput></SearchInput>
<tab tab="{{tab}}" bindtabItemChange="handleTabItemChange">
<block wx:if="{{tab[0].isActive}}">1</block>
<block wx:elif="{{tab[1].isActive}}">2</block>
<block wx:elif="{{tab[2].isActive}}">3</block>
</tab>
2. pages/gods_list/index.json
{
"usingComponents": {
"SearchInput":"../../components/SearchInput/SearchInput",
"tab":"../../components/tab/tab"
},
"navigationBarTitleText": "商品列表"
}
3. pages/gods_list/index.js
Page({
data: {
tab:[
{
id:0,
value:'综合',
isActive:true
},
{
id:1,
value:'销量',
isActive:false
},
{
id:0,
value:'价格',
isActive:false
}
]
},
onLoad: function (options) {
console.log(options)
},
handleTabItemChange(e)
{
const {index}=e.detail
let {tab}=this.data
tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
this.setData({
tab
})
}
})
6.3 商品列表的实现
需求分析
-
用户上滑页面 滚动条触底 开始加载下一页数据
- 找到滚动条触底事件
- 判断还有没有下一页数据
- 获取总页数 总页数=math.ceil(总条数/页容量)
- 获取当前页的页码
- 判断当前;页面是否大于总页数
- 假如没有下一页数据会弹出一个提示
- 假如有下一页数据,加载下一页数据
当前的页码++ 重新发送请求 数据请求回来 要对data中的数据进行拼接而不是全部替换 -
下拉刷新页面
- 触发下拉刷新事件 需要在页面的Json文件中开始一个配置项
- 重置数据 数组
- 重置页码 设置为1
- 重新发送请求
- 数据请求回来了 需要手动关闭 等待效果
1. pages/goods_list/index.js
import {request} from "../../request/index.js";
Page({
data: {
tab:[
{
id:0,
value:'综合',
isActive:true
},
{
id:1,
value:'销量',
isActive:false
},
{
id:0,
value:'价格',
isActive:false
}
],
goodsList:[]
},
QueryParams:{
qurty:"",
cid:"",
pagenum:1,
pagesize:10
},
totalPages:1,
onLoad: function (options) {
this.QueryParams.cid=options.cid;
this.getGoodsList();
},
getGoodsList(){
request({url:"goods/search",data:this.QueryParams})
.then(result=>{
const total= result.data.message.total
this.totalPages=Math.ceil(total/this.QueryParams.pagesize)
console.log(this.totalPages)
this.setData({
goodsList:[...this.data.goodsList,...result.data.message.goods]
})
})
wx.stopPullDownRefresh()
},
handleTabItemChange(e)
{
const {index}=e.detail
let {tab}=this.data
tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
this.setData({
tab
})
},
onReachBottom(){
if(this.QueryParams.pagenum>=this.totalPages)
{
wx.showToast({title: '没有下一页数据了' })
}
else{
this.QueryParams.pagenum++;
this.getGoodsList()
}
},
onPullDownRefresh()
{
this.setData({
goodsList:[]
})
this.QueryParams.pagenum=1;
this.getGoodsList();
}
})
2. pages/goods_list/index.wxss
.first_tab .goods_item {
display: flex;
border-bottom: 1rpx solid #ccc;
}
.first_tab .goods_item .goods_img_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.first_tab .goods_item .goods_img_wrap image {
width: 70%;
}
.first_tab .goods_item .goods_info_wrap {
flex: 3;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.first_tab .goods_item .goods_info_wrap .goods_name {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.first_tab .goods_item .goods_info_wrap .goods_price {
color: var(--themeColor);
font-size: 32rpx;
}
3. pages/goods_list/index.js
import {request} from "../../request/index.js";
Page({
data: {
tab:[
{
id:0,
value:'综合',
isActive:true
},
{
id:1,
value:'销量',
isActive:false
},
{
id:0,
value:'价格',
isActive:false
}
],
goodsList:[]
},
QueryParams:{
qurty:"",
cid:"",
pagenum:1,
pagesize:10
},
totalPages:1,
onLoad: function (options) {
this.QueryParams.cid=options.cid;
this.getGoodsList();
},
getGoodsList(){
request({url:"goods/search",data:this.QueryParams})
.then(result=>{
const total= result.data.message.total
this.totalPages=Math.ceil(total/this.QueryParams.pagesize)
console.log(this.totalPages)
this.setData({
goodsList:[...this.data.goodsList,...result.data.message.goods]
})
})
wx.stopPullDownRefresh()
},
handleTabItemChange(e)
{
const {index}=e.detail
let {tab}=this.data
tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
this.setData({
tab
})
},
onReachBottom(){
if(this.QueryParams.pagenum>=this.totalPages)
{
wx.showToast({title: '没有下一页数据了' })
}
else{
this.QueryParams.pagenum++;
this.getGoodsList()
}
},
onPullDownRefresh()
{
this.setData({
goodsList:[]
})
this.QueryParams.pagenum=1;
this.getGoodsList();
}
})
7. 商品详情页面
需求分析
-
点击轮播图预览大图 1. 给轮播图绑定点击事件 2. 调用小程序的api priviewUmage -
点击加入购物车
- 绑定点击事件
- 获取缓存中的购物车数据 数组格式
- 先判断当前的商品是否存在购物车中
- 已经存在 修改购物车中的商品数量 购物车中的商品数量++ 重新把购物车中的数组填充回缓存中
- 不存在于购物车的数组中,直接给购物车数组添加一个新元素 新元素带上购买属性num 重新把购物车数组填充回缓存中
1.pages/goods_detail/index.wxml
<view class="detail_swiper">
<swiper
autoplay
circular
indicator-dots
>
<swiper-item
wx:for="{{goodsObj.pics}}"
wx:key="pics_id"
bindtap="bindlePrevewImage"
data-url="{{item.pics_mid}}"
>
<image mode="widthFix" src="{{item.pics_mid}}"></image>
</swiper-item>
</swiper>
<view class="goods_price">¥{{goodsObj.goods_price}}</view>
<view class="goods_name_row">
<view class="goods_name">{{goodsObj.goods_name}}</view>
<view class="goods_collect" bindtap="handleCollect">
<text class="iconfont {{isCollect?'icon-shoucangxuanzhong':'icon-shoucang'}} "></text>
<view class="collect_text">收藏</view>
</view>
</view>
</view>
<view class="goods_info">
<view class="goods_info_title">图文详情</view>
<view class="goods_info_content">
<rich-text nodes="{{goodsObj.goods_introduce}}">
</rich-text>
</view>
</view>
<view class="btm_tool">
<view class="tool_item">
<view class="iconfont icon-kefu"></view>
<view>客服</view>
<button open-type="contact"></button>
</view>
<view class="tool_item">
<view class="iconfont icon-fenxiang"></view>
<view>分享</view>
<button open-type="share"></button>
</view>
<navigator open-type="switchTab" url="/pages/cart/index" class="tool_item">
<view class="iconfont icon-gouwucheman"></view>
<view>购物车</view>
</navigator>
<view class="tool_item btn_cart" bindtap="handleCartAdd">
<view>加入购物车</view>
</view>
<view class="tool_item btn_buy">
<view>立即购买</view>
</view>
</view>
2. pages/goods_detail/index.wxss
page {
padding-bottom: 90rpx;
}
.detail_swiper swiper {
height: 65vw;
text-align: center;
}
.detail_swiper swiper image {
width: 60%;
}
.goods_price {
padding: 15rpx;
font-size: 32rpx;
font-weight: 600;
color: var(--themeColor);
}
.goods_name_row {
display: flex;
}
.goods_name_row .goods_name {
border-top: 5rpx solid #dedede;
border-bottom: 5rpx solid #dedede;
padding: 10rpx 0;
flex: 5;
color: #000;
font-size: 30rpx;
padding: 0 10rpx;
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.goods_name_row .goods_collect {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
border-left: 1rpx solid #000;
}
.goods_name_row .goods_collect .icon-shoucangxuanzhong {
color: orangered;
}
.goods_info .goods_info_title {
font-size: 32rpx;
color: var(--themeColor);
font-weight: 600;
padding: 20rpx;
}
.btm_tool {
border-top: 1rpc solid #000;
position: fixed;
left: 0;
bottom: 0;
width: 100%;
height: 90rpx;
background-color: #fff;
display: flex;
}
.btm_tool .tool_item {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: 24rpx;
position: relative;
}
.btm_tool .tool_item button {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
.btm_tool .btn_cart {
flex: 2;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #ffa500;
color: #fff;
font-size: 30rpx;
font-weight: 600;
}
.btm_tool .btn_buy {
flex: 2;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background-color: #eb4450;
color: #fff;
font-size: 30rpx;
font-weight: 600;
}
- pages/goods_detail/index.js
import {request} from "../../request/index.js";
Page({
data: {
goodsObj:{},
isCollect:false
},
GoodsInfo:{},
onShow: function () {
let pages= getCurrentPages();
let currentPage=pages[pages.length-1];
let options=currentPage.options;
const {goods_id}=options
this.getGoodsDetail(goods_id)
},
getGoodsDetail(goods_id)
{
request({url:'goods/detail',data:{goods_id}})
.then(result=>{
const goodsObj=result.data.message;
this.GoodsInfo=goodsObj
let collect=wx.getStorageSync("collect")||[];
let isCollect = collect.some(v=>v.goods_id===this.GoodsInfo.goods_id)
this.setData({
goodsObj:{
goods_name:goodsObj.goods_name,
goods_price:goodsObj.goods_price,
goods_introduce:goodsObj.goods_introduce.replace(/\.webp/g,'.jpg'),
pics:goodsObj.pics
},
isCollect
})
})
},
bindlePrevewImage(e){
const urls=this.GoodsInfo.pics.map(v=>v.pics_mid);
const current=e.currentTarget.dataset.url;
wx.previewImage({
current,
urls
})
},
handleCartAdd()
{
let cart=wx.getStorageSync("cart")||[];
let index=cart.findIndex(v=>v.goods_id===this.GoodsInfo.goods_id);
if(index===-1)
{
this.GoodsInfo.num=1;
this.GoodsInfo.checked=true
cart.push(this.GoodsInfo);
}
else
{
cart[index].num++
}
wx.setStorageSync('cart', cart)
wx.showToast({
title:"加入成功",
icon:'success',
mask: true
})
},
handleCollect(){
let isCollect=false
let collect=wx.getStorageSync("collect")||[]
let index=collect.findIndex(v=>v.goods_id===this.GoodsInfo.goods_id)
if(index!==-1)
{
collect.splice(index,1)
isCollect=false;
wx.showToast({
title:"取消成功",
icon:"success",
mask:true
});
}
else{
collect.push(this.GoodsInfo)
isCollect=true
wx.showToast({
title:"收藏成功",
icon:"success",
mask:true
});
}
wx.setStorageSync("collect",collect)
this.setData({
isCollect
})
}
})
8. 商品收藏页面
1. 在 pages/collect/index.json中引入组件
{
"usingComponents": {
"tab":"../../components/tab/tab"
},
"navigationBarTitleText":"商品收藏"
}
** 2.pages/collect/index.wxml**
<tab tab="{{tabs}}" bindtabItemChange="handleTabItemChange">
<view class="collect_main">
<view class="collect_title">
<text class="collect_tips active">全部</text>
<text class="collect_tips">正在热卖</text>
<text class="collect_tips">即将上线</text>
</view>
<view class="collect_content">
<navigator class="goods_item"
wx:for="{{collect}}"
wx:key="goods_id"
url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
>
<view class="goods_img_wrap">
<image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'http://image5.suning.cn/uimg/b2c/newcatentries/0000000000-000000000160455569_1_400x400.jpg'}}">
</image>
</view>
<view class="goods_info_wrap" >
<view class="goods_name">{{item.goods_name}}</view>
<view class="goods_price">¥{{item.goods_price}}</view>
</view>
</navigator>
</view>
</view>
</tab>
3. pages/collect/index.js
Page({
data: {
collect:[],
tabs:[
{
id:0,
value:"商品收藏",
isActive:true
},
{
id:1,
value:"品牌收藏",
isActive:false
},
{
id:2,
value:"店铺收藏",
isActive:false
},
{
id:3,
value:"浏览足迹",
isActive:false
}
]
},
onShow(){
const collect=wx.getStorageSync("collect")||[]
this.setData({
collect
})
},
handleTabItemChange(e)
{
console.log(e)
const {index}=e.detail
let {tabs}=this.data
for(let i=0;i<4;i++)
{
if(i===index)
{
tabs[i].isActive=true
}
else
{
tabs[i].isActive=false
}
}
this.setData({
tabs
})
}
})
4. pages/collect/index.wxss
.collect_main {
background-color: #f3f4f6;
}
.collect_main .collect_title {
padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {
padding: 15rpx;
border: 1rpx solid #ccc;
margin-left: 25rpx;
background-color: #fff;
}
.collect_main .collect_title .active {
color: var(--themeColor);
border-color: currentColor;
}
.collect_main .collect_content .goods_item {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #ccc;
}
.collect_main .collect_content .goods_item .goods_img_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {
width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {
flex: 3;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {
color: var(--themeColor);
font-size: 32rpx;
}
9.购物车页面
需求分析
-
获取用户的收获地址
- 绑定点击事件
- 调用小程序内置api 获取用户是收货地址 wx.choseAddress
-
获取用户对小程序所授予获取地址的权限状态 scope
- 假设用户点击获取收货地址的提示框选择确定 authSetting scope,当 scope值为true 可以直接调用收货地址api
- 假设用户点击获取收货地址的提示框选择取消 scope值为true 诱导用户自己打开授权设置页面 当用户重新给收货地址授权的时候 获取收货地址
- 假设用户没有调用过获取收货地址的api scopewei undefined 可以之际调用收货地址的api
- 把收货地址放入本地缓冲中
- 页面加载完毕
- 获取本地存储中的地址数据
- 把数据设置给data中的一个变量
-
onshow 1. 获取缓存中的购物车数组 2. 把购物车中的数据填充到data中 -
全选的实现 1. onshow获取缓存中的购物车数据 2. 根据购物车中的商品数据 所有的商品都被选中 checked=true 全选就被选中 -
总价格 总数量
- 需要商品被选中,我们才拿他来计算
- 获取购物车数组
- 遍历
- 判断商品是否选中
- 总价格+=商品单价*商品数量
- 总数量+=商品数量
- 把计算后的单价和数量 设置回data即可
-
商品属性
- 绑定change事件
- 获取被修改的商品对象
- 商品对象的选择状态取反
- 重新将数据填充回data中和缓存中
- 重新计算全选 总价格 总数量
-
全选和反选
- 全选 复选框都绑定事件change
- 获取data中的全选变量 allchecked
- 直接取反 allchecked
- 遍历购物车数组和allchecked 重新设置回data 把购物车重新设置回缓存中
-
商品数量的编辑
- “+”,"-"按钮 绑定同一个点击事件 区分的关键 自定义属性: “+” +1, “-” -1
- 传递被点击商品的id goods_id
- 获取data中的购物车数组 来获取需要被修改的商品的对象
- 直接修改商品对象的数据 num
当购物车的数量为=1 同时点击的为"-" 弹框(showMNodel)提示用户是否删除 1 确定直接删除 2 取消 什么也不做 5.把cart数组 重新设置回缓存中和data中 -
点击结算
- 判断有没有收货地址信息
- 判断用户有没有选购商品
- 经过以上认证跳转到支付页面
1. pages/cart/index.wxml
<view class="revice_address_row">
<view class="address_btn" wx:if="{{!address.userName}}">
<button bindtap="handleChoseAddress" type="primary" plain>收货地址</button>
</view>
<view wx:else class="user_info_row">
<view class="user_info">
<view>
{{address.userName}}
</view>
<view>
{{address.provinceName+address.cityName+address.countyName+address.detailInfo}}
</view>
<view class="user_phone">
{{address.telNumber}}
</view>
</view>
</view>
</view>
<view class="cart_content">
<view class="cart_title">购物车</view>
<view class="cart_main">
<block wx:if="{{cart.length!==0}}">
<view class="cart_item"
wx:for="{{cart}}"
wx:key="goods_id"
>
<view class="cart_chk_wrap">
<checkbox-group bindchange="handeItemChange" data-id="{{item.goods_id}}">
<checkbox checked="{{item.checked}}"></checkbox>
</checkbox-group>
</view>
<navigator class="cart_img_wrap">
<image mode="widthFix" src="{{item.goods_small_logo}}"></image>
</navigator>
<view class="cart_info_wrap">
<view class="goods_name">{{item.goods_name}}</view>
<view class="goods_price_wrap">
<view class="goods_price">¥{{item.goods_price}}</view>
<view class="cart_num_tool">
<view bindtap="handleItemNumEdit" data-operation="{{-1}}" data-id="{{item.goods_id}}" class="num_edit" >-</view>
<view class="goods_num">{{item.num}}</view>
<view bindtap="handleItemNumEdit" data-operation="{{1}}" data-id="{{item.goods_id}}" class="num_edit">+</view>
</view>
</view>
</view>
</view>
</block>
<block wx:else="{{cart.length!==0}}">
<image mode="widthFix" src="https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fhbimg.b0.upaiyun.com%2Fa4064bdab5f6c800ed664014f3eb7d13a4dd25b3138d0-hYHe07_fw658&refer=http%3A%2F%2Fhbimg.b0.upaiyun.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1644844807&t=a98780f2ab7a2ee2653d589ef43e9f93"></image>
</block>
</view>
</view>
<view class="footer_tool">
<view class="all_chk_wrap">
<checkbox-group bindchange="handleItemAllCheck">
<checkbox checked="{{allChecked}}">全选</checkbox>
</checkbox-group>
</view>
<view class="total_price_wrap">
<view class="total_price">
总计<text class="total_price_text">¥{{totalPrice}}</text>
</view>
<view>
包含运费
</view>
</view>
<view class="order_pay_wrap" bindtap="handlePay">
结算{{totalNum}}
</view>
</view>
2. pages/cart/index.wxss
page {
padding-bottom: 90rpx;
}
.revice_address_row .address_btn {
padding: 20rpx;
}
.revice_address_row .address_btn button {
width: 60%;
}
.revice_address_row .user_info_row {
display: flex;
padding: 20rpx;
}
.revice_address_row .user_info_row .user_info {
flex: 5;
}
.revice_address_row .user_info_row .user_phone {
flex: 3;
text-align: right;
}
.cart_content .cart_title {
padding: 20rpx;
font-size: 36rpx;
color: var(--themeColor);
border-top: 1rpx solid currentColor;
border-bottom: 1rpx solid currentColor;
}
.cart_content .cart_main .cart_item {
display: flex;
padding: 10rpx;
border-bottom: 1rpx solid #000;
}
.cart_content .cart_main .cart_item .cart_chk_wrap {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.cart_content .cart_main .cart_item .cart_img_wrap {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
}
.cart_content .cart_main .cart_item .cart_img_wrap image {
width: 80%;
}
.cart_content .cart_main .cart_item .cart_info_wrap {
flex: 4;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_name {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
color: #666;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap {
display: flex;
justify-content: space-between;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .goods_price {
color: var(--themeColor);
font-size: 34rpx;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool {
display: flex;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool .num_edit {
width: 55rpx;
height: 55rpx;
display: flex;
justify-content: center;
justify-items: center;
border: 1rpx solid #ccc;
}
.cart_content .cart_main .cart_item .cart_info_wrap .goods_price_wrap .cart_num_tool .goods_num {
width: 55rpx;
height: 55rpx;
display: flex;
justify-content: center;
justify-items: center;
}
.footer_tool {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 90rpx;
background-color: #fff;
display: flex;
border-top: 1rpx solid #ccc;
}
.footer_tool .all_chk_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.footer_tool .total_price_wrap {
flex: 5;
padding-right: 15rpx;
text-align: right;
}
.footer_tool .total_price_wrap .total_price text.total_price_text {
color: var(--themeColor);
font-size: 34rpx;
font-weight: 600;
}
.footer_tool .order_pay_wrap {
flex: 3;
background-color: var(--themeColor);
color: #fff;
font-size: 32rpx;
font-weight: 600;
display: flex;
justify-content: center;
align-items: center;
}
异步编程中的Promise
在使用微信小程序的过程中,我发现由于异步流程延迟的原因,导致不能及时的获取真实的数据,我们可以使用promise()异步请求来解决这个问题 在utils下面创建一个名为asyncWx.js文件,后面问我们可以进行异步待遇
export const getSetting =()=>{
return new Promise((resolve,reject)=>{
wx.getSetting({
success: function(result) {
resolve(result)
},
fail:(err)=>{
reject(err)
}
});
})
}
export const chooseAddress =()=>{
return new Promise((resolve,reject)=>{
wx.chooseAddress({
success: function(result) {
resolve(result)
},
fail:(err)=>{
reject(err)
}
});
})
}
export const openSetting =()=>{
return new Promise((resolve,reject)=>{
wx.openSetting({
success: function(result) {
resolve(result)
},
fail:(err)=>{
reject(err)
}
});
})
}
export const showToast =({title})=>{
return new Promise((resolve,reject)=>{
wx.showToast({
title: title,
icon: 'success',
success:(res)=>{
resolve(res)
},
fail:(err)=>{
reject(err)
}
})
})
}
3.pages/cart/index.js 调用相关的异步函数
import{getSetting,chooseAddress,openSetting,showToast} from "../../utils/asyncWx.js"
Page({
data: {
address:{},
cart:[],
allChecked:false,
totalPrice:0,
totalNum:0
},
onShow(){
const address=wx.getStorageSync("address")
const cart=wx.getStorageSync("cart")||[]
this.setCart(cart)
this.setData({
address
})
},
async handleChoseAddress()
{
const res1=await getSetting();
const scopeAddress=res1.authSetting['scope.address']
if(scopeAddress===false)
{
await openSetting()
}
const address=await chooseAddress();
console.log(address)
wx.setStorageSync('address', address)
},
handeItemChange(e)
{
const goods_id=e.currentTarget.dataset.id
let {cart}=this.data;
let index = cart.findIndex(v=>v.goods_id===goods_id)
cart[index].checked=!cart[index].checked
this.setCart(cart)
},
handleItemAllCheck(e)
{
let {cart,allChecked}=this.data
allChecked=!allChecked
cart.forEach(v=>v.checked=allChecked)
this.setCart(cart)
},
handleItemNumEdit(e)
{
const {operation,id}=e.currentTarget.dataset
let {cart}=this.data;
const index=cart.findIndex(v=>v.goods_id===id)
if(cart[index].num===1&&operation===-1)
{
wx.showModal({
title: '提示',
content: '你是否删除',
success: (res)=> {
if (res.confirm) {
cart.splice(index, 1);
this.setCart(cart)
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
}
else
{
cart[index].num+=operation
this.setCart(cart)
}
},
async handlePay(){
const {address,totalNum}=this.data
if(!address.userName)
{
await showToast({title:"您还没有选择地址"})
return
}
if(totalNum===0)
{
await showToast({title:"您还没有选购商品"})
return
}
wx.navigateTo({
url: '/pages/pay/index'
});
},
setCart(cart){
let allChecked=true
let totalPrice=0
let totalNum=0
cart.forEach(v=>{
if(v.checked)
{
totalPrice+=v.num*v.goods_price
totalNum+=v.num
}
else{
allChecked=false
}
})
if(cart.length==0)
{
allChecked=false
}
this.setData({
cart,
totalNum,
totalPrice,
allChecked
})
wx.setStorageSync("cart",cart)
}
})
10.搜索中心
10.1 需求分析
-
输入绑定 值改变事件 input事件
- 获取输入框的值
- 判断合法性
- 校验通过 把输入框的值发送到后台
- 返回数据打印的页面上
-
防抖 (防止抖动) 定时器 节流
- 防抖一般用于防止重复输入 重复请求 节流一般用于页面上拉和下拉,可以定义全局变量定时器id来实现
10.2 具体实现
1. pages/search/index.wxml
<view class="search_row">
<input placeholder="请输入您要搜索的商品" value="{{inputValue}}" bindinput="handleInpput"></input>
<button hidden="{{!isFouces}}" bindtap="handleCancle">取消</button>
</view>
<view class="search_content">
<navigator url="/pages/goods_detail/index?goods_id={{item.goods_id}}" class="search_item" wx:for="{{goods}}" wx:key="{{goods_id}}">
{{item.goods_name}}
</navigator>
</view>
2. pages/search/index.js
import {request} from "../../request/index.js"
Page({
data: {
goods:[],
isFouces:false,
inputValue:''
},
Timeid:-1,
handleInpput(e)
{
const {value}=e.detail
if(!value.trim())
{
this.setData({
goods:[],
isFouces:false
})
return ;
}
this.setData({
isFouces:true
})
clearTimeout(this.Timeid)
this.Timeid=setTimeout(()=>{
this.qsearch(value)
},1000)
this.qsearch(value)
},
async qsearch(query)
{
console.log(query)
const res=await request({url:"goods/search",data:{query}});
this.setData({
goods:res.data.message.goods
})
},
handleCancle()
{
this.setData({
inputValue:'',
isFouces:false,
goods:[]
})
}
})
3. pages/search/index.wxss
page {
background-color: #dedede;
padding: 20rpx;
}
.search_row {
height: 60rpx;
display: flex;
}
.search_row input {
background-color: #fff;
flex: 4;
height: 100%;
padding-left: 30rpx;
}
.search_row button {
flex: 1;
height: 100%;
font-size: 26rpx;
padding: 0;
margin: 0 10rpx;
display: flex;
justify-content: center;
align-items: center;
}
.search_content {
margin-top: 30rpx;
}
.search_content .search_item {
background-color: #fff;
font-size: 26rpx;
padding: 15rpx 10rpx;
border-bottom: 1rpx solid #ccc;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
11.个人中心
11.1 用户登录授权
1. page/login/index.wxml
<button plain type="primary" open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">登录 </button>
2. pages/login/index.js
Page({
handleGetUserInfo(e)
{
const {userInfo} = e.detail;
wx.setStorageSync('userInfo', userInfo)
wx.navigateBack({
delta: 1
})
}
})
3. pages/login/index.wxss
button{
margin-top: 40rpx;
width: 70%;
}
11.2 商品收藏
1. 在pages/collect/index.json中引入组件
{
"usingComponents": {
"tab":"../../components/tab/tab"
},
"navigationBarTitleText":"商品收藏"
}
2. pages/collect/index.wxss
<tab tab="{{tabs}}" bindtabItemChange="handleTabItemChange">
<view class="collect_main">
<view class="collect_title">
<text class="collect_tips active">全部</text>
<text class="collect_tips">正在热卖</text>
<text class="collect_tips">即将上线</text>
</view>
<view class="collect_content">
<navigator class="goods_item"
wx:for="{{collect}}"
wx:key="goods_id"
url="/pages/goods_detail/index?goods_id={{item.goods_id}}"
>
<view class="goods_img_wrap">
<image mode="widthFix" src="{{item.goods_small_logo?item.goods_small_logo:'http://image5.suning.cn/uimg/b2c/newcatentries/0000000000-000000000160455569_1_400x400.jpg'}}">
</image>
</view>
<view class="goods_info_wrap" >
<view class="goods_name">{{item.goods_name}}</view>
<view class="goods_price">¥{{item.goods_price}}</view>
</view>
</navigator>
</view>
</view>
</tab>
3. pages/ccollect/index.wxss
.collect_main {
background-color: #f3f4f6;
}
.collect_main .collect_title {
padding: 40rpx 0;
}
.collect_main .collect_title .collect_tips {
padding: 15rpx;
border: 1rpx solid #ccc;
margin-left: 25rpx;
background-color: #fff;
}
.collect_main .collect_title .active {
color: var(--themeColor);
border-color: currentColor;
}
.collect_main .collect_content .goods_item {
display: flex;
background-color: #fff;
border-bottom: 1rpx solid #ccc;
}
.collect_main .collect_content .goods_item .goods_img_wrap {
flex: 2;
display: flex;
justify-content: center;
align-items: center;
}
.collect_main .collect_content .goods_item .goods_img_wrap image {
width: 70%;
}
.collect_main .collect_content .goods_item .goods_info_wrap {
flex: 3;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_name {
display: -webkit-box;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
.collect_main .collect_content .goods_item .goods_info_wrap .goods_price {
color: var(--themeColor);
font-size: 32rpx;
}
4. pages/collect/index.js
Page({
data: {
collect:[],
tabs:[
{
id:0,
value:"商品收藏",
isActive:true
},
{
id:1,
value:"品牌收藏",
isActive:false
},
{
id:2,
value:"店铺收藏",
isActive:false
},
{
id:3,
value:"浏览足迹",
isActive:false
}
]
},
onShow(){
const collect=wx.getStorageSync("collect")||[]
this.setData({
collect
})
},
handleTabItemChange(e)
{
console.log(e)
const {index}=e.detail
let {tabs}=this.data
for(let i=0;i<4;i++)
{
if(i===index)
{
tabs[i].isActive=true
}
else
{
tabs[i].isActive=false
}
}
this.setData({
tabs
})
}
})
11.3 意见反馈
11.3.1 需求分析
-
点击+号 触发tap点击事件
- 调用小程序内置的选择图片的api
- 获取到图片的路径 数组
- 把图片路径存到data的变量中
- 页面可以根据 图片数组 进行循环显示自定义组件
-
点击自定义图片 组件
- 获取被点击元素的索引
- 获取data中的图片数组
- 根据索引=数组中删除对应的元素
- 把数组重新设置到data中
-
点击提交
- 获取文本域的内容
- data中定义变量表示输入框的内容
- 文本域绑定输入事件 事件触发的时候 把输入框的值存入到变量中
- 对这些内容合法性验证
- 验证通过用户选择图片 上传到我们专门的图片服务器 返回图片的外网链接
- 遍历图片数组
- 挨个上传
- 维护一个图数组 存放;图片上传后的外网路径
- 文本域和外网的图片链接一起提交到服务器 前端模拟
- 清空当前页面
- 返回上一页
11.3.2 具体实现
由于需要使用图片上传的组件,先引入图片上传的组件
11.3.2.1 引入图片上传的组件
- 在components下面创建一个名为Upimg的文件夹,用于文件上传组件
- 在components/Upimg下面的Upimg文件下声明组件
{
"component": true,
"usingComponents": {}
}
- components/Upimg/Upimg.wxml
<view class="up_img_wrap">
<image src="{{src}}"></image>
<icon class="" type="clear" size="23" color="red">
</icon>
</view>
- components/Upimg/Upimg.wxss
.up_img_wrap{
width: 90rpx;
height: 90rpx;
position: relative;
}
.up_img_wrap image{
width: 100%;
height: 100%;
border-radius: 15rpx;
}
.up_img_wrap icon{
position: absolute;
top: -22rpx;
right: -22rpx;
}
- components/Upimg/Uping.js
Component({
properties: {
src: {
type:String,
value:""
}
},
data: {
},
methods: {
}
})
11.3.2.2 页面编写
1. 在pages/feedback/index.json中声明组件
{
"usingComponents": {
"SearchInput":"../../components/SearchInput/SearchInput",
"tab":"../../components/tab/tab",
"UpImg":"../../components/Upimg/Upimg"
},
"navigationBarTitleText":"意见反馈",
"enablePullDownRefresh":true,
"backgroundTextStyle":"dark"
}
2. pages/feedback/index.wxml
<tab tab="{{tab}}" bindtabItemChange="handleTabItemChange">
<view class="fb_main">
<view class="fb_title">问题的种类</view>
<view class="fb_tips">
<text>功能建议</text>
<text>购买遇到的问题</text>
<text>性能问题</text>
<text>其他</text>
</view>
<view class="fb_content">
<textarea value="{{textVal}}" bindinput="handleTextInput" placeholder="请输入您要描述的问题"></textarea>
<view class="fb_tool">
<button bindtap="handleChoseImg">+</button>
<view class="up_img_item"
wx:for="{{chooseImgs}}"
wx:key="this"
bindtap="handleRemoveImage"
data-index="{{index}}"
>
<UpImg src="{{item}}"></UpImg>
</view>
</view>
</view>
<view class="form_btn_wrap">
<button bindtap="handleFormSubmit" type="warn">
<icon class="" type="success_no_circle" size="23" color="white">
</icon>
提交
</button>
</view>
</view>
</tab>
3. pages/feedback/index.wxss
page {
background-color: #eeeeee;
}
.fb_main {
padding: 20rpx;
}
.fb_main .fb_tips {
display: flex;
flex-wrap: wrap;
}
.fb_main .fb_tips text {
width: 30%;
padding: 10rpx;
text-align: center;
background-color: #fff;
margin: 20rpx 10rpx;
}
.fb_main .fb_content {
background-color: #fff;
margin-top: 20rpx;
}
.fb_main .fb_content textarea {
padding: 10rpx;
}
.fb_main .fb_content .fb_tool {
display: flex;
flex-wrap: nowrap;
padding-bottom: 30rpx;
}
.fb_main .fb_content .fb_tool button {
margin: 0;
width: 90rpx;
height: 90rpx;
font-size: 60rpx;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
margin-left: 20rpx;
color: #ccc;
margin-top: 20rpx;
}
.fb_main .fb_content .fb_tool .up_img_item {
margin-top: 20rpx;
margin-left: 20rpx;
}
.fb_main .form_btn_wrap {
margin-top: 20rpx;
display: flex;
justify-content: flex-end;
}
.fb_main .form_btn_wrap button {
background-color: red;
color: white;
margin: 0;
padding: 0;
width: 30%;
}
4. pages/feedback/index.js
Page({
data: {
tab:[
{
id:0,
value:'体验问题',
isActive:true
},
{
id:1,
value:'商品商家投诉',
isActive:false
}
],
chooseImgs:[],
textVal:''
},
handleTabItemChange(e)
{
const {index}=e.detail
let {tab}=this.data
tab.forEach((v,i)=>i===index?v.isActive=true:v.isActive=false);
this.setData({
tab
})
},
handleChoseImg(e)
{
wx.chooseImage({
count:9,
sizeType:['original','compressed'],
sourceType:['album','camera'],
success:(result)=>{
console.log(result)
this.setData({
chooseImgs:[...this.data.chooseImgs,...result.tempFilePaths]
})
}
});
},
handleRemoveImage(e)
{
const {index}=e.currentTarget.dataset
let {chooseImgs}=this.data
chooseImgs.splice(index,1)
this.setData({
chooseImgs
})
},
handleTextInput(e)
{
this.setData({
textVal:e.detail.value
})
},
handleFormSubmit()
{
const {textVal,chooseImgs}=this.data
if(!textVal.trim())
{
wx.showToast({
title:'输入不合法',
icon:'none',
mask: true
});
return
}
wx.showLoading({
title:"正在上传",
mask:true
})
if(chooseImgs.length!=0)
{
chooseImgs.forEach((v,i)=>{
wx.uploadFile({
url:'https://img.coolcr.cn/api/upload',
filePath:v,
name:"image",
formData: {},
success: function(res){
let url=JSON.parse(res.data).data.url
}
})
if(i===chooseImgs.length-1){
wx.hideLoading()
console.log('把文本中的内容和外网的图片数组提交到后台中')
this.setData({
textVal:'',
chooseImgs:[]
})
wx.navigateBack({
delta: 1
})
}
})
}
else
{
console.log("只是提交了文本")
wx.hideLoading()
wx.navigateBack({
delta: 1
})
}
}
})
11.4 个人中心
1. pages/user/index.wxml
<view class="user_info_wrap">
<view wx:if="{{userInfo.avatarUrl}}" class="user_img_wrap">
<image class="user_bg" src="{{userInfo.avatarUrl}}"></image>
<view class="user_info">
<image class="user_icon" src="{{userInfo.avatarUrl}}"></image>
<view class="user_name">{{userInfo.nickName}}</view>
</view>
</view>
<view wx:else class="user_btn">
<navigator url="/pages/login/index">登录</navigator>
</view>
</view>
<view class="user_content">
<view class="user_main">
<view class="history_wrap">
<navigator>
<view class="his_num">8</view>
<view class="his_name">收藏的店铺</view>
</navigator>
<navigator url="/pages/collect/index">
<view class="his_num">{{collectNums}}</view>
<view class="his_name">收藏的商品</view>
</navigator>
<navigator>
<view class="his_num">8</view>
<view class="his_name">关注的商品</view>
</navigator>
<navigator>
<view class="his_num">8</view>
<view class="his_name">我的足迹</view>
</navigator>
</view>
<view class="orders_wrap">
<view class="order_title">我的订单</view>
<view class="order_content">
<navigator url="/pages/order/index?type=1">
<view class="iconfont icon-dingdan"></view>
<view class="order_name">全部订单</view>
</navigator>
<navigator url="/pages/order/index?type=2">
<view class="iconfont icon-fukuantongzhi"></view>
<view class="order_name">待付款</view>
</navigator>
<navigator url="/pages/order/index?type=3">
<view class="iconfont icon-weibiaoti2fuzhi08"></view>
<view class="order_name">待收货</view>
</navigator>
<navigator>
<view class="iconfont icon-tuihuotuikuan_dianpu"></view>
<view class="order_name">退货/退款</view>
</navigator>
</view>
</view>
<view class="address_wrap">
收货地址管理
</view>
<view class="app_info_wrap">
<view class="app_info_item app_info_contact">
<text>联系客服</text>
<text>400-618-4000</text>
</view>
<navigator class="app_info_item" url="/pages/feedback/index">意见反馈</navigator>
<view class="app_info_item">关于我们</view>
</view>
<view class="recommend_wrap">
把应用推荐给其他人
</view>
</view>
</view>
2. pages/user/index.wxss
page {
background-color: #edece8;
}
.user_info_wrap {
height: 45vh;
overflow: hidden;
background-color: var(--themeColor);
position: relative;
}
.user_info_wrap .user_img_wrap {
position: relative;
}
.user_info_wrap .user_img_wrap .user_bg {
height: 50vh;
filter: blur(10rpx);
}
.user_info_wrap .user_img_wrap .user_info {
position: absolute;
left: 50%;
top: 20%;
transform: translateX(-50%);
text-align: center;
}
.user_info_wrap .user_img_wrap .user_icon {
width: 150rpx;
height: 150rpx;
border-radius: 50%;
}
.user_info_wrap .user_img_wrap .user_name {
color: #fff;
margin-top: 40rpx;
}
.user_info_wrap .user_btn {
position: absolute;
left: 50%;
transform: translateX(-50%);
top: 40%;
border: 1rpx solid greenyellow;
color: greenyellow;
font-size: 38rpx;
padding: 30rpx;
border-radius: 10rpx;
}
.user_content {
position: relative;
}
.user_content .user_main {
padding-bottom: 100rpx;
color: #666;
position: absolute;
width: 90%;
left: 50%;
transform: translateX(-50%);
top: -40rpx;
}
.user_content .user_main .history_wrap {
background-color: #fff;
display: flex;
}
.user_content .user_main .history_wrap navigator {
flex: 1;
padding: 10rpx 0;
text-align: center;
}
.user_content .user_main .history_wrap navigator .his_num {
color: var(--themeColor);
}
.user_content .user_main .orders_wrap {
background-color: #fff;
margin-top: 30rpx;
}
.user_content .user_main .orders_wrap .order_title {
padding: 20rpx;
border-bottom: 1rpx solid #ccc;
}
.user_content .user_main .orders_wrap .order_content {
display: flex;
}
.user_content .user_main .orders_wrap .order_content navigator {
padding: 15rpx 0;
flex: 1;
text-align: center;
}
.user_content .user_main .orders_wrap .order_content navigator .iconfont {
color: var(--themeColor);
font-size: 40rpx;
}
.user_content .user_main .address_wrap {
margin-top: 30rpx;
background-color: #fff;
padding: 20rpx;
}
.user_content .user_main .app_info_wrap {
margin-top: 30rpx;
background-color: #fff;
}
.user_content .user_main .app_info_wrap .app_info_item {
padding: 20rpx;
border-bottom: 1rpx solid #ccc;
}
.user_content .user_main .app_info_wrap .app_info_contact {
display: flex;
justify-content: space-between;
}
.user_content .user_main .recommend_wrap {
margin-top: 30rpx;
background-color: #fff;
padding: 20rpx;
}
3. pages/user/index.js
Page({
data: {
userInfo:{},
collectNums:0
},
onShow(){
const userInfo=wx.getStorageSync('userInfo')
const collect=wx.getStorageSync("collect")||[]
this.setData({
userInfo,
collectNums:collect.length
})
}
})
说明:由于是个人用户,小程序的订单功能未实现,但小程序中的大部分功能已经实现
|