提交 bf845e1e authored 作者: lhh's avatar lhh

update

上级
File added
<script>
/**
* vuex管理登陆状态,具体可以参考官方登陆模板示例
*/
import {
mapMutations
} from 'vuex';
export default {
methods: {
...mapMutations(['login'])
},
onLaunch: function() {
let userInfo = uni.getStorageSync('userInfo') || '';
if(userInfo.id){
//更新登陆状态
uni.getStorage({
key: 'userInfo',
success: (res) => {
this.login(res.data);
}
});
}
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
},
}
</script>
<style lang='scss'>
/*
全局公共样式和字体图标
*/
@font-face {
font-family: yticon;
font-weight: normal;
font-style: normal;
src: url('https://at.alicdn.com/t/font_1078604_w4kpxh0rafi.ttf') format('truetype');
}
.yticon {
font-family: "yticon" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-yiguoqi1:before {
content: "\e700";
}
.icon-iconfontshanchu1:before {
content: "\e619";
}
.icon-iconfontweixin:before {
content: "\e611";
}
.icon-alipay:before {
content: "\e636";
}
.icon-shang:before {
content: "\e624";
}
.icon-shouye:before {
content: "\e626";
}
.icon-shanchu4:before {
content: "\e622";
}
.icon-xiaoxi:before {
content: "\e618";
}
.icon-jiantour-copy:before {
content: "\e600";
}
.icon-fenxiang2:before {
content: "\e61e";
}
.icon-pingjia:before {
content: "\e67b";
}
.icon-daifukuan:before {
content: "\e68f";
}
.icon-pinglun-copy:before {
content: "\e612";
}
.icon-dianhua-copy:before {
content: "\e621";
}
.icon-shoucang:before {
content: "\e645";
}
.icon-xuanzhong2:before {
content: "\e62f";
}
.icon-gouwuche_:before {
content: "\e630";
}
.icon-icon-test:before {
content: "\e60c";
}
.icon-icon-test1:before {
content: "\e632";
}
.icon-bianji:before {
content: "\e646";
}
.icon-jiazailoading-A:before {
content: "\e8fc";
}
.icon-zuoshang:before {
content: "\e613";
}
.icon-jia2:before {
content: "\e60a";
}
.icon-huifu:before {
content: "\e68b";
}
.icon-sousuo:before {
content: "\e7ce";
}
.icon-arrow-fine-up:before {
content: "\e601";
}
.icon-hot:before {
content: "\e60e";
}
.icon-lishijilu:before {
content: "\e6b9";
}
.icon-zhengxinchaxun-zhifubaoceping-:before {
content: "\e616";
}
.icon-naozhong:before {
content: "\e64a";
}
.icon-xiatubiao--copy:before {
content: "\e608";
}
.icon-shoucang_xuanzhongzhuangtai:before {
content: "\e6a9";
}
.icon-jia1:before {
content: "\e61c";
}
.icon-bangzhu1:before {
content: "\e63d";
}
.icon-arrow-left-bottom:before {
content: "\e602";
}
.icon-arrow-right-bottom:before {
content: "\e603";
}
.icon-arrow-left-top:before {
content: "\e604";
}
.icon-icon--:before {
content: "\e744";
}
.icon-zuojiantou-up:before {
content: "\e605";
}
.icon-xia:before {
content: "\e62d";
}
.icon--jianhao:before {
content: "\e60b";
}
.icon-weixinzhifu:before {
content: "\e61a";
}
.icon-comment:before {
content: "\e64f";
}
.icon-weixin:before {
content: "\e61f";
}
.icon-fenlei1:before {
content: "\e620";
}
.icon-erjiye-yucunkuan:before {
content: "\e623";
}
.icon-Group-:before {
content: "\e688";
}
.icon-you:before {
content: "\e606";
}
.icon-forward:before {
content: "\e607";
}
.icon-tuijian:before {
content: "\e610";
}
.icon-bangzhu:before {
content: "\e679";
}
.icon-share:before {
content: "\e656";
}
.icon-yiguoqi:before {
content: "\e997";
}
.icon-shezhi1:before {
content: "\e61d";
}
.icon-fork:before {
content: "\e61b";
}
.icon-kafei:before {
content: "\e66a";
}
.icon-iLinkapp-:before {
content: "\e654";
}
.icon-saomiao:before {
content: "\e60d";
}
.icon-shezhi:before {
content: "\e60f";
}
.icon-shouhoutuikuan:before {
content: "\e631";
}
.icon-gouwuche:before {
content: "\e609";
}
.icon-dizhi:before {
content: "\e614";
}
.icon-fenlei:before {
content: "\e706";
}
.icon-xingxing:before {
content: "\e70b";
}
.icon-tuandui:before {
content: "\e633";
}
.icon-zuanshi:before {
content: "\e615";
}
.icon-zuo:before {
content: "\e63c";
}
.icon-shoucang2:before {
content: "\e62e";
}
.icon-shouhuodizhi:before {
content: "\e712";
}
.icon-yishouhuo:before {
content: "\e71a";
}
.icon-dianzan-ash:before {
content: "\e617";
}
view,
scroll-view,
swiper,
swiper-item,
cover-view,
cover-image,
icon,
text,
rich-text,
progress,
button,
checkbox,
form,
input,
label,
radio,
slider,
switch,
textarea,
navigator,
audio,
camera,
image,
video {
box-sizing: border-box;
}
/* 骨架屏替代方案 */
.Skeleton {
background: #f3f3f3;
padding: 20upx 0;
border-radius: 8upx;
}
/* 图片载入替代方案 */
.image-wrapper {
font-size: 0;
background: #f3f3f3;
border-radius: 4px;
image {
width: 100%;
height: 100%;
transition: .6s;
opacity: 0;
&.loaded {
opacity: 1;
}
}
}
.clamp {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: block;
}
.common-hover {
background: #f5f5f5;
}
/*边框*/
.b-b:after,
.b-t:after {
position: absolute;
z-index: 3;
left: 0;
right: 0;
height: 0;
content: '';
transform: scaleY(.5);
border-bottom: 1px solid $border-color-base;
}
.b-b:after {
bottom: 0;
}
.b-t:after {
top: 0;
}
/* button样式改写 */
uni-button,
button {
height: 80upx;
line-height: 80upx;
font-size: $font-lg + 2upx;
font-weight: normal;
&.no-border:before,
&.no-border:after {
border: 0;
}
}
uni-button[type=default],
button[type=default] {
color: $font-color-dark;
}
/* input 样式 */
.input-placeholder {
color: #999999;
}
.placeholder {
color: #999999;
}
</style>
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2020-2024] [macrozheng]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
# mall-app-web
<p>
<a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-macrozheng-blue.svg" alt="公众号"></a>
<a href="#公众号"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E4%BA%A4%E6%B5%81-%E5%BE%AE%E4%BF%A1%E7%BE%A4-2BA245.svg" alt="交流"></a>
<a href="https://github.com/macrozheng/mall"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%90%8E%E5%8F%B0%E9%A1%B9%E7%9B%AE-mall-blue.svg" alt="后台项目"></a>
<a href="https://github.com/macrozheng/mall-admin-web"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E5%89%8D%E7%AB%AF%E9%A1%B9%E7%9B%AE-mall--admin--web-green.svg" alt="前端项目"></a>
<a href="https://gitee.com/macrozheng/mall-app-web"><img src="http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/badge/%E7%A0%81%E4%BA%91-%E9%A1%B9%E7%9B%AE%E5%9C%B0%E5%9D%80-orange.svg" alt="码云"></a>
</p>
## 前言
该项目为前后端分离项目的前端部分,后端项目`mall`地址:[传送门](https://github.com/macrozheng/mall)
## 项目介绍
`mall-app-web`是一个电商系统的移动端项目,基于`uni-app`实现。主要包括首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等功能。
### 项目演示
项目在线演示地址:[https://www.macrozheng.com/app/](https://www.macrozheng.com/app/)
### 效果展示
![](http://img.macrozheng.com/mall/project/mall_app_web_preview_01.png)![](http://img.macrozheng.com/mall/project/mall_app_web_preview_02.png)
![](http://img.macrozheng.com/mall/project/mall_app_web_preview_03.png)![](http://img.macrozheng.com/mall/project/mall_app_web_preview_04.png)
![](http://img.macrozheng.com/mall/project/mall_app_web_preview_05.png)![](http://img.macrozheng.com/mall/project/mall_app_web_preview_06.png)
![](http://img.macrozheng.com/mall/project/mall_app_web_preview_07.png)![](http://img.macrozheng.com/mall/project/mall_app_web_preview_08.png)
![](http://img.macrozheng.com/mall/project/mall_app_web_preview_09.png)![](http://img.macrozheng.com/mall/project/mall_app_web_preview_10.png)
### 技术选型
| 技术 | 说明 | 官网 |
| ------------ | ---------------- | --------------------------------------- |
| Vue | 核心前端框架 | https://vuejs.org |
| Vuex | 全局状态管理框架 | https://vuex.vuejs.org |
| uni-app | 移动端前端框架 | https://uniapp.dcloud.io |
| mix-mall | 电商项目模板 | https://ext.dcloud.net.cn/plugin?id=200 |
| luch-request | HTTP请求框架 | https://github.com/lei-mu/luch-request |
### 项目结构
``` lua
src -- 源码目录
├── api -- luch-request网络请求定义
├── components -- 通用组件封装
├── js_sdk -- 第三方sdk源码
├── static -- 图片等静态资源
├── store -- vuex的状态管理
├── utils -- 工具类
└── pages -- 前端页面
├── address -- 地址管理页
├── brand -- 商品品牌页
├── cart -- 购物车页
├── category -- 商品分类页
├── coupon -- 优惠券页
├── index -- 首页
├── money -- 支付页
├── notice -- 通知页
├── order -- 订单页
├── product -- 商品页
├── public -- 登录页
├── set -- 设置页
├── user -- 会员页
└── userinfo -- 会员信息页
```
## 搭建步骤
- 本项目使用了`uni-app`专用开发工具`HBuilder X`(App开发版)开发,下载地址:https://www.dcloud.io/hbuilderx.html
- 该项目为前后端分离项目,访问本地访问接口需搭建后台环境,搭建请参考后端项目[传送门](https://github.com/macrozheng/mall)
- 注意由于`mall-app-web`中的接口都在`mall-portal`模块中,所以一定要启动该模块;
- 访问在线接口无需搭建后台环境,只需将`utils/requestUtil.js`文件中的`config.baseUrl`改为线上地址即可:https://portal-api.macrozheng.com
- 克隆源代码到本地,使用`HBuilder X`打开;
-`HBuilder X`中使用`运行->运行到浏览器->Chrome`运行项目,运行成功后会自动打开下面地址(将浏览器改为手机模式):http://localhost:8080
- 如果浏览器没有启动的话,可以直接访问如下地址访问:http://localhost:8080
## 公众号
学习不走弯路,关注公众号「**macrozheng**」,回复「**学习路线**」,获取mall项目专属学习路线!
加微信群交流,公众号后台回复「**加群**」即可。
![公众号图片](http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/banner/qrcode_for_macrozheng_258.jpg)
## 许可证
[Apache License 2.0](https://github.com/macrozheng/mall-app-web/blob/master/LICENSE)
Copyright (c) 2020-2024 macrozheng
\ No newline at end of file
import request from '@/utils/requestUtil'
export function fetchAddressList() {
return request({
method: 'GET',
url: '/member/address/list'
})
}
export function fetchAddressDetail(id) {
return request({
method: 'GET',
url: `/member/address/${id}`
})
}
export function addAddress(data) {
return request({
method: 'POST',
url: '/member/address/add',
data:data
})
}
export function updateAddress(data) {
return request({
method: 'POST',
url: `/member/address/update/${data.id}`,
data:data
})
}
export function deleteAddress(id) {
return request({
method: 'POST',
url: `/member/address/delete/${id}`
})
}
import request from '@/utils/requestUtil'
export function getBrandDetail(id) {
return request({
method: 'GET',
url: `/brand/detail/${id}`,
})
}
export function fetchBrandProductList(params) {
return request({
method: 'GET',
url: '/brand/productList',
params:params
})
}
export function fetchBrandRecommendList(params) {
return request({
method: 'GET',
url: '/brand/recommendList',
params:params
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function addCartItem(data) {
return request({
method: 'POST',
url: '/cart/add',
data: data
})
}
export function fetchCartList() {
return request({
method: 'GET',
url: '/cart/list'
})
}
export function deletCartItem(params) {
return request({
method: 'POST',
url: '/cart/delete',
params:params
})
}
export function updateQuantity(params) {
return request({
method: 'GET',
url: '/cart/update/quantity',
params:params
})
}
export function clearCartList() {
return request({
method: 'POST',
url: '/cart/clear'
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function fetchProductCouponList(productId) {
return request({
method: 'GET',
url: `/member/coupon/listByProduct/${productId}`,
})
}
export function addMemberCoupon(couponId) {
return request({
method: 'POST',
url: `/member/coupon/add/${couponId}`,
})
}
export function fetchMemberCouponList(useStatus) {
return request({
method: 'GET',
url: '/member/coupon/list',
params:{useStatus:useStatus}
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function fetchContent() {
return request({
method: 'GET',
url: '/home/content'
})
}
export function fetchRecommendProductList(params) {
return request({
method: 'GET',
url: '/home/recommendProductList',
params:params
})
}
export function fetchProductCateList(parentId) {
return request({
method: 'GET',
url: '/home/productCateList/'+parentId,
})
}
export function fetchNewProductList(params) {
return request({
method: 'GET',
url: '/home/newProductList',
params:params
})
}
export function fetchHotProductList(params) {
return request({
method: 'GET',
url: '/home/hotProductList',
params:params
})
}
import request from '@/utils/requestUtil'
export function memberLogin(data) {
return request({
method: 'POST',
url: '/sso/login',
header: {
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
},
data: data
})
}
export function memberInfo() {
return request({
method: 'GET',
url: '/sso/info'
})
}
import request from '@/utils/requestUtil'
export function createBrandAttention(data) {
return request({
method: 'POST',
url: '/member/attention/add',
data: data
})
}
export function deleteBrandAttention(params) {
return request({
method: 'POST',
url: '/member/attention/delete',
params: params
})
}
export function fetchBrandAttentionList(params) {
return request({
method: 'GET',
url: '/member/attention/list',
params:params
})
}
export function brandAttentionDetail(params) {
return request({
method: 'GET',
url: '/member/attention/detail',
params: params
})
}
export function clearBrandAttention() {
return request({
method: 'POST',
url: '/member/attention/clear'
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function createProductCollection(data) {
return request({
method: 'POST',
url: '/member/productCollection/add',
data: data
})
}
export function deleteProductCollection(params) {
return request({
method: 'POST',
url: '/member/productCollection/delete',
params: params
})
}
export function fetchProductCollectionList(params) {
return request({
method: 'GET',
url: '/member/productCollection/list',
params:params
})
}
export function productCollectionDetail(params) {
return request({
method: 'GET',
url: '/member/productCollection/detail',
params: params
})
}
export function clearProductCollection() {
return request({
method: 'POST',
url: '/member/productCollection/clear'
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function createReadHistory(data) {
return request({
method: 'POST',
url: '/member/readHistory/create',
data: data
})
}
export function fetchReadHistoryList(params) {
return request({
method: 'GET',
url: '/member/readHistory/list',
params: params
})
}
export function clearReadHistory() {
return request({
method: 'POST',
url: '/member/readHistory/clear'
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function generateConfirmOrder(data) {
return request({
method: 'POST',
url: '/order/generateConfirmOrder',
data: data
})
}
export function generateOrder(data) {
return request({
method: 'POST',
url: '/order/generateOrder',
data: data
})
}
export function fetchOrderList(params) {
return request({
method: 'GET',
url: '/order/list',
params: params
})
}
export function payOrderSuccess(data) {
return request({
method: 'POST',
url: '/order/paySuccess',
header: {
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
},
data: data
})
}
export function fetchOrderDetail(orderId) {
return request({
method: 'GET',
url: `/order/detail/${orderId}`
})
}
export function cancelUserOrder(data) {
return request({
method: 'POST',
url: '/order/cancelUserOrder',
header: {
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
},
data: data
})
}
export function confirmReceiveOrder(data) {
return request({
method: 'POST',
url: '/order/confirmReceiveOrder',
header: {
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
},
data: data
})
}
export function deleteUserOrder(data) {
return request({
method: 'POST',
url: '/order/deleteOrder',
header: {
'content-type': 'application/x-www-form-urlencoded;charset=utf-8'
},
data: data
})
}
export function fetchAliapyStatus(params) {
return request({
method: 'GET',
url: '/alipay/query',
params: params
})
}
\ No newline at end of file
import request from '@/utils/requestUtil'
export function searchProductList(params) {
return request({
method: 'GET',
url: '/product/search',
params: params
})
}
export function fetchCategoryTreeList() {
return request({
method: 'GET',
url: '/product/categoryTreeList'
})
}
export function fetchProductDetail(id) {
return request({
method: 'GET',
url: '/product/detail/'+id
})
}
<template>
<view class="empty-content">
<image class="empty-content-image" :src="setSrc" mode="aspectFit"></image>
</view>
</template>
<script>
export default {
props: {
src: {
type: String,
default: 'empty'
},
},
data() {
return {
typeSrc: {
empty: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMMAAACtCAYAAAANgcuAAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Rjk5MjI3NjM1NUJGMTFFOThGRTZGQUIxMjY1ODk1QTkiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Rjk5MjI3NjQ1NUJGMTFFOThGRTZGQUIxMjY1ODk1QTkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGOTkyMjc2MTU1QkYxMUU5OEZFNkZBQjEyNjU4OTVBOSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGOTkyMjc2MjU1QkYxMUU5OEZFNkZBQjEyNjU4OTVBOSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvBPHdgAAEY/SURBVHja7H3bkiTH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'
},
}
},
computed: {
setSrc() {
return this.typeSrc[this.src];
},
}
}
</script>
<style lang="scss">
.empty-content {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: $page-color-base;
padding-bottom: 120upx;
&-image {
width: 200upx;
height: 200upx;
}
}
</style>
<template>
<view class="content">
<view class="mix-list-cell" :class="border" @click="eventClick" hover-class="cell-hover" :hover-stay-time="50">
<text
v-if="icon"
class="cell-icon yticon"
:style="[{
color: iconColor,
}]"
:class="icon"
></text>
<text class="cell-tit clamp">{{title}}</text>
<text v-if="tips" class="cell-tip">{{tips}}</text>
<text class="cell-more yticon"
:class="typeList[navigateType]"
></text>
</view>
</view>
</template>
<script>
/**
* 简单封装了下, 应用范围比较狭窄,可以在此基础上进行扩展使用
* 比如加入image, iconSize可控等
*/
export default {
data() {
return {
typeList: {
left: 'icon-zuo',
right: 'icon-you',
up: 'icon-shang',
down: 'icon-xia'
},
}
},
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: '标题'
},
tips: {
type: String,
default: ''
},
navigateType: {
type: String,
default: 'right'
},
border: {
type: String,
default: 'b-b'
},
hoverClass: {
type: String,
default: 'cell-hover'
},
iconColor: {
type: String,
default: '#333'
}
},
methods: {
eventClick(){
this.$emit('eventClick');
}
},
}
</script>
<style lang='scss'>
.icon .mix-list-cell.b-b:after{
left: 90upx;
}
.mix-list-cell{
display:flex;
align-items:baseline;
padding: 20upx $page-row-spacing;
line-height:60upx;
position:relative;
&.cell-hover{
background:#fafafa;
}
&.b-b:after{
left: 30upx;
}
.cell-icon{
align-self:center;
width:56upx;
max-height:60upx;
font-size:38upx;
}
.cell-more{
align-self: center;
font-size:30upx;
color:$font-color-base;
margin-left:$uni-spacing-row-sm;
}
.cell-tit{
flex: 1;
font-size: $font-base;
color: $font-color-dark;
margin-right:10upx;
}
.cell-tip{
font-size: $font-sm+2upx;
color: $font-color-light;
}
}
</style>
<template>
<!-- loading 加载 -->
<view class="mix-loading-content">
<view class="mix-loading-wrapper">
<image
class="mix-loading-icon"
src="data:image/png;base64,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">
</image>
</view>
</view>
</template>
<script>
export default {
props: {
top: {
//距离顶部距离,单位upx
type: Number,
default: 0
},
},
data() {
return {
};
},
methods: {
}
}
</script>
<style>
.mix-loading-content{
display:flex;
justify-content: center;
align-items: center;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: transparent;
}
.mix-loading-wrapper{
display: flex;
justify-content: center;
align-items: center;
animation: loading .5s ease-in infinite both alternate;
}
.mix-loading-icon{
width: 80upx;
height: 80upx;
transition: .3s;
}
@keyframes loading {
0% {
transform: translateY(-20upx) scaleX(1);
}
100% {
transform: translateY(4upx) scaleX(1.3);
}
}
</style>
<template>
<view v-if="show" class="mask" @click="toggleMask" @touchmove.stop.prevent="stopPrevent"
:style="{backgroundColor: backgroundColor}"
>
<view
class="mask-content"
@click.stop.prevent="stopPrevent"
:style="[{
height: config.height,
transform: transform
}]"
>
<scroll-view class="view-content" scroll-y>
<view class="share-header">
分享到
</view>
<view class="share-list">
<view
v-for="(item, index) in shareList" :key="index"
class="share-item"
@click="shareToFriend(item.text)"
>
<image :src="item.icon" mode=""></image>
<text>{{item.text}}</text>
</view>
</view>
</scroll-view>
<view class="bottom b-t" @click="toggleMask">取消</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
transform: 'translateY(50vh)',
timer: 0,
backgroundColor: 'rgba(0,0,0,0)',
show: false,
config: {},
};
},
props:{
contentHeight:{
type: Number,
default: 0
},
//是否是tabbar页面
hasTabbar:{
type: Boolean,
default: false
},
shareList:{
type: Array,
default: function(){
return [];
}
}
},
created() {
const height = uni.upx2px(this.contentHeight) + 'px';
this.config = {
height: height,
transform: `translateY(${height})`,
backgroundColor: 'rgba(0,0,0,.4)',
}
this.transform = this.config.transform;
},
methods:{
toggleMask(){
//防止高频点击
if(this.timer == 1){
return;
}
this.timer = 1;
setTimeout(()=>{
this.timer = 0;
}, 500)
if(this.show){
this.transform = this.config.transform;
this.backgroundColor = 'rgba(0,0,0,0)';
setTimeout(()=>{
this.show = false;
this.hasTabbar && uni.showTabBar();
}, 200)
return;
}
this.show = true;
//等待mask重绘完成执行
if(this.hasTabbar){
uni.hideTabBar({
success: () => {
setTimeout(()=>{
this.backgroundColor = this.config.backgroundColor;
this.transform = 'translateY(0px)';
}, 10)
}
});
}else{
setTimeout(()=>{
this.backgroundColor = this.config.backgroundColor;
this.transform = 'translateY(0px)';
}, 10)
}
},
//防止冒泡和滚动穿透
stopPrevent(){},
//分享操作
shareToFriend(type){
this.$api.msg(`分享给${type}`);
this.toggleMask();
},
}
}
</script>
<style lang='scss'>
.mask{
position:fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
display:flex;
justify-content: center;
align-items: flex-end;
z-index: 998;
transition: .3s;
.bottom{
position:absolute;
left: 0;
bottom: 0;
display:flex;
justify-content: center;
align-items: center;
width: 100%;
height: 90upx;
background: #fff;
z-index: 9;
font-size: $font-base + 2upx;
color: $font-color-dark;
}
}
.mask-content{
width: 100%;
height: 580upx;
transition: .3s;
background: #fff;
&.has-bottom{
padding-bottom: 90upx;
}
.view-content{
height: 100%;
}
}
.share-header{
height: 110upx;
font-size: $font-base+2upx;
color: font-color-dark;
display:flex;
align-items:center;
justify-content: center;
padding-top: 10upx;
&:before, &:after{
content: '';
width: 240upx;
heighg: 0;
border-top: 1px solid $border-color-base;
transform: scaleY(.5);
margin-right: 30upx;
}
&:after{
margin-left: 30upx;
margin-right: 0;
}
}
.share-list{
display:flex;
flex-wrap: wrap;
}
.share-item{
min-width: 33.33%;
display:flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 180upx;
image{
width: 80upx;
height: 80upx;
margin-bottom: 16upx;
}
text{
font-size: $font-base;
color: $font-color-base;
}
}
</style>
<template>
<view class="uni-load-more">
<view class="uni-load-more__img" v-show="status === 'loading' && showIcon">
<view class="load1">
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
</view>
<view class="load2">
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
</view>
<view class="load3">
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
<view :style="{background:color}"></view>
</view>
</view>
<text class="uni-load-more__text" :style="{color:color}">{{status === 'more' ? contentText.contentdown : (status === 'loading' ? contentText.contentrefresh : contentText.contentnomore)}}</text>
</view>
</template>
<script>
export default {
name: "uni-load-more",
props: {
status: {
//上拉的状态:more-loading前;loading-loading中;noMore-没有更多了
type: String,
default: 'more'
},
showIcon: {
type: Boolean,
default: true
},
color: {
type: String,
default: "#777777"
},
contentText: {
type: Object,
default () {
return {
contentdown: "上拉显示更多",
contentrefresh: "正在加载...",
contentnomore: "没有更多数据了"
};
}
}
},
data() {
return {}
}
}
</script>
<style>
@charset "UTF-8";
.uni-load-more {
display: flex;
flex-direction: row;
height: 80upx;
align-items: center;
justify-content: center
}
.uni-load-more__text {
font-size: 28upx;
color: #999
}
.uni-load-more__img {
height: 24px;
width: 24px;
margin-right: 10px
}
.uni-load-more__img>view {
position: absolute
}
.uni-load-more__img>view view {
width: 6px;
height: 2px;
border-top-left-radius: 1px;
border-bottom-left-radius: 1px;
background: #999;
position: absolute;
opacity: .2;
transform-origin: 50%;
animation: load 1.56s ease infinite
}
.uni-load-more__img>view view:nth-child(1) {
transform: rotate(90deg);
top: 2px;
left: 9px
}
.uni-load-more__img>view view:nth-child(2) {
transform: rotate(180deg);
top: 11px;
right: 0
}
.uni-load-more__img>view view:nth-child(3) {
transform: rotate(270deg);
bottom: 2px;
left: 9px
}
.uni-load-more__img>view view:nth-child(4) {
top: 11px;
left: 0
}
.load1,
.load2,
.load3 {
height: 24px;
width: 24px
}
.load2 {
transform: rotate(30deg)
}
.load3 {
transform: rotate(60deg)
}
.load1 view:nth-child(1) {
animation-delay: 0s
}
.load2 view:nth-child(1) {
animation-delay: .13s
}
.load3 view:nth-child(1) {
animation-delay: .26s
}
.load1 view:nth-child(2) {
animation-delay: .39s
}
.load2 view:nth-child(2) {
animation-delay: .52s
}
.load3 view:nth-child(2) {
animation-delay: .65s
}
.load1 view:nth-child(3) {
animation-delay: .78s
}
.load2 view:nth-child(3) {
animation-delay: .91s
}
.load3 view:nth-child(3) {
animation-delay: 1.04s
}
.load1 view:nth-child(4) {
animation-delay: 1.17s
}
.load2 view:nth-child(4) {
animation-delay: 1.3s
}
.load3 view:nth-child(4) {
animation-delay: 1.43s
}
@-webkit-keyframes load {
0% {
opacity: 1
}
100% {
opacity: .2
}
}
</style>
\ No newline at end of file
<template>
<view class="uni-numbox">
<view class="uni-numbox-minus"
@click="_calcValue('subtract')"
>
<text class="yticon icon--jianhao" :class="minDisabled?'uni-numbox-disabled': ''" ></text>
</view>
<input
class="uni-numbox-value"
type="number"
:disabled="disabled"
:value="inputValue"
@blur="_onBlur"
>
<view
class="uni-numbox-plus"
@click="_calcValue('add')"
>
<text class="yticon icon-jia2" :class="maxDisabled?'uni-numbox-disabled': ''" ></text>
</view>
</view>
</template>
<script>
export default {
name: 'uni-number-box',
props: {
isMax: {
type: Boolean,
default: false
},
isMin: {
type: Boolean,
default: false
},
index: {
type: Number,
default: 0
},
value: {
type: Number,
default: 0
},
min: {
type: Number,
default: -Infinity
},
max: {
type: Number,
default: Infinity
},
step: {
type: Number,
default: 1
},
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
inputValue: this.value,
minDisabled: false,
maxDisabled: false
}
},
created(){
this.maxDisabled = this.isMax;
this.minDisabled = this.isMin;
},
computed: {
},
watch: {
inputValue(number) {
const data = {
number: number,
index: this.index
}
this.$emit('eventChange', data);
}
},
methods: {
_calcValue(type) {
const scale = this._getDecimalScale();
let value = this.inputValue * scale;
let newValue = 0;
let step = this.step * scale;
if(type === 'subtract'){
newValue = value - step;
if (newValue <= this.min){
this.minDisabled = true;
}
if(newValue < this.min){
newValue = this.min
}
if(newValue < this.max && this.maxDisabled === true){
this.maxDisabled = false;
}
}else if(type === 'add'){
newValue = value + step;
if (newValue >= this.max){
this.maxDisabled = true;
}
if(newValue > this.max){
newValue = this.max
}
if(newValue > this.min && this.minDisabled === true){
this.minDisabled = false;
}
}
if(newValue === value){
return;
}
this.inputValue = newValue / scale;
},
_getDecimalScale() {
let scale = 1;
// 浮点型
if (~~this.step !== this.step) {
scale = Math.pow(10, (this.step + '').split('.')[1].length);
}
return scale;
},
_onBlur(event) {
let value = event.detail.value;
if (!value) {
this.inputValue = 0;
return
}
value = +value;
if (value > this.max) {
value = this.max;
} else if (value < this.min) {
value = this.min
}
this.inputValue = value
}
}
}
</script>
<style>
.uni-numbox {
position:absolute;
left: 30upx;
bottom: 0;
display: flex;
justify-content: flex-start;
align-items: center;
width:230upx;
height: 70upx;
background:#f5f5f5;
}
.uni-numbox-minus,
.uni-numbox-plus {
margin: 0;
background-color: #f5f5f5;
width: 70upx;
height: 100%;
line-height: 70upx;
text-align: center;
position: relative;
}
.uni-numbox-minus .yticon,
.uni-numbox-plus .yticon{
font-size: 36upx;
color: #555;
}
.uni-numbox-minus {
border-right: none;
border-top-left-radius: 6upx;
border-bottom-left-radius: 6upx;
}
.uni-numbox-plus {
border-left: none;
border-top-right-radius: 6upx;
border-bottom-right-radius: 6upx;
}
.uni-numbox-value {
position: relative;
background-color: #f5f5f5;
width: 90upx;
height: 50upx;
text-align: center;
padding: 0;
font-size: 30upx;
}
.uni-numbox-disabled.yticon {
color: #d6d6d6;
}
</style>
<template>
<view class="upload-content">
<block v-for="(item, index) in imageList" :key="index">
<view class="upload-item">
<image class="upload-img" :src="item.filePath" mode="aspectFill" @click="previewImage(index)"></image>
<image class="upload-del-btn"
@click="delImage(index)"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6RjNBODgzQjUwNDM5MTFFOUJDMjlGN0UwRTJGMjVCNjQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6RjNBODgzQjYwNDM5MTFFOUJDMjlGN0UwRTJGMjVCNjQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGM0E4ODNCMzA0MzkxMUU5QkMyOUY3RTBFMkYyNUI2NCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGM0E4ODNCNDA0MzkxMUU5QkMyOUY3RTBFMkYyNUI2NCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuYQTIAAAAHNSURBVHjazJgxasMwFIbjnsD4BDmCLxDQDZrNQ5bcILlBvXvpDRLIWmiHFkyH2t0LyVbwEkPpbE/eYlUCGYQax096UuIffkIgij7ryU/vyZuYy2e+Z56K70R8lsJcOfPn5AriMCvmZ2YKdMW8EQ/hRA9iEorwXlpZtObMRySQ6kysvrFWloHUEIcmUBuHUDLccmxQskErt7wyVLdy00tQ4Q2g5De2V9kNwWjffiNDA33f/42i6E13Qj5uNptlwJD+SyODuaqqqi/KVBTFOxSKA7VtW/NxSZI8AcbE2nuraZpvKgSBk6G4drsdBOwogz1CwAghH/JEl+BUKJ1VltPHHjoIAoeE4l53YFqb+RKcBSgqyqXhtxEKZwkKB3YOThYCqjPuCDoHZwGK3mGLtdPp5LmqTI1Dqe4p26EkNqA4iE6eg2z+iQ0okyQMATvYgLIIt9Y6knTyFBIudHqIq3BpmkL60VJ9O0sXZY8MBwSLtdNGEAQ/i8XiVXcz83EcEPDbuq/fzMdYWt+6GTmMsX2rh9q3TtsxNrzXhKt1rwg6rR1Dhdjqo7QMlWOvoWTF4imxQMRF7eaL8L5ohmwrLgDB8pCQc8DlcG7y538CDABJNGPqfaJgfgAAAABJRU5ErkJggg=="
mode="scaleToFill">
</image>
<view class="upload-progress" v-if="item.progress < 100">{{item.progress}}%</view>
</view>
</block>
<view class="upload-add-btn" v-if="rduLength > 0" @click="chooseImage"></view>
</view>
</template>
<script>
export default {
data() {
return {
imageList: []
};
},
props: {
url: {
type: String,
value: '' //上传接口地址
},
count: {
type: Number,
value: 4 //单次可选择的图片数量
},
length: {
type: Number,
value: 50 //可上传总数量
}
},
computed: {
rduLength(){
return this.length - this.imageList.length;
}
},
methods: {
//选择图片
chooseImage: function(){
uni.chooseImage({
count: this.rduLength < this.count ? this.rduLength : this.count, //最多可以选择的图片张数,默认9
sizeType: ['original', 'compressed'], //original 原图,compressed 压缩图,默认二者都有
sourceType: ['album'], //album 从相册选图,camera 使用相机,默认二者都有
success: (res)=> {
const images = res.tempFilePaths;
this.uploadFiles(images);
}
});
},
//上传图片
async uploadFiles(images){
this.imageList.push({
filePath: images[0],
progress: 0
});
uni.showLoading({
title: '请稍后..',
mask: true,
})
try{
const uploadUrl = await this.uploadImage(images[0]);
}catch(err){
console.log(err);
return;
}
if(uploadUrl !== false){
images.splice(0, 1);
this.imageList[this.imageList.length - 1].src = uploadUrl;
//判断是否需要继续上传
if(images.length > 0 && this.rduLength > 0){
this.uploadFiles(images);
}else{
uni.hideLoading();
}
}else{
//上传失败处理
this.imageList.pop();
uni.hideLoading();
uni.showToast({
title: '上传中出现问题,已终止上传',
icon: 'none',
mask: true,
duration: 2000
});
}
},
uploadImage: function(file){
return new Promise((resolve, reject)=> {
//发送给后端的附加参数
const formData = {
thumb_mode: 1,
};
this.uploadTask = uni.uploadFile({
url: this.url,
filePath: file,
name: 'file',
formData: formData,
success(uploadFileResult){
const uploadFileRes = JSON.parse(uploadFileResult.data) || {};
if(uploadFileRes.status === 1 && uploadFileRes.data){
resolve(uploadFileRes.data);
}else{
reject('接口返回错误');
}
},
fail(){
reject('网络链接错误');
}
});
//上传进度
this.uploadTask.onProgressUpdate((progressRes)=> {
this.imageList[this.imageList.length - 1].progress = progressRes.progress;
});
});
},
//删除图片
delImage: function(index){
uni.showModal({
content: '确定要放弃这张图片么?',
success: (confirmRes)=> {
if (confirmRes.confirm) {
this.imageList.splice(index, 1);
}
}
});
},
//预览图片
previewImage: function(index){
const urls = [];
this.imageList.forEach((item)=> {
urls.push(item.filePath);
})
uni.previewImage({
current: urls[index],
urls: urls,
indicator: "number"
})
}
}
}
</script>
<style lang="scss">
.upload-content{
padding:24upx 0 0 28upx;
background-color: #fff;
overflow:hidden;
}
.upload-item{
position: relative;
float:left;
width:150upx;
height:150upx;
margin-right:30upx;
margin-bottom:30upx;
&:nth-child(4n){
margin-right:0;
}
.upload-img{
width:100%;
height:100%;
border-radius:8upx;
}
.upload-del-btn{
position: absolute;
right:-16upx;
top:-14upx;
width:36upx;
height:36upx;
border: 4upx solid #fff;
border-radius: 100px;
}
.upload-progress{
position: absolute;
left:0;
top:0;
display:flex;
align-items:center;
justify-content: center;
width:100%;
height:100%;
background-color: rgba(0,0,0,.4);
color:#fff;
font-size:24upx;
border-radius:8upx;
}
}
.upload-add-btn {
position: relative;
float:left;
width: 150upx;
height: 150upx;
z-index: 99;
border-radius:8upx;
background:#f9f9f9;
&:before,
&:after {
content: " ";
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
width: 4upx;
height: 60upx;
background-color: #d6d6d6;
}
&:after {
width: 60upx;
height: 4upx;
}
&:active {
background-color: #f7f7f7;
}
}
</style>
**插件使用说明**
- 基于 Promise 对象实现更简单的 request 使用方式,支持请求和响应拦截
- 支持全局挂载
- 支持多个全局配置实例
- 支持自定义验证器
- 支持文件上传(如不使用可以删除class里upload 方法)
- 支持` typescript `` javascript ` 版本(如果不使用ts版本,则可以把luch-request-ts 文件夹删除)
- 下载后把 http-request 文件夹放到项目 utils/ 目录下
**Example**
---
创建实例
``` javascript
const http = new Request();
```
执行` GET `请求
``` javascript
http.get('/user/login', {params: {userName: 'name', password: '123456'}}).then(res => {
}).catch(err => {
})
// 局部修改配置,局部配置优先级高于全局配置
http.get('/user/login', {
params: {userName: 'name', password: '123456'}, /* 会加在url上 */
header: {}, /* 会覆盖全局header */
dataType: 'json',
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
// #ifndef MP-ALIPAY || APP-PLUS
responseType: 'text',
// #endif
// #ifdef MP-ALIPAY
timeout: 30000, // 仅支付宝小程序支持
// #endif
// #ifdef APP-PLUS
sslVerify: true // 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
// #endif
}).then(res => {
}).catch(err => {
})
```
执行` POST `请求
``` javascript
http.post('/user/login', {userName: 'name', password: '123456'} ).then(res => {
}).catch(err => {
})
// 局部修改配置,局部配置优先级高于全局配置
http.post('/user/login', {userName: 'name', password: '123456'}, {
params: {}, /* 会加在url上 */
header: {}, /* 会覆盖全局header */
dataType: 'json',
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
// #ifndef MP-ALIPAY || APP-PLUS
responseType: 'text',
// #endif
// #ifdef MP-ALIPAY
timeout: 30000, // 仅支付宝小程序支持
// #endif
// #ifdef APP-PLUS
sslVerify: true // 验证 ssl 证书 仅5+App安卓端支持(HBuilderX 2.3.3+)
// #endif
}).then(res => {
}).catch(err => {
})
```
执行` upload `请求
``` javascript
http.upload('api/upload/img', {
files: [], // 仅5+App支持
fileType'image/video/audio', // 仅支付宝小程序,且必填。
filePath: '', // 要上传文件资源的路径。
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
custom: {auth: true}, // 可以加一些自定义参数,在拦截器等地方使用。比如这里我加了一个auth,可在拦截器里拿到,如果true就传token
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {},
formData: {}, // HTTP 请求中其他额外的 form data
}).then(res => {
}).catch(err => {
})
```
**luch-request API**
--
``` javascript
http.request({
method: 'POST', // 请求方法必须大写
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
},
params: { // 会拼接到url上
token: '1111'
},
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
custom: {} // 自定义参数
})
具体参数说明:[uni.uploadFile](https://uniapp.dcloud.io/api/request/network-file)
http.upload('api/upload/img', {
files: [], // 仅5+App支持
fileType:'image/video/audio', // 仅支付宝小程序,且必填。
filePath: '', // 要上传文件资源的路径。
name: 'file', // 文件对应的 key , 开发者在服务器端通过这个 key 可以获取到文件二进制内容
header: {}, // 如填写,会覆盖全局header,
custom: {} // 自定义参数
formData: {}, // HTTP 请求中其他额外的 form data
})
```
请求方法别名 / 实例方法
``` javascript
http.request(config)
http.get(url[, config])
http.upload(url[, config])
http.delete(url[, data[, config]])
http.head(url[, data[, config]])
http.post(url[, data[, config]])
http.put(url[, data[, config]])
http.connect(url[, data[, config]])
http.options(url[, data[, config]])
http.trace(url[, data[, config]])
```
**全局请求配置**
--
``` javascript
{
baseUrl: '',
header: {
'content-type': 'application/json;charset=UTF-8'
},
method: 'GET',
dataType: 'json',
// #ifndef MP-ALIPAY || APP-PLUS
responseType: 'text',
// #endif
// 注:如果局部custom与全局custom有同名属性,则后面的属性会覆盖前面的属性,相当于Object.assign(全局,局部)
custom: {}, // 全局自定义参数默认值
// #ifdef MP-ALIPAY
timeout: 30000,
// #endif
// #ifdef APP-PLUS
sslVerify: true
// #endif
}
```
全局配置修改` setConfig `
``` javascript
/**
* @description 修改全局默认配置
* @param {Function}
*/
http.setConfig((config) => { /* config 为默认全局配置*/
config.baseUrl = 'http://www.bbb.cn'; /* 根域名 */
config.header = {
a: 1,
b: 2
}
return config
})
```
自定义验证器` validateStatus `
``` javascript
/**
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode(只读)
* @return { Boolean } 如果为true,则 resolve, 否则 reject
*/
http.validateStatus = (statusCode) => { // 默认
return statusCode === 200
}
// 举个栗子
http.validateStatus = (statusCode) => {
return statusCode && statusCode >= 200 && statusCode < 300
}
```
**拦截器**
--
在请求之前拦截
``` javascript
/**
* @param { Function} cancel - 取消请求,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行; 不会进入响应拦截器
*
* @param {String} text ['handle cancel'| any] - catch((err) => {}) err.errMsg === 'handle cancel'。非必传,默认'handle cancel'
* @cancel {Object} config - catch((err) => {}) err.config === config; 非必传,默认为request拦截器修改之前的config
* function cancel(text, config) {}
*/
http.interceptor.request((config, cancel) => { /* cancel 为函数,如果调用会取消本次请求。需要注意:调用cancel,本次请求的catch仍会执行。必须return config */
config.header = {
...config.header,
a: 1
}
// if (config.custom.auth) {
// config.header.token = 'token'
// }
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
cancel('token 不存在', config) // 把修改后的config传入,之后响应就可以拿到修改后的config。 如果调用了cancel但是不传修改后的config,则catch((err) => {}) err.config 为request拦截器修改之前的config
}
*/
return config;
})
```
在请求之后拦截
``` javascript
http.interceptor.response((response) => { /* 对响应成功做点什么 (statusCode === 200),必须return response*/
// if (response.data.code !== 200) { // 服务端返回的状态码不等于200,则reject()
// return Promise.reject(response)
// }
// if (response.config.custom.verification) { // 演示自定义参数的作用
// return response.data
// }
console.log(response)
return response
}, (response) => { /* 对响应错误做点什么 (statusCode !== 200),必须return response*/
console.log(response)
return response
})
```
**typescript使用**
--
` request.ts `里还暴露了五个接口
```javascript
{
options, // request 方法配置参数
handleOptions, // get/post 方法配置参数
config, // init 全局config接口(setConfig 参数接口)
requestConfig, // 请求之前参数配置项
response // 响应体
}
```
**常见问题**
--
1. 为什么会请求两次?
- 总有些小白问这些很那啥的问题,有两种可能,一种是‘post三次握手’(不知道的请先给个五星好评,然后打自己一巴掌,并问自己,为什么这都不知道),还有一种可能是`本地访问接口时跨域请求,所以浏览器会先发一个option 去预测能否成功,然后再发一个真正的请求`(没有自己观察请求头,Request Method,就跑来问的,请再打自己一巴掌,并问自己,为什么这都不知道,不知道也行,为什么不百度)。
2. 如何跨域?
- 问的人不少,可以先百度了解一下。<a href="https://ask.dcloud.net.cn/article/35267" target="_blank">如何跨域</a>
3. post 怎么传不了数组的参数啊?
- <a href="https://uniapp.dcloud.io/api/request/request">uni-request</a> <br>
可以点击看一下uni-request 的api 文档,data支持的文件类型只有<code>Object/String/ArrayBuffer</code>这个真跟我没啥关系 0.0
4. 'Content-Type' 为什么要小写?
- hbuilderX 更新至‘2.3.0.20190919’ 后,uni.request post请求,如果 ‘Content-Type’ 大写,就会在后面自动拼接‘ application/json’,请求头变成
`Content-Type: application/json;charset=UTF-8 application/json`,导致后端无法解析类型,`Status Code 415`,post 请求失败。但是小写就不会出现这个问题。至于为什么我也没有深究,我现在也不清楚这是他们的bug,还是以后就这样规范了。我能做的只有立马兼容,至于后边uni官方会不会继续变动也不清楚。
5. 为什么不支持task?
- 一方面精力有限,另一方面违背了本人的一些意愿,具体看第6条
6. 为什么不能配置超时时间?
- 配置超时时间,请求时需要task,并且每个请求都需要创建一个定时器,本人认为这个消耗没必要。设置超时时间可以通过<a href="https://uniapp.dcloud.io/collocation/manifest?id=networktimeout" target="_blank">manifest.json 配置</a>进行设置。我想用的就是一个小而简单的请求插件。
**tip**
--
- 不想使用upload 可把class 里的upload 删除
**issue**
--
有任何问题或者建议可以=> <a href="https://ask.dcloud.net.cn/question/74922" target="_blank">issue提交</a>,先给个五星好评QAQ!!
**作者想说**
--
- 主体代码9kb
- 目前该插件已经上项目,遇到任何问题请先检查自己的代码(排除新版本发布的情况)。最近新上了` typescript ` 版本,因为本人没使用过ts,所以写的不好的地方,还请见谅~
- 写代码很容易,为了让你们看懂写文档真的很lei 0.0
- 最近发现有插件与我雷同,当初接触uni-app 就发现插件市场虽然有封装的不错的request库,但是都没有对多全局配置做处理,都是通过修改源码的方式配置。我首先推出通过class类,并仿照axios的api实现request请求库,并起名‘仿axios封装request网络请求库,支持拦截器全局配置’。他们虽然修改了部分代码,但是功能与性能并没有优化,反而使代码很冗余。希望能推出新的功能,和性能更加强悍的请求库。
- 任何形式的‘参考’、‘借鉴’,请标明作者
```javascript
<a href="https://ext.dcloud.net.cn/plugin?id=392">luch-request</a>
```
- 关于问问题
1. 首先请善于利用搜索引擎,不管百度,还是Google,遇到问题请先自己尝试解决。自己尝试过无法解决,再问。
2. 不要问类似为什么我的xx无法使用这种问题。请仔细阅读文档,检查代码,或者说明运行环境,把相关代码贴至评论或者发送至我的邮箱,还可以点击上面的issue提交,在里面提问,可能我在里面已经回答了。
3. 我的代码如果真的出现bug,或者你有好的建议、需求,可以提issue,我看到后会立即解决
4. 不要问一些弱智问题!!!
- 如何问问题
1. 仔细阅读文档,检查代码
2. 说明运行环境,比如:app端 ios、android 版本号、手机机型、普遍现象还是个别现象(越详细越好)
3. 发出代码片段或者截图至邮箱(很重要)
4. 或者可以在上方的'issue提交' 里发出详细的问题描述
5. 以上都觉得解决不了你的问题,可以加QQ:`370306150`
**土豪赞赏**
--
<img src="https://img-cdn-qiniu.dcloud.net.cn/uploads/answer/20191014/0d9fff1e6a57a83024787224593b39c1.png" width="150">
####创作不易,五星好评你懂得!
/**
* Request 1.0.5
* @Class Request
* @description luch-request 1.0.4 http请求插件
* @Author lu-ch
* @Date 2019-12-12
* @Email webwork.s@qq.com
* http://ext.dcloud.net.cn/plugin?id=392
*/
export default class Request {
config = {
baseUrl: '',
header: {
'content-type': 'application/json;charset=UTF-8'
},
method: 'GET',
dataType: 'json',
// #ifndef MP-ALIPAY || APP-PLUS
responseType: 'text',
// #endif
custom: {},
// #ifdef MP-ALIPAY
timeout: 30000,
// #endif
// #ifdef APP-PLUS
sslVerify: true
// #endif
}
static posUrl (url) { /* 判断url是否为绝对路径 */
return /(http|https):\/\/([\w.]+\/?)\S*/.test(url)
}
static addQueryString (params) {
let paramsData = ''
Object.keys(params).forEach(function (key) {
paramsData += key + '=' + encodeURIComponent(params[key]) + '&'
})
return paramsData.substring(0, paramsData.length - 1)
}
/**
* @property {Function} request 请求拦截器
* @property {Function} response 响应拦截器
* @type {{request: Request.interceptor.request, response: Request.interceptor.response}}
*/
interceptor = {
/**
* @param {Request~requestCallback} cb - 请求之前拦截,接收一个函数(config, cancel)=> {return config}。第一个参数为全局config,第二个参数为函数,调用则取消本次请求。
*/
request: (cb) => {
if (cb) {
this.requestBeforeFun = cb
}
},
/**
* @param {Request~responseCallback} cb 响应拦截器,对响应数据做点什么
* @param {Request~responseErrCallback} ecb 响应拦截器,对响应错误做点什么
*/
response: (cb, ecb) => {
if (cb && ecb) {
this.requestComFun = cb
this.requestComFail = ecb
}
}
}
requestBeforeFun (config) {
return config
}
requestComFun (response) {
return response
}
requestComFail (response) {
return response
}
/**
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode(只读)
* @return { Boolean } 如果为true,则 resolve, 否则 reject
*/
validateStatus (statusCode) {
return statusCode === 200
}
/**
* @Function
* @param {Request~setConfigCallback} f - 设置全局默认配置
*/
setConfig (f) {
this.config = f(this.config)
}
/**
* @Function
* @param {Object} options - 请求配置项
* @prop {String} options.url - 请求路径
* @prop {Object} options.data - 请求参数
* @prop {Object} [options.responseType = config.responseType] [text|arraybuffer] - 响应的数据类型
* @prop {Object} [options.dataType = config.dataType] - 如果设为 json,会尝试对返回的数据做一次 JSON.parse
* @prop {Object} [options.header = config.header] - 请求header
* @prop {Object} [options.method = config.method] - 请求方法
* @returns {Promise<unknown>}
*/
async request (options = {}) {
options.baseUrl = this.config.baseUrl
options.dataType = options.dataType || this.config.dataType
// #ifndef MP-ALIPAY || APP-PLUS
options.responseType = options.responseType || this.config.responseType
// #endif
// #ifdef MP-ALIPAY
options.timeout = options.timeout || this.config.timeout
// #endif
options.url = options.url || ''
options.data = options.data || {}
options.params = options.params || {}
options.header = options.header || this.config.header
options.method = options.method || this.config.method
options.custom = { ...this.config.custom, ...(options.custom || {}) }
// #ifdef APP-PLUS
options.sslVerify = options.sslVerify === undefined ? this.config.sslVerify : options.sslVerify
// #endif
return new Promise((resolve, reject) => {
let next = true
let handleRe = {}
options.complete = (response) => {
response.config = handleRe
if (this.validateStatus(response.statusCode)) { // 成功
response = this.requestComFun(response)
resolve(response)
} else {
response = this.requestComFail(response)
reject(response)
}
}
const cancel = (t = 'handle cancel', config = options) => {
const err = {
errMsg: t,
config: config
}
reject(err)
next = false
}
handleRe = { ...this.requestBeforeFun(options, cancel) }
const _config = { ...handleRe }
if (!next) return
delete _config.custom
let mergeUrl = Request.posUrl(_config.url) ? _config.url : (_config.baseUrl + _config.url)
if (JSON.stringify(_config.params) !== '{}') {
const paramsH = Request.addQueryString(_config.params)
mergeUrl += mergeUrl.indexOf('?') === -1 ? `?${paramsH}` : `&${paramsH}`
}
_config.url = mergeUrl
uni.request(_config)
})
}
get (url, options = {}) {
return this.request({
url,
method: 'GET',
...options
})
}
post (url, data, options = {}) {
return this.request({
url,
data,
method: 'POST',
...options
})
}
// #ifndef MP-ALIPAY
put (url, data, options = {}) {
return this.request({
url,
data,
method: 'PUT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
delete (url, data, options = {}) {
return this.request({
url,
data,
method: 'DELETE',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN
connect (url, data, options = {}) {
return this.request({
url,
data,
method: 'CONNECT',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
head (url, data, options = {}) {
return this.request({
url,
data,
method: 'HEAD',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN || MP-BAIDU
options (url, data, options = {}) {
return this.request({
url,
data,
method: 'OPTIONS',
...options
})
}
// #endif
// #ifdef APP-PLUS || H5 || MP-WEIXIN
trace (url, data, options = {}) {
return this.request({
url,
data,
method: 'TRACE',
...options
})
}
// #endif
upload (url, {
// #ifdef APP-PLUS
files,
// #endif
// #ifdef MP-ALIPAY
fileType,
// #endif
filePath,
name,
header,
formData,
custom
}) {
return new Promise((resolve, reject) => {
let next = true
let handleRe = {}
const globalHeader = { ...this.config.header }
delete globalHeader['content-type']
const pubConfig = {
baseUrl: this.config.baseUrl,
url,
// #ifdef APP-PLUS
files,
// #endif
// #ifdef MP-ALIPAY
fileType,
// #endif
filePath,
method: 'UPLOAD',
name,
header: header || globalHeader,
formData,
custom: { ...this.config.custom, ...(custom || {}) },
complete: (response) => {
response.config = handleRe
if (response.statusCode === 200) { // 成功
response = this.requestComFun(response)
resolve(response)
} else {
response = this.requestComFail(response)
reject(response)
}
}
}
const cancel = (t = 'handle cancel', config = pubConfig) => {
const err = {
errMsg: t,
config: config
}
reject(err)
next = false
}
handleRe = { ...this.requestBeforeFun(pubConfig, cancel) }
const _config = { ...handleRe }
if (!next) return
delete _config.custom
_config.url = Request.posUrl(_config.url) ? _config.url : (_config.baseUrl + _config.url)
uni.uploadFile(_config)
})
}
}
/**
* setConfig回调
* @return {Object} - 返回操作后的config
* @callback Request~setConfigCallback
* @param {Object} config - 全局默认config
*/
/**
* 请求拦截器回调
* @return {Object} - 返回操作后的config
* @callback Request~requestCallback
* @param {Object} config - 全局config
* @param {Function} [cancel] - 取消请求钩子,调用会取消本次请求
*/
/**
* 响应拦截器回调
* @return {Object} - 返回操作后的response
* @callback Request~responseCallback
* @param {Object} response - 请求结果 response
*/
/**
* 响应错误拦截器回调
* @return {Object} - 返回操作后的response
* @callback Request~responseErrCallback
* @param {Object} response - 请求结果 response
*/
import Vue from 'vue'
import store from './store'
import App from './App'
const msg = (title, duration=1500, mask=false, icon='none')=>{
//统一提示方便全局修改
if(Boolean(title) === false){
return;
}
uni.showToast({
title,
duration,
mask,
icon
});
}
const prePage = ()=>{
let pages = getCurrentPages();
let prePage = pages[pages.length - 2];
// #ifdef H5
return prePage;
// #endif
return prePage.$vm;
}
Vue.config.productionTip = false
Vue.prototype.$fire = new Vue();
Vue.prototype.$store = store;
Vue.prototype.$api = {msg, prePage};
App.mpType = 'app'
const app = new Vue({
...App
})
app.$mount()
\ No newline at end of file
{
"name" : "mall-app-web",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
"app-plus" : {
/* 5+App特有相关 */
"usingComponents" : true,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
"modules" : {},
/* 模块配置 */
"distribute" : {
/* 应用发布信息 */
"android" : {
/* android打包配置 */
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.READ_CONTACTS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.WRITE_CONTACTS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.RECORD_AUDIO\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.MODIFY_AUDIO_SETTINGS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.CALL_PHONE\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
"ios" : {},
/* ios打包配置 */
"sdkConfigs" : {}
}
},
/* SDK配置 */
"quickapp" : {},
/* 快应用特有相关 */
"mp-weixin" : {
/* 小程序特有相关 */
"usingComponents" : true,
"appid" : "",
"setting" : {
"urlCheck" : true
}
},
"h5" : {
"devServer" : {
"https" : false,
"port" : 8060
},
"domain" : "localhost",
"router" : {
"base" : ""
}
}
}
{
"pages": [
{
"path": "pages/index/index",
"style": {
// #ifdef MP
"navigationBarTitleText": "Mall商城",
//"navigationStyle": "custom",
// #endif
"enablePullDownRefresh": true,
"app-plus": {
"titleNView": {
"type": "transparent",
"searchInput": {
"backgroundColor": "rgba(231, 231, 231,.7)",
"borderRadius": "16px",
"placeholder": "请输入商品 如:手机",
"disabled": true,
"placeholderColor": "#606266"
},
"buttons": [{
"fontSrc": "/static/yticon.ttf",
"text": "\ue60d",
"fontSize": "26",
"color": "#303133",
"float": "left",
"background": "rgba(0,0,0,0)"
},
{
"fontSrc": "/static/yticon.ttf",
"text": "\ue744",
"fontSize": "27",
"color": "#303133",
"background": "rgba(0,0,0,0)",
"redDot": true
}
]
}
}
}
},
{
"path": "pages/product/product",
"style": {
"navigationBarTitleText": "详情展示",
"app-plus": {
"titleNView": {
"type": "transparent"
}
}
}
}, {
"path": "pages/set/set",
"style": {
"navigationBarTitleText": "设置"
}
},
{
"path": "pages/userinfo/userinfo",
"style": {
"navigationBarTitleText": "修改资料"
}
}, {
"path": "pages/cart/cart",
"style": {
"navigationBarTitleText": "购物车"
}
}, {
"path": "pages/public/login",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false,
"animationType": "slide-in-bottom"
}
}
}, {
"path": "pages/public/register",
"style": {
"navigationBarTitleText": "",
"navigationStyle": "custom",
"app-plus": {
"titleNView": false,
"animationType": "slide-in-bottom"
}
}
}, {
"path": "pages/user/user",
"style": {
"navigationBarTitleText": "我的",
// #ifdef MP
"navigationStyle": "custom",
// #endif
"app-plus": {
"bounce": "none",
"titleNView": {
"type": "transparent",
"buttons": [{
"fontSrc": "/static/yticon.ttf",
"text": "\ue60f",
"fontSize": "24",
"color": "#303133",
"width": "46px",
"background": "rgba(0,0,0,0)"
},
{
"fontSrc": "/static/yticon.ttf",
"text": "\ue744",
"fontSize": "28",
"color": "#303133",
"background": "rgba(0,0,0,0)",
"redDot": true
}
]
}
}
}
}, {
"path": "pages/order/order",
"style": {
"navigationBarTitleText": "我的订单",
"app-plus": {
"bounce": "none"
}
}
}, {
"path": "pages/money/money",
"style": {}
}, {
"path": "pages/order/createOrder",
"style": {
"navigationBarTitleText": "创建订单"
}
}, {
"path": "pages/order/orderDetail",
"style": {
"navigationBarTitleText": "订单详情"
}
}, {
"path": "pages/address/address",
"style": {
"navigationBarTitleText": "收货地址"
}
}, {
"path": "pages/address/addressManage",
"style": {
"navigationBarTitleText": ""
}
}, {
"path": "pages/money/pay",
"style": {
"navigationBarTitleText": "支付"
}
},
{
"path": "pages/money/paySuccess",
"style": {
"navigationBarTitleText": "支付成功"
}
}, {
"path": "pages/notice/notice",
"style": {
"navigationBarTitleText": "通知"
}
}, {
"path": "pages/category/category",
"style": {
"navigationBarTitleText": "分类",
"app-plus": {
"bounce": "none"
}
}
}, {
"path": "pages/product/list",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "商品列表"
}
}, {
"path": "pages/coupon/couponList",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "优惠券列表"
}
}, {
"path": "pages/brand/brandDetail",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "品牌详情"
}
}, {
"path": "pages/brand/list",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "推荐品牌列表"
}
}, {
"path": "pages/product/newProductList",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "新鲜好物"
}
}, {
"path": "pages/product/hotProductList",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "人气推荐"
}
}, {
"path": "pages/user/readHistory",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的足迹",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "清空",
"fontSize": "16",
"color": "#303133",
"width": "46px",
"background": "rgba(0,0,0,0)"
}]
}
}
}
},{
"path": "pages/user/productCollection",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的收藏",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "清空",
"fontSize": "16",
"color": "#303133",
"width": "46px",
"background": "rgba(0,0,0,0)"
}]
}
}
}
},{
"path": "pages/user/brandAttention",
"style": {
"enablePullDownRefresh": true,
"navigationBarTitleText": "我的关注",
"app-plus": {
"titleNView": {
"buttons": [{
"text": "清空",
"fontSize": "16",
"color": "#303133",
"width": "46px",
"background": "rgba(0,0,0,0)"
}]
}
}
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "mall-app-web",
"navigationBarBackgroundColor": "#FFFFFF",
"backgroundColor": "#f8f8f8"
},
"tabBar": {
"color": "#C0C4CC",
"selectedColor": "#fa436a",
"borderStyle": "black",
"backgroundColor": "#ffffff",
"list": [{
"pagePath": "pages/index/index",
"iconPath": "static/tab-home.png",
"selectedIconPath": "static/tab-home-current.png",
"text": "首页"
},
{
"pagePath": "pages/category/category",
"iconPath": "static/tab-cate.png",
"selectedIconPath": "static/tab-cate-current.png",
"text": "分类"
},
{
"pagePath": "pages/cart/cart",
"iconPath": "static/tab-cart.png",
"selectedIconPath": "static/tab-cart-current.png",
"text": "购物车"
},
{
"pagePath": "pages/user/user",
"iconPath": "static/tab-my.png",
"selectedIconPath": "static/tab-my-current.png",
"text": "我的"
}
]
}
}
<template>
<view class="content b-t">
<view class="list b-b" v-for="(item, index) in addressList" :key="index" @click="checkAddress(item)">
<view class="wrapper">
<view class="address-box">
<text v-if="item.defaultStatus==1" class="tag">默认</text>
<text class="address">{{item.province}} {{item.city}} {{item.region}} {{item.detailAddress}}</text>
</view>
<view class="u-box">
<text class="name">{{item.name}}</text>
<text class="mobile">{{item.phoneNumber}}</text>
</view>
</view>
<text class="yticon icon-bianji" @click.stop="addAddress('edit', item)"></text>
<text class="yticon icon-iconfontshanchu1" @click.stop="handleDeleteAddress(item.id)"></text>
</view>
<button class="add-btn" @click="addAddress('add')">新增地址</button>
</view>
</template>
<script>
import {
fetchAddressList,
deleteAddress
} from '@/api/address.js';
export default {
data() {
return {
source: 0,
addressList: []
}
},
onLoad(option) {
console.log(option.source);
this.source = option.source;
this.loadData();
},
methods: {
async loadData() {
fetchAddressList().then(response => {
this.addressList = response.data;
});
},
//选择地址
checkAddress(item) {
if (this.source == 1) {
//this.$api.prePage()获取上一页实例,在App.vue定义
this.$api.prePage().currentAddress = item;
uni.navigateBack()
}
},
addAddress(type, item) {
if (type == 'edit') {
uni.navigateTo({
url: `/pages/address/addressManage?type=${type}&id=${item.id}`
})
} else {
uni.navigateTo({
url: `/pages/address/addressManage?type=${type}`
})
}
},
//处理删除地址
handleDeleteAddress(id){
let superThis = this;
uni.showModal({
title: '提示',
content: '是否要删除该地址',
success: function (res) {
if (res.confirm) {
deleteAddress(id).then(response=>{
superThis.loadData();
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
//添加或修改成功之后回调
refreshList(data, type) {
//添加或修改后事件,这里直接在最前面添加了一条数据,实际应用中直接刷新地址列表即可
// this.addressList.unshift(data);
this.loadData();
console.log(data, type);
}
}
}
</script>
<style lang='scss'>
page {
padding-bottom: 120upx;
}
.content {
position: relative;
}
.list {
display: flex;
align-items: center;
padding: 20upx 30upx;
;
background: #fff;
position: relative;
}
.wrapper {
display: flex;
flex-direction: column;
flex: 1;
}
.address-box {
display: flex;
align-items: center;
.tag {
font-size: 24upx;
color: $base-color;
margin-right: 10upx;
background: #fffafb;
border: 1px solid #ffb4c7;
border-radius: 4upx;
padding: 4upx 10upx;
line-height: 1;
}
.address {
font-size: 30upx;
color: $font-color-dark;
}
}
.u-box {
font-size: 28upx;
color: $font-color-light;
margin-top: 16upx;
.name {
margin-right: 30upx;
}
}
.icon-bianji {
display: flex;
align-items: center;
height: 80upx;
font-size: 40upx;
color: $font-color-light;
padding-left: 30upx;
}
.icon-iconfontshanchu1 {
display: flex;
align-items: center;
height: 80upx;
font-size: 40upx;
color: $font-color-light;
padding-left: 30upx;
}
.add-btn {
position: fixed;
left: 30upx;
right: 30upx;
bottom: 16upx;
z-index: 95;
display: flex;
align-items: center;
justify-content: center;
width: 690upx;
height: 80upx;
font-size: 32upx;
color: #fff;
background-color: $base-color;
border-radius: 10upx;
box-shadow: 1px 2px 5px rgba(219, 63, 96, 0.4);
}
</style>
<template>
<view class="content">
<view class="row b-b">
<text class="tit">姓名</text>
<input class="input" type="text" v-model="addressData.name" placeholder="收货人姓名" placeholder-class="placeholder" />
</view>
<view class="row b-b">
<text class="tit">手机号码</text>
<input class="input" type="number" v-model="addressData.phoneNumber" placeholder="收货人手机号码" placeholder-class="placeholder" />
</view>
<view class="row b-b">
<text class="tit">邮政编码</text>
<input class="input" type="number" v-model="addressData.postCode" placeholder="收货人邮政编码" placeholder-class="placeholder" />
</view>
<!-- <view class="row b-b">
<text class="tit">所在区域</text>
<text @click="chooseLocation" class="input">
{{addressData.province}} {{addressData.city}} {{addressData.region}}
</text>
<text class="yticon icon-shouhuodizhi" @click="chooseLocation"></text>
</view> -->
<view class="row b-b">
<text class="tit">所在区域</text>
<input class="input" type="text" v-model="addressData.prefixAddress" placeholder="所在区域" placeholder-class="placeholder" />
</view>
<view class="row b-b">
<text class="tit">详细地址</text>
<input class="input" type="text" v-model="addressData.detailAddress" placeholder="详细地址" placeholder-class="placeholder" />
</view>
<view class="row default-row">
<text class="tit">设为默认</text>
<switch :checked="addressData.defaultStatus==1" color="#fa436a" @change="switchChange" />
</view>
<button class="add-btn" @click="confirm">提交</button>
</view>
</template>
<script>
import {
addAddress,
updateAddress,
fetchAddressDetail
} from '@/api/address.js';
export default {
data() {
return {
addressData: {
name: '',
phoneNumber: '',
postCode: '',
detailAddress: '',
default: false,
province: '',
city: '',
region: '',
prefixAddress: ''
}
}
},
onLoad(option) {
let title = '新增收货地址';
if (option.type === 'edit') {
title = '编辑收货地址'
fetchAddressDetail(option.id).then(response=>{
this.addressData = response.data;
this.addressData.prefixAddress = this.addressData.province+this.addressData.city+this.addressData.region;
});
}
this.manageType = option.type;
uni.setNavigationBarTitle({
title
})
},
methods: {
switchChange(e) {
this.addressData.defaultStatus = e.detail.value ? 1 : 0;
},
//地图选择地址
chooseLocation() {
uni.chooseLocation({
success: (data) => {
this.covertAdderss(data.address);
this.addressData.detailAddress = data.name;
}
})
},
//将地址转化为省市区
covertAdderss(address) {
console.log("covertAdderss", address);
if (address.indexOf("省") != -1) {
this.addressData.province = address.substr(0, address.indexOf("省") + 1);
address = address.replace(this.addressData.province, "");
this.addressData.city = address.substr(0, address.indexOf("市") + 1);
address = address.replace(this.addressData.city, "");
this.addressData.region = address.substr(0, address.indexOf("区") + 1);
} else {
this.addressData.province = address.substr(0, address.indexOf("市") + 1);
address = address.replace(this.addressData.province, "");
this.addressData.city = "";
this.addressData.region = address.substr(0, address.indexOf("区") + 1);
}
},
//提交
confirm() {
let data = this.addressData;
if (!data.name) {
this.$api.msg('请填写收货人姓名');
return;
}
if (!/(^1[3|4|5|7|8][0-9]{9}$)/.test(data.phoneNumber)) {
this.$api.msg('请输入正确的手机号码');
return;
}
if (!data.prefixAddress) {
this.$api.msg('请输入区域');
return;
}
this.covertAdderss(data.prefixAddress);
if (!data.province) {
this.$api.msg('请输入正确的省份');
return;
}
if (!data.detailAddress) {
this.$api.msg('请填写详细地址信息');
return;
}
if(this.manageType=='edit'){
updateAddress(this.addressData).then(response=>{
//this.$api.prePage()获取上一页实例,可直接调用上页所有数据和方法,在App.vue定义
this.$api.prePage().refreshList(data, this.manageType);
this.$api.msg("地址修改成功!");
setTimeout(() => {
uni.navigateBack()
}, 800)
});
}else{
addAddress(this.addressData).then(response=>{
//this.$api.prePage()获取上一页实例,可直接调用上页所有数据和方法,在App.vue定义
this.$api.prePage().refreshList(data, this.manageType);
this.$api.msg("地址添加成功!");
setTimeout(() => {
uni.navigateBack()
}, 800)
});
}
},
}
}
</script>
<style lang="scss">
page {
background: $page-color-base;
padding-top: 16upx;
}
.row {
display: flex;
align-items: center;
position: relative;
padding: 0 30upx;
height: 110upx;
background: #fff;
.tit {
flex-shrink: 0;
width: 150upx;
font-size: 30upx;
color: $font-color-dark;
}
.input {
flex: 1;
font-size: 30upx;
color: $font-color-dark;
}
.icon-shouhuodizhi {
font-size: 36upx;
color: $font-color-light;
}
}
.default-row {
margin-top: 16upx;
.tit {
flex: 1;
}
switch {
transform: translateX(16upx) scale(.9);
}
}
.add-btn {
display: flex;
align-items: center;
justify-content: center;
width: 690upx;
height: 80upx;
margin: 60upx auto;
font-size: $font-lg;
color: #fff;
background-color: $base-color;
border-radius: 10upx;
box-shadow: 1px 2px 5px rgba(219, 63, 96, 0.4);
}
</style>
<template>
<view>
<!-- 顶部大图 -->
<view class="top-image">
<view class="image-wrapper">
<image :src="brand.bigPic" class="loaded" mode="aspectFill"></image>
</view>
</view>
<!-- 品牌信息 -->
<view class="info">
<view class="image-wrapper">
<image :src="brand.logo" class="loaded" mode="aspectFit"></image>
</view>
<view class="title">
<text :class="{Skeleton:!loaded}">{{brand.name}}</text>
<text :class="{Skeleton:!loaded}">品牌首字母:{{brand.firstLetter}}</text>
</view>
<view>
<text class="yticon icon-shoucang" :class="{active: favoriteStatus}" @click="favorite()"></text>
</view>
</view>
<!-- 品牌故事 -->
<view class="section-tit">品牌故事</view>
<view class="brand-story">
<text class="text">{{brand.brandStory}}</text>
</view>
<!-- 相关商品 -->
<view class="section-tit">相关商品</view>
<view class="goods-list">
<view v-for="(item, index) in productList" :key="index" class="goods-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFill"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2">{{item.subTitle}}</text>
<view class="price-box">
<text class="price">{{item.price}}</text>
<text>已售 {{item.sale}}</text>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import share from '@/components/share';
import {
getBrandDetail,
fetchBrandProductList
} from '@/api/brand.js';
import {
createBrandAttention,
deleteBrandAttention,
brandAttentionDetail
} from '@/api/memberBrandAttention.js';
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
import {
mapState
} from 'vuex';
export default {
components: {
share
},
data() {
return {
loaded: false,
brand: {},
productList:[],
loadingType: 'more',
favoriteStatus:false,
queryParam: {
brandId: null,
pageNum: 1,
pageSize: 4
}
};
},
onLoad(options) {
this.loaded = true;
let id = options.id;
getBrandDetail(id).then(response => {
this.brand = response.data;
this.initBrandAttention();
});
this.queryParam.brandId = id;
this.loadData('refresh');
},
computed: {
...mapState(['hasLogin'])
},
methods: {
imageOnLoad(key, index) {
this.$set(this.data[key][index], 'loaded', 'loaded');
},
//收藏
favorite() {
if (!this.checkForLogin()) {
return;
}
if (this.favoriteStatus) {
//取消收藏
deleteBrandAttention({
brandId: this.brand.id
}).then(response => {
uni.showToast({
title: "取消收藏成功!",
icon: 'none'
});
this.favoriteStatus = !this.favoriteStatus;
});
} else {
//收藏
let brandAttention = {
brandId : this.brand.id,
brandName : this.brand.name,
brandLogo : this.brand.logo,
brandCity : ""
}
createBrandAttention(brandAttention).then(response=>{
uni.showToast({
title: "收藏成功!",
icon: 'none'
});
this.favoriteStatus = !this.favoriteStatus;
});
}
},
//详情
navToDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.queryParam.pageNum=1;
this.productList = [];
}
fetchBrandProductList(this.queryParam).then(response => {
let productList = response.data.list;
if (response.data.list.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.queryParam.pageNum--;
} else {
if (response.data.list.length < this.queryParam.pageSize) {
this.loadingType = 'nomore';
this.queryParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.productList = this.productList.concat(productList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.queryParam.pageNum++;
this.loadData();
},
//初始化收藏状态
initBrandAttention(){
if(this.hasLogin){
brandAttentionDetail({brandId:this.brand.id}).then(response=>{
this.favoriteStatus = response.data!=null;
});
}
},
//检查登录状态并弹出登录框
checkForLogin() {
if (!this.hasLogin) {
uni.showModal({
title: '提示',
content: '你还没登录,是否要登录?',
confirmText: '去登录',
cancelText: '取消',
success: function(res) {
if (res.confirm) {
uni.navigateTo({
url: '/pages/public/login'
})
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
return false;
} else {
return true;
}
},
}
}
</script>
<style lang="scss">
page {
background: $page-color-base;
}
.top-image {
height: 200px;
.image-wrapper {
display: flex;
justify-content: center;
align-content: center;
width: 100%;
height: 100%;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
}
.info {
display: flex;
align-items: center;
padding: 30upx 50upx;
background: #fff;
margin-top: 16upx;
.image-wrapper {
width: 210upx;
height: 70upx;
background: #fff;
image{
width:100%;
height: 100%;
}
}
.title {
flex: 1;
display: flex;
flex-direction: column;
font-size: $font-lg+4upx;
margin-left: 30upx;
color: $font-color-dark;
text:last-child {
font-size: $font-sm;
color: $font-color-light;
margin-top: 8upx;
&.Skeleton{
width:220upx;
}
}
}
.yticon {
font-size: 80upx;
color: $font-color-base;
margin: 0 10upx 0 30upx;
&.active {
color: #ff4443;
}
}
}
.brand-story {
display: flex;
padding: 30upx;
background: #fff;
.text {
font-size: $font-sm;
color: $font-color-light;
}
}
.actions {
padding: 10upx 28upx;
background: #fff;
.yticon {
font-size: 46upx;
color: $font-color-base;
padding: 10upx 12upx;
&.active {
color: #ff4443;
}
&:nth-child(2) {
font-size: 50upx;
}
}
}
.section-tit {
font-size: $font-base+2upx;
color: $font-color-dark;
background: #fff;
margin-top: 16upx;
text-align: center;
padding-top: 20upx;
padding-bottom: 20upx;
}
/* 商品列表 */
.goods-list {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.goods-item {
display: flex;
flex-direction: column;
width: 48%;
padding-bottom: 40upx;
&:nth-child(2n+1) {
margin-right: 4%;
}
}
.image-wrapper {
width: 100%;
height: 330upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price-box {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10upx;
font-size: 24upx;
color: $font-color-light;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 1;
&:before {
content: '¥';
font-size: 26upx;
}
}
}
</style>
<template>
<view class="content">
<image src="/static/recommend_brand_banner.png" class="banner-image"></image>
<view class="section-tit">相关品牌</view>
<view class="goods-list">
<view v-for="(item, index) in brandList" :key="index" class="goods-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.logo" mode="aspectFit"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2">商品数量:{{item.productCount}}</text>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import {
fetchBrandRecommendList
} from '@/api/brand.js';
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
export default {
components: {
uniLoadMore
},
data() {
return {
loadingType: 'more', //加载更多状态
brandList: [],
searchParam: {
pageNum: 1,
pageSize: 6
}
};
},
onLoad(options) {
this.loadData();
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
methods: {
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum=1;
this.brandList = [];
}
fetchBrandRecommendList(this.searchParam).then(response => {
let prandList = response.data;
if (response.data.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (response.data.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.brandList = this.brandList.concat(prandList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//详情
navToDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/brand/brandDetail?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.banner-image{
width: 100%;
}
.section-tit {
font-size: $font-base+2upx;
color: $font-color-dark;
background: #fff;
margin-top: 16upx;
text-align: center;
padding-top: 20upx;
padding-bottom: 20upx;
}
/* 商品列表 */
.goods-list {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.goods-item {
display: flex;
flex-direction: column;
width: 48%;
padding-bottom: 40upx;
&:nth-child(2n+1) {
margin-right: 4%;
}
}
.image-wrapper {
width: 100%;
height: 150upx;
border-radius: 3px;
overflow: hidden;
background-color: #fff;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price-box {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10upx;
font-size: 24upx;
color: $font-color-light;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 1;
}
}
</style>
<template>
<view class="container">
<!-- 空白页 -->
<view v-if="!hasLogin || empty===true" class="empty">
<image src="/static/emptyCart.jpg" mode="aspectFit"></image>
<view v-if="hasLogin" class="empty-tips">
空空如也
<navigator class="navigator" v-if="hasLogin" url="../index/index" open-type="switchTab">随便逛逛></navigator>
</view>
<view v-else class="empty-tips">
空空如也
<view class="navigator" @click="navToLogin">去登陆></view>
</view>
</view>
<view v-else>
<!-- 列表 -->
<view class="cart-list">
<block v-for="(item, index) in cartList" :key="item.id">
<view class="cart-item" :class="{'b-b': index!==cartList.length-1}">
<view class="image-wrapper">
<image :src="item.productPic" :class="[item.loaded]" mode="aspectFill" lazy-load @load="onImageLoad('cartList', index)"
@error="onImageError('cartList', index)"></image>
<view class="yticon icon-xuanzhong2 checkbox" :class="{checked: item.checked}" @click="check('item', index)"></view>
</view>
<view class="item-right">
<text class="clamp title">{{item.productName}}</text>
<text class="attr">{{item.spDataStr}}</text>
<text class="price">¥{{item.price}}</text>
<uni-number-box class="step" :min="1" :max="100" :value="item.quantity" :index="index" @eventChange="numberChange"></uni-number-box>
</view>
<text class="del-btn yticon icon-fork" @click="handleDeleteCartItem(index)"></text>
</view>
</block>
</view>
<!-- 底部菜单栏 -->
<view class="action-section">
<view class="checkbox">
<image :src="allChecked?'/static/selected.png':'/static/select.png'" mode="aspectFit" @click="check('all')"></image>
<view class="clear-btn" :class="{show: allChecked}" @click="clearCart">
清空
</view>
</view>
<view class="total-box">
<text class="price">¥{{total}}</text>
</view>
<button type="primary" class="no-border confirm-btn" @click="createOrder">去结算</button>
</view>
</view>
</view>
</template>
<script>
import {
mapState
} from 'vuex';
import uniNumberBox from '@/components/uni-number-box.vue';
import {
fetchCartList,
deletCartItem,
updateQuantity,
clearCartList
} from '@/api/cart.js';
export default {
components: {
uniNumberBox
},
data() {
return {
total: 0, //总价格
allChecked: false, //全选状态 true|false
empty: false, //空白页现实 true|false
cartList: [],
};
},
onLoad() {
// this.loadData();
},
onShow(){
//页面显示时重新加载购物车
this.loadData();
},
watch: {
//显示空白页
cartList(e) {
let empty = e.length === 0 ? true : false;
if (this.empty !== empty) {
this.empty = empty;
}
}
},
computed: {
...mapState(['hasLogin'])
},
methods: {
//请求数据
async loadData() {
if(!this.hasLogin){
return;
}
fetchCartList().then(response => {
let list = response.data;
let cartList = list.map(item => {
item.checked = true;
item.loaded = "loaded";
let spDataArr = JSON.parse(item.productAttr);
let spDataStr = '';
for (let attr of spDataArr) {
spDataStr += attr.key;
spDataStr += ":";
spDataStr += attr.value;
spDataStr += ";";
}
item.spDataStr = spDataStr;
return item;
});
this.cartList = cartList;
this.calcTotal(); //计算总价
});
},
//监听image加载完成
onImageLoad(key, index) {
this.$set(this[key][index], 'loaded', 'loaded');
},
//监听image加载失败
onImageError(key, index) {
this[key][index].productPic = '/static/errorImage.jpg';
},
navToLogin() {
uni.navigateTo({
url: '/pages/public/login'
})
},
//选中状态处理
check(type, index) {
if (type === 'item') {
this.cartList[index].checked = !this.cartList[index].checked;
} else {
const checked = !this.allChecked
const list = this.cartList;
list.forEach(item => {
item.checked = checked;
})
this.allChecked = checked;
}
this.calcTotal(type);
},
//数量
numberChange(data) {
let cartItem = this.cartList[data.index];
updateQuantity({id:cartItem.id,quantity:data.number}).then(response=>{
cartItem.quantity = data.number;
this.calcTotal();
});
},
//删除
handleDeleteCartItem(index) {
let list = this.cartList;
let row = list[index];
let id = row.id;
deletCartItem({ids:id}).then(response=>{
this.cartList.splice(index, 1);
this.calcTotal();
uni.hideLoading();
});
},
//清空
clearCart() {
clearCartList().then(response=>{
uni.showModal({
content: '清空购物车?',
success: (e) => {
if (e.confirm) {
this.cartList = [];
}
}
})
});
},
//计算总价
calcTotal() {
let list = this.cartList;
if (list.length === 0) {
this.empty = true;
return;
}
let total = 0;
let checked = true;
list.forEach(item => {
if (item.checked === true) {
total += item.price * item.quantity;
} else if (checked === true) {
checked = false;
}
})
this.allChecked = checked;
this.total = Number(total.toFixed(2));
},
//创建订单
createOrder() {
let list = this.cartList;
let cartIds = [];
list.forEach(item => {
if (item.checked) {
cartIds.push(item.id);
}
})
if(cartIds.length==0){
uni.showToast({
title:'您还未选择要下单的商品!',
duration:1000
})
return;
}
uni.navigateTo({
url: `/pages/order/createOrder?cartIds=${JSON.stringify(cartIds)}`
})
}
}
}
</script>
<style lang='scss'>
.container {
padding-bottom: 134upx;
/* 空白页 */
.empty {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100vh;
padding-bottom: 100upx;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
background: #fff;
image {
width: 240upx;
height: 160upx;
margin-bottom: 30upx;
}
.empty-tips {
display: flex;
font-size: $font-sm+2upx;
color: $font-color-disabled;
.navigator {
color: $uni-color-primary;
margin-left: 16upx;
}
}
}
}
/* 购物车列表项 */
.cart-item {
display: flex;
position: relative;
padding: 30upx 40upx;
.image-wrapper {
width: 230upx;
height: 230upx;
flex-shrink: 0;
position: relative;
image {
border-radius: 8upx;
}
}
.checkbox {
position: absolute;
left: -16upx;
top: -16upx;
z-index: 8;
font-size: 44upx;
line-height: 1;
padding: 4upx;
color: $font-color-disabled;
background: #fff;
border-radius: 50px;
}
.item-right {
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
position: relative;
padding-left: 30upx;
.title,
.price {
font-size: $font-base + 2upx;
color: $font-color-dark;
height: 40upx;
line-height: 40upx;
}
.attr {
font-size: $font-sm + 2upx;
color: $font-color-light;
height: 50upx;
line-height: 50upx;
}
.price {
height: 50upx;
line-height: 50upx;
}
}
.del-btn {
padding: 4upx 10upx;
font-size: 34upx;
height: 50upx;
color: $font-color-light;
}
}
/* 底部栏 */
.action-section {
/* #ifdef H5 */
margin-bottom: 100upx;
/* #endif */
position: fixed;
left: 30upx;
bottom: 30upx;
z-index: 95;
display: flex;
align-items: center;
width: 690upx;
height: 100upx;
padding: 0 30upx;
background: rgba(255, 255, 255, .9);
box-shadow: 0 0 20upx 0 rgba(0, 0, 0, .5);
border-radius: 16upx;
.checkbox {
height: 52upx;
position: relative;
image {
width: 52upx;
height: 100%;
position: relative;
z-index: 5;
}
}
.clear-btn {
position: absolute;
left: 26upx;
top: 0;
z-index: 4;
width: 0;
height: 52upx;
line-height: 52upx;
padding-left: 38upx;
font-size: $font-base;
color: #fff;
background: $font-color-disabled;
border-radius: 0 50px 50px 0;
opacity: 0;
transition: .2s;
&.show {
opacity: 1;
width: 120upx;
}
}
.total-box {
flex: 1;
display: flex;
flex-direction: column;
text-align: right;
padding-right: 40upx;
.price {
font-size: $font-lg;
color: $font-color-dark;
}
.coupon {
font-size: $font-sm;
color: $font-color-light;
text {
color: $font-color-dark;
}
}
}
.confirm-btn {
padding: 0 38upx;
margin: 0;
border-radius: 100px;
height: 76upx;
line-height: 76upx;
font-size: $font-base + 2upx;
background: $uni-color-primary;
box-shadow: 1px 2px 5px rgba(217, 60, 93, 0.72)
}
}
/* 复选框选中状态 */
.action-section .checkbox.checked,
.cart-item .checkbox.checked {
color: $uni-color-primary;
}
</style>
<template>
<view class="content">
<scroll-view scroll-y class="left-aside">
<view v-for="item in flist" :key="item.id" class="f-item b-b" :class="{active: item.id === currentId}" @click="tabtap(item)">
{{item.name}}
</view>
</scroll-view>
<scroll-view scroll-with-animation scroll-y class="right-aside">
<view class="s-list">
<view @click="navToList(item.id)" class="s-item" v-for="item in slist" :key="item.id">
<image :src="item.icon||'http://macro-oss.oss-cn-shenzhen.aliyuncs.com/mall/images/20190519/default.png'"></image>
<text>{{item.name}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
fetchProductCateList
} from '@/api/home.js';
export default {
data() {
return {
currentId: 0,
flist: [],
slist: []
}
},
onLoad() {
this.loadData();
},
methods: {
async loadData() {
fetchProductCateList(0).then(response => {
this.flist = response.data;
if (this.flist.length > 0) {
this.currentId = this.flist[0].id;
fetchProductCateList(this.currentId).then(response => {
this.slist = response.data;
});
}
})
},
//一级分类点击
tabtap(item) {
this.currentId = item.id;
fetchProductCateList(this.currentId).then(response => {
this.slist = response.data;
});
},
navToList(sid) {
uni.navigateTo({
url: `/pages/product/list?fid=${this.currentId}&sid=${sid}`
})
}
}
}
</script>
<style lang='scss'>
page,
.content {
height: 100%;
background-color: #f8f8f8;
}
.content {
display: flex;
}
.left-aside {
flex-shrink: 0;
width: 200upx;
height: 100%;
background-color: #fff;
}
.f-item {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100upx;
font-size: 28upx;
color: $font-color-base;
position: relative;
&.active {
color: $base-color;
background: #f8f8f8;
&:before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
height: 36upx;
width: 8upx;
background-color: $base-color;
border-radius: 0 4px 4px 0;
opacity: .8;
}
}
}
.right-aside {
flex: 1;
overflow: hidden;
padding-left: 20upx;
}
.s-list {
margin-top: 20upx;
display: flex;
flex-wrap: wrap;
width: 100%;
background: #fff;
padding-top: 12upx;
&:after {
content: '';
flex: 99;
height: 0;
}
}
.s-item {
flex-shrink: 0;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
width: 176upx;
font-size: 26upx;
color: #666;
padding-bottom: 20upx;
image {
width: 140upx;
height: 140upx;
}
}
</style>
<template>
<view class="content">
<view class="navbar">
<view v-for="(item, index) in navList" :key="index" class="nav-item" :class="{current: tabCurrentIndex === index}"
@click="tabClick(index)">
{{item.text}}
</view>
</view>
<!-- 优惠券页面,仿mt -->
<view class="coupon-item" v-for="(item,index) in couponList" :key="index">
<view class="con">
<view class="left">
<text class="title">{{item.name}}</text>
<text class="time">有效期至{{item.endTime | formatDateTime}}</text>
</view>
<view class="right">
<text class="price">{{item.amount}}</text>
<text>{{item.minPoint}}可用</text>
</view>
<view class="circle l"></view>
<view class="circle r"></view>
</view>
<text class="tips">{{item.useType | formatCouponUseType}}</text>
</view>
</view>
</template>
<script>
import {
fetchMemberCouponList
} from '@/api/coupon.js';
import {
formatDate
} from '@/utils/date';
export default {
data() {
return {
couponList: [],
tabCurrentIndex:0,
useStatus:0,
navList: [
{
useStatus: 0,
text: '未使用'
},
{
useStatus: 1,
text: '已使用'
},
{
useStatus: 2,
text: '已过期'
}
],
};
},
onLoad() {
this.loadData();
},
filters:{
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
formatCouponUseType(useType) {
if (useType == 0) {
return "全场通用";
} else if (useType == 1) {
return "指定分类商品可用";
} else if (useType == 2) {
return "指定商品可用";
}
return null;
},
},
methods: {
loadData(){
fetchMemberCouponList(this.useStatus).then(response=>{
this.couponList = response.data;
});
},
tabClick(index){
this.tabCurrentIndex = index;
this.useStatus = this.navList[index].useStatus;
this.loadData();
},
}
}
</script>
<style lang='scss'>
page {
background: $page-color-base;
padding-bottom: 100upx;
}
.navbar {
display: flex;
height: 40px;
padding: 0 5px;
background: #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, .06);
position: relative;
z-index: 10;
.nav-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 15px;
color: $font-color-dark;
position: relative;
&.current {
color: $base-color;
&:after {
content: '';
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 44px;
height: 0;
border-bottom: 2px solid $base-color;
}
}
}
}
/* 优惠券列表 */
.coupon-item {
display: flex;
flex-direction: column;
margin: 20upx 24upx;
background: #fff;
.con {
display: flex;
align-items: center;
position: relative;
height: 120upx;
padding: 0 30upx;
&:after {
position: absolute;
left: 0;
bottom: 0;
content: '';
width: 100%;
height: 0;
border-bottom: 1px dashed #f3f3f3;
transform: scaleY(50%);
}
}
.left {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
overflow: hidden;
height: 100upx;
}
.title {
font-size: 32upx;
color: $font-color-dark;
margin-bottom: 10upx;
}
.time {
font-size: 24upx;
color: $font-color-light;
}
.right {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 26upx;
color: $font-color-base;
height: 100upx;
}
.price {
font-size: 44upx;
color: $base-color;
&:before {
content: '¥';
font-size: 34upx;
}
}
.tips {
font-size: 24upx;
color: $font-color-light;
line-height: 60upx;
padding-left: 30upx;
}
.circle {
position: absolute;
left: -6upx;
bottom: -10upx;
z-index: 10;
width: 20upx;
height: 20upx;
background: #f3f3f3;
border-radius: 100px;
&.r {
left: auto;
right: -6upx;
}
}
}
</style>
<template>
<view class="container">
<!-- 小程序头部兼容 -->
<!-- #ifdef MP -->
<view class="mp-search-box">
<input class="ser-input" type="text" value="输入关键字搜索" disabled />
</view>
<!-- #endif -->
<!-- 头部轮播 -->
<view class="carousel-section">
<!-- 标题栏和状态栏占位符 -->
<view class="titleNview-placing"></view>
<!-- 背景色区域 -->
<view class="titleNview-background" :style="{backgroundColor:titleNViewBackground}"></view>
<swiper class="carousel" circular @change="swiperChange">
<swiper-item v-for="(item, index) in advertiseList" :key="index" class="carousel-item" @click="navToAdvertisePage(item)">
<image :src="item.pic" />
</swiper-item>
</swiper>
<!-- 自定义swiper指示器 -->
<view class="swiper-dots">
<text class="num">{{swiperCurrent+1}}</text>
<text class="sign">/</text>
<text class="num">{{swiperLength}}</text>
</view>
</view>
<!-- 头部功能区 -->
<view class="cate-section">
<view class="cate-item">
<image src="/static/temp/c3.png"></image>
<text>专题</text>
</view>
<view class="cate-item">
<image src="/static/temp/c5.png"></image>
<text>话题</text>
</view>
<view class="cate-item">
<image src="/static/temp/c6.png"></image>
<text>优选</text>
</view>
<view class="cate-item">
<image src="/static/temp/c7.png"></image>
<text>特惠</text>
</view>
</view>
<!-- 品牌制造商直供 -->
<view class="f-header m-t" @click="navToRecommendBrandPage()">
<image src="/static/icon_home_brand.png"></image>
<view class="tit-box">
<text class="tit">品牌制造商直供</text>
<text class="tit2">工厂直达消费者,剔除品牌溢价</text>
</view>
<text class="yticon icon-you"></text>
</view>
<view class="guess-section">
<view v-for="(item, index) in brandList" :key="index" class="guess-item" @click="navToBrandDetailPage(item)">
<view class="image-wrapper-brand">
<image :src="item.logo" mode="aspectFit"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2">商品数量:{{item.productCount}}</text>
</view>
</view>
<!-- 秒杀专区 -->
<view class="f-header m-t" v-if="homeFlashPromotion!==null">
<image src="/static/icon_flash_promotion.png"></image>
<view class="tit-box">
<text class="tit">秒杀专区</text>
<text class="tit2">下一场 {{homeFlashPromotion.nextStartTime | formatTime}} 开始</text>
</view>
<view class="tit-box">
<text class="tit2" style="text-align: right;">本场结束剩余:</text>
<view style="text-align: right;">
<text class="hour timer">{{cutDownTime.endHour}}</text>
<text>:</text>
<text class="minute timer">{{cutDownTime.endMinute}}</text>
<text>:</text>
<text class="second timer">{{cutDownTime.endSecond}}</text>
</view>
</view>
<text class="yticon icon-you" v-show="false"></text>
</view>
<view class="guess-section">
<view v-for="(item, index) in homeFlashPromotion.productList" :key="index" class="guess-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFill"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2 clamp">{{item.subTitle}}</text>
<text class="price">{{item.price}}</text>
</view>
</view>
<!-- 新鲜好物 -->
<view class="f-header m-t" @click="navToNewProudctListPage()">
<image src="/static/icon_new_product.png"></image>
<view class="tit-box">
<text class="tit">新鲜好物</text>
<text class="tit2">为你寻觅世间好物</text>
</view>
<text class="yticon icon-you"></text>
</view>
<view class="seckill-section">
<scroll-view class="floor-list" scroll-x>
<view class="scoll-wrapper">
<view v-for="(item, index) in newProductList" :key="index" class="floor-item" @click="navToDetailPage(item)">
<image :src="item.pic" mode="aspectFill"></image>
<text class="title clamp">{{item.name}}</text>
<text class="title2 clamp">{{item.subTitle}}</text>
<text class="price">{{item.price}}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 人气推荐楼层 -->
<view class="f-header m-t" @click="navToHotProudctListPage()">
<image src="/static/icon_hot_product.png"></image>
<view class="tit-box">
<text class="tit">人气推荐</text>
<text class="tit2">大家都赞不绝口的</text>
</view>
<text class="yticon icon-you"></text>
</view>
<view class="hot-section">
<view v-for="(item, index) in hotProductList" :key="index" class="guess-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFill"></image>
</view>
<view class="txt">
<text class="title clamp">{{item.name}}</text>
<text class="title2">{{item.subTitle}}</text>
<text class="price">{{item.price}}</text>
</view>
</view>
</view>
<!-- 猜你喜欢-->
<view class="f-header m-t">
<image src="/static/icon_recommend_product.png"></image>
<view class="tit-box">
<text class="tit">猜你喜欢</text>
<text class="tit2">你喜欢的都在这里了</text>
</view>
<text class="yticon icon-you" v-show="false"></text>
</view>
<view class="guess-section">
<view v-for="(item, index) in recommendProductList" :key="index" class="guess-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFill"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2 clamp">{{item.subTitle}}</text>
<text class="price">{{item.price}}</text>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import {
fetchContent,
fetchRecommendProductList
} from '@/api/home.js';
import {
formatDate
} from '@/utils/date';
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
export default {
components: {
uniLoadMore
},
data() {
return {
titleNViewBackground: '',
titleNViewBackgroundList: ['rgb(203, 87, 60)', 'rgb(205, 215, 218)'],
swiperCurrent: 0,
swiperLength: 0,
carouselList: [],
goodsList: [],
advertiseList: [],
brandList: [],
homeFlashPromotion: [],
newProductList: [],
hotProductList: [],
recommendProductList: [],
recommendParams: {
pageNum: 1,
pageSize: 4
},
loadingType:'more'
};
},
onLoad() {
this.loadData();
},
//下拉刷新
onPullDownRefresh(){
this.recommendParams.pageNum=1;
this.loadData();
},
//加载更多
onReachBottom(){
this.recommendParams.pageNum++;
this.loadingType = 'loading';
fetchRecommendProductList(this.recommendParams).then(response => {
let addProductList = response.data;
if(response.data.length===0){
//没有更多了
this.recommendParams.pageNum--;
this.loadingType = 'nomore';
}else{
this.recommendProductList = this.recommendProductList.concat(addProductList);
this.loadingType = 'more';
}
})
},
computed: {
cutDownTime() {
let endTime = new Date(this.homeFlashPromotion.endTime);
let endDateTime = new Date();
let startDateTime = new Date();
endDateTime.setHours(endTime.getHours());
endDateTime.setMinutes(endTime.getMinutes());
endDateTime.setSeconds(endTime.getSeconds());
let offsetTime = (endDateTime.getTime() - startDateTime.getTime());
let endHour = Math.floor(offsetTime / (60 * 60 * 1000));
let offsetMinute = offsetTime % (60 * 60 * 1000);
let endMinute = Math.floor(offsetMinute / (60 * 1000));
let offsetSecond = offsetTime % (60 * 1000);
let endSecond = Math.floor(offsetSecond / 1000);
return {
endHour: endHour,
endMinute: endMinute,
endSecond: endSecond
}
}
},
filters: {
formatTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'hh:mm:ss')
},
},
methods: {
/**
* 加载数据
*/
async loadData() {
fetchContent().then(response => {
console.log("onLoad", response.data);
this.advertiseList = response.data.advertiseList;
this.swiperLength = this.advertiseList.length;
this.titleNViewBackground = this.titleNViewBackgroundList[0];
this.brandList = response.data.brandList;
this.homeFlashPromotion = response.data.homeFlashPromotion;
this.newProductList = response.data.newProductList;
this.hotProductList = response.data.hotProductList;
fetchRecommendProductList(this.recommendParams).then(response => {
this.recommendProductList = response.data;
uni.stopPullDownRefresh();
})
});
},
//轮播图切换修改背景色
swiperChange(e) {
const index = e.detail.current;
this.swiperCurrent = index;
let changeIndex = index % this.titleNViewBackgroundList.length;
this.titleNViewBackground = this.titleNViewBackgroundList[changeIndex];
},
//商品详情页
navToDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
//广告详情页
navToAdvertisePage(item) {
let id = item.id;
console.log("navToAdvertisePage",item)
},
//品牌详情页
navToBrandDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/brand/brandDetail?id=${id}`
})
},
//推荐品牌列表页
navToRecommendBrandPage() {
uni.navigateTo({
url: `/pages/brand/list`
})
},
//新鲜好物列表页
navToNewProudctListPage() {
uni.navigateTo({
url: `/pages/product/newProductList`
})
},
//人气推荐列表页
navToHotProudctListPage() {
uni.navigateTo({
url: `/pages/product/hotProductList`
})
},
},
// #ifndef MP
// 标题栏input搜索框点击
onNavigationBarSearchInputClicked: async function(e) {
this.$api.msg('点击了搜索框');
},
//点击导航栏 buttons 时触发
onNavigationBarButtonTap(e) {
const index = e.index;
if (index === 0) {
this.$api.msg('点击了扫描');
} else if (index === 1) {
// #ifdef APP-PLUS
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const currentWebview = page.$getAppWebview();
currentWebview.hideTitleNViewButtonRedDot({
index
});
// #endif
uni.navigateTo({
url: '/pages/notice/notice'
})
}
}
// #endif
}
</script>
<style lang="scss">
/* #ifdef MP */
.mp-search-box {
position: absolute;
left: 0;
top: 30upx;
z-index: 9999;
width: 100%;
padding: 0 80upx;
.ser-input {
flex: 1;
height: 56upx;
line-height: 56upx;
text-align: center;
font-size: 28upx;
color: $font-color-base;
border-radius: 20px;
background: rgba(255, 255, 255, .6);
}
}
page {
.cate-section {
position: relative;
z-index: 5;
border-radius: 16upx 16upx 0 0;
margin-top: -20upx;
}
.carousel-section {
padding: 0;
.titleNview-placing {
padding-top: 0;
height: 0;
}
.carousel {
.carousel-item {
padding: 0;
}
}
.swiper-dots {
left: 45upx;
bottom: 40upx;
}
}
}
/* #endif */
page {
background: #f5f5f5;
}
.m-t {
margin-top: 16upx;
}
/* 头部 轮播图 */
.carousel-section {
position: relative;
padding-top: 10px;
.titleNview-placing {
height: var(--status-bar-height);
padding-top: 44px;
box-sizing: content-box;
}
.titleNview-background {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 426upx;
transition: .4s;
}
}
.carousel {
width: 100%;
height: 350upx;
.carousel-item {
width: 100%;
height: 100%;
padding: 0 28upx;
overflow: hidden;
}
image {
width: 100%;
height: 100%;
border-radius: 10upx;
}
}
.swiper-dots {
display: flex;
position: absolute;
left: 60upx;
bottom: 15upx;
width: 72upx;
height: 36upx;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABkCAYAAADDhn8LAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTMyIDc5LjE1OTI4NCwgMjAxNi8wNC8xOS0xMzoxMzo0MCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OTk4MzlBNjE0NjU1MTFFOUExNjRFQ0I3RTQ0NEExQjMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OTk4MzlBNjA0NjU1MTFFOUExNjRFQ0I3RTQ0NEExQjMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6Q0E3RUNERkE0NjExMTFFOTg5NzI4MTM2Rjg0OUQwOEUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6Q0E3RUNERkI0NjExMTFFOTg5NzI4MTM2Rjg0OUQwOEUiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4Gh5BPAAACTUlEQVR42uzcQW7jQAwFUdN306l1uWwNww5kqdsmm6/2MwtVCp8CosQtP9vg/2+/gY+DRAMBgqnjIp2PaCxCLLldpPARRIiFj1yBbMV+cHZh9PURRLQNhY8kgWyL/WDtwujjI8hoE8rKLqb5CDJaRMJHokC6yKgSCR9JAukmokIknCQJpLOIrJFwMsBJELFcKHwM9BFkLBMKFxNcBCHlQ+FhoocgpVwwnv0Xn30QBJGMC0QcaBVJiAMiec/dcwKuL4j1QMsVCXFAJE4s4NQA3K/8Y6DzO4g40P7UcmIBJxbEesCKWBDg8wWxHrAiFgT4fEGsB/CwIhYE+AeBAAdPLOcV8HRmWRDAiQVcO7GcV8CLM8uCAE4sQCDAlHcQ7x+ABQEEAggEEAggEEAggEAAgQACASAQQCCAQACBAAIBBAIIBBAIIBBAIABe4e9iAe/xd7EAJxYgEGDeO4j3EODp/cOCAE4sYMyJ5cwCHs4rCwI4sYBxJ5YzC84rCwKcXxArAuthQYDzC2JF0H49LAhwYUGsCFqvx5EF2T07dMaJBetx4cRyaqFtHJ8EIhK0i8OJBQxcECuCVutxJhCRoE0cZwMRyRcFefa/ffZBVPogePihhyCnbBhcfMFFEFM+DD4m+ghSlgmDkwlOgpAl4+BkkJMgZdk4+EgaSCcpVX7bmY9kgXQQU+1TgE0c+QJZUUz1b2T4SBbIKmJW+3iMj2SBVBWz+leVfCQLpIqYbp8b85EskIxyfIOfK5Sf+wiCRJEsllQ+oqEkQfBxmD8BBgA5hVjXyrBNUQAAAABJRU5ErkJggg==);
background-size: 100% 100%;
.num {
width: 36upx;
height: 36upx;
border-radius: 50px;
font-size: 24upx;
color: #fff;
text-align: center;
line-height: 36upx;
}
.sign {
position: absolute;
top: 0;
left: 50%;
line-height: 36upx;
font-size: 12upx;
color: #fff;
transform: translateX(-50%);
}
}
/* 分类 */
.cate-section {
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
padding: 30upx 22upx;
background: #fff;
.cate-item {
display: flex;
flex-direction: column;
align-items: center;
font-size: $font-sm + 2upx;
color: $font-color-dark;
}
/* 原图标颜色太深,不想改图了,所以加了透明度 */
image {
width: 88upx;
height: 88upx;
margin-bottom: 14upx;
border-radius: 50%;
opacity: .7;
box-shadow: 4upx 4upx 20upx rgba(250, 67, 106, 0.3);
}
}
.ad-1 {
width: 100%;
height: 210upx;
padding: 10upx 0;
background: #fff;
image {
width: 100%;
height: 100%;
}
}
/* 秒杀专区 */
.seckill-section {
padding: 4upx 30upx 24upx;
background: #fff;
.s-header {
display: flex;
align-items: center;
height: 92upx;
line-height: 1;
.s-img {
width: 140upx;
height: 30upx;
}
.tip {
font-size: $font-base;
color: $font-color-light;
margin: 0 20upx 0 40upx;
}
.timer {
display: inline-block;
width: 40upx;
height: 36upx;
text-align: center;
line-height: 36upx;
margin-right: 14upx;
font-size: $font-sm+2upx;
color: #fff;
border-radius: 2px;
background: rgba(0, 0, 0, .8);
}
.icon-you {
font-size: $font-lg;
color: $font-color-light;
flex: 1;
text-align: right;
}
}
.floor-list {
white-space: nowrap;
}
.scoll-wrapper {
display: flex;
align-items: flex-start;
}
.floor-item {
width: 300upx;
margin-right: 20upx;
font-size: $font-sm+2upx;
color: $font-color-dark;
line-height: 1.8;
image {
width: 300upx;
height: 300upx;
border-radius: 6upx;
}
.price {
color: $uni-color-primary;
}
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
}
}
.f-header {
display: flex;
align-items: center;
height: 140upx;
padding: 6upx 30upx 8upx;
background: #fff;
image {
flex-shrink: 0;
width: 80upx;
height: 80upx;
margin-right: 20upx;
}
.tit-box {
flex: 1;
display: flex;
flex-direction: column;
}
.tit {
font-size: $font-lg +2upx;
color: #font-color-dark;
line-height: 1.3;
}
.tit2 {
font-size: $font-sm;
color: $font-color-light;
}
.icon-you {
font-size: $font-lg +2upx;
color: $font-color-light;
}
.timer {
display: inline-block;
width: 40upx;
height: 36upx;
text-align: center;
line-height: 36upx;
margin-right: 14upx;
font-size: $font-sm+2upx;
color: #fff;
border-radius: 2px;
background: rgba(0, 0, 0, .8);
}
}
/* 分类推荐楼层 */
.hot-floor {
width: 100%;
overflow: hidden;
margin-bottom: 20upx;
.floor-img-box {
width: 100%;
height: 320upx;
position: relative;
&:after {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: linear-gradient(rgba(255, 255, 255, .06) 30%, #f8f8f8);
}
}
.floor-img {
width: 100%;
height: 100%;
}
.floor-list {
white-space: nowrap;
padding: 20upx;
padding-right: 50upx;
border-radius: 6upx;
margin-top: -140upx;
margin-left: 30upx;
background: #fff;
box-shadow: 1px 1px 5px rgba(0, 0, 0, .2);
position: relative;
z-index: 1;
}
.scoll-wrapper {
display: flex;
align-items: flex-start;
}
.floor-item {
width: 180upx;
margin-right: 20upx;
font-size: $font-sm+2upx;
color: $font-color-dark;
line-height: 1.8;
image {
width: 180upx;
height: 180upx;
border-radius: 6upx;
}
.price {
color: $uni-color-primary;
}
}
.more {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
flex-shrink: 0;
width: 180upx;
height: 180upx;
border-radius: 6upx;
background: #f3f3f3;
font-size: $font-base;
color: $font-color-light;
text:first-child {
margin-bottom: 4upx;
}
}
}
/* 猜你喜欢 */
.guess-section {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.guess-item {
display: flex;
flex-direction: column;
width: 48%;
padding-bottom: 40upx;
&:nth-child(2n+1) {
margin-right: 4%;
}
}
.image-wrapper {
width: 100%;
height: 330upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.image-wrapper-brand {
width: 100%;
height: 150upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 1;
}
}
.hot-section {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.guess-item {
display: flex;
flex-direction: row;
width: 100%;
padding-bottom: 40upx;
}
.image-wrapper {
width: 30%;
height: 250upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 80upx;
}
.txt {
width: 70%;
display: flex;
flex-direction: column;
padding-left: 40upx;
}
}
</style>
<template>
<view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style>
</style>
<template>
<view class="app">
<view class="price-box">
<text>支付金额</text>
<text class="price">{{orderInfo.payAmount}}</text>
</view>
<view class="pay-type-list">
<view class="type-item b-b" @click="changePayType(1)">
<text class="icon yticon icon-alipay"></text>
<view class="con">
<text class="tit">支付宝支付</text>
<text>推荐使用支付宝支付</text>
</view>
<label class="radio">
<radio value="" color="#fa436a" :checked='payType == 1' />
</radio>
</label>
</view>
<view class="type-item b-b" @click="changePayType(2)">
<text class="icon yticon icon-weixinzhifu"></text>
<view class="con">
<text class="tit">微信支付</text>
</view>
<label class="radio">
<radio value="" color="#fa436a" :checked='payType == 2' />
</radio>
</label>
</view>
</view>
<text class="mix-btn" @click="confirm">确认支付</text>
</view>
</template>
<script>
import {
fetchOrderDetail,
payOrderSuccess
} from '@/api/order.js';
import { API_BASE_URL, USE_ALIPAY } from '@/utils/appConfig.js';
export default {
data() {
return {
orderId: null,
payType: 1,
orderInfo: {}
};
},
onLoad(options) {
this.orderId = options.orderId;
fetchOrderDetail(this.orderId).then(response => {
this.orderInfo = response.data;
});
},
methods: {
//选择支付方式
changePayType(type) {
this.payType = type;
},
//确认支付
confirm: async function() {
if(USE_ALIPAY){
if(this.payType!=1){
uni.showToast({
title:"暂不支持微信支付!",
icon:"none"
})
return;
}
window.location.href = API_BASE_URL+"/alipay/webPay?outTradeNo=" + this.orderInfo.orderSn + "&subject=" + this.orderInfo.receiverName + "的商品订单" + "&totalAmount=" + this.orderInfo.totalAmount
}else{
payOrderSuccess({
orderId: this.orderId,
payType: this.payType
}).then(response => {
uni.redirectTo({
url: '/pages/money/paySuccess'
})
});
}
},
}
}
</script>
<style lang='scss'>
.app {
width: 100%;
}
.price-box {
background-color: #fff;
height: 265upx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 28upx;
color: #909399;
.price {
font-size: 50upx;
color: #303133;
margin-top: 12upx;
&:before {
content: '¥';
font-size: 40upx;
}
}
}
.pay-type-list {
margin-top: 20upx;
background-color: #fff;
padding-left: 60upx;
.type-item {
height: 120upx;
padding: 20upx 0;
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 60upx;
font-size: 30upx;
position: relative;
}
.icon {
width: 100upx;
font-size: 52upx;
}
.icon-erjiye-yucunkuan {
color: #fe8e2e;
}
.icon-weixinzhifu {
color: #36cb59;
}
.icon-alipay {
color: #01aaef;
}
.tit {
font-size: $font-lg;
color: $font-color-dark;
margin-bottom: 4upx;
}
.con {
flex: 1;
display: flex;
flex-direction: column;
font-size: $font-sm;
color: $font-color-light;
}
}
.mix-btn {
display: flex;
align-items: center;
justify-content: center;
width: 630upx;
height: 80upx;
margin: 80upx auto 30upx;
font-size: $font-lg;
color: #fff;
background-color: $base-color;
border-radius: 10upx;
box-shadow: 1px 2px 5px rgba(219, 63, 96, 0.4);
}
</style>
<template>
<view class="content">
<text class="success-icon yticon icon-xuanzhong2"></text>
<text class="tit">{{payText}}</text>
<view class="btn-group">
<navigator url="/pages/order/order?state=0" open-type="redirect" class="mix-btn">查看订单</navigator>
<navigator url="/pages/index/index" open-type="switchTab" class="mix-btn hollow">返回首页</navigator>
</view>
</view>
</template>
<script>
import {
fetchAliapyStatus
} from '@/api/order.js';
import { USE_ALIPAY } from '@/utils/appConfig.js';
export default {
data() {
return {
payText: '',
tradeStatus: null
}
},
onLoad(options) {
if(!USE_ALIPAY){
this.payText = '支付成功';
return;
}
let outTradeNo = options.out_trade_no;
console.log(options.out_trade_no);
fetchAliapyStatus({outTradeNo:outTradeNo}).then(response => {
this.tradeStatus = response.data;
if(this.tradeStatus!=null&&'TRADE_SUCCESS'==this.tradeStatus){
this.payText = '支付成功';
}else{
this.payText = '支付失败';
}
console.log(this.tradeStatus);
});
},
methods: {
}
}
</script>
<style lang='scss'>
.content{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.success-icon{
font-size: 160upx;
color: #fa436a;
margin-top: 100upx;
}
.tit{
font-size: 38upx;
color: #303133;
}
.btn-group{
padding-top: 100upx;
}
.mix-btn {
margin-top: 30upx;
display: flex;
align-items: center;
justify-content: center;
width: 600upx;
height: 80upx;
font-size: $font-lg;
color: #fff;
background-color: $base-color;
border-radius: 10upx;
&.hollow{
background: #fff;
color: #303133;
border: 1px solid #ccc;
}
}
</style>
<template>
<view>
<view class="notice-item">
<text class="time">11:30</text>
<view class="content">
<text class="title">新品上市,全场满199减50</text>
<view class="img-wrapper">
<image class="pic" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1556465765776&di=57bb5ff70dc4f67dcdb856e5d123c9e7&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01fd015aa4d95fa801206d96069229.jpg%401280w_1l_2o_100sh.jpg"></image>
</view>
<text class="introduce">
虽然做了一件好事,但很有可能因此招来他人的无端猜测,例如被质疑是否藏有其他利己动机等,乃至谴责。即便如此,还是要做好事。
</text>
<view class="bot b-t">
<text>查看详情</text>
<text class="more-icon yticon icon-you"></text>
</view>
</view>
</view>
<view class="notice-item">
<text class="time">昨天 12:30</text>
<view class="content">
<text class="title">新品上市,全场满199减50</text>
<view class="img-wrapper">
<image class="pic" src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3761064275,227090144&fm=26&gp=0.jpg"></image>
<view class="cover">
活动结束
</view>
</view>
<view class="bot b-t">
<text>查看详情</text>
<text class="more-icon yticon icon-you"></text>
</view>
</view>
</view>
<view class="notice-item">
<text class="time">2019-07-26 12:30</text>
<view class="content">
<text class="title">新品上市,全场满199减50</text>
<view class="img-wrapper">
<image class="pic" src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1556465765776&di=57bb5ff70dc4f67dcdb856e5d123c9e7&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F01fd015aa4d95fa801206d96069229.jpg%401280w_1l_2o_100sh.jpg"></image>
<view class="cover">
活动结束
</view>
</view>
<text class="introduce">新品上市全场2折起,新品上市全场2折起,新品上市全场2折起,新品上市全场2折起,新品上市全场2折起</text>
<view class="bot b-t">
<text>查看详情</text>
<text class="more-icon yticon icon-you"></text>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
}
},
methods: {
}
}
</script>
<style lang='scss'>
page {
background-color: #f7f7f7;
padding-bottom: 30upx;
}
.notice-item {
display: flex;
flex-direction: column;
align-items: center;
}
.time {
display: flex;
align-items: center;
justify-content: center;
height: 80upx;
padding-top: 10upx;
font-size: 26upx;
color: #7d7d7d;
}
.content {
width: 710upx;
padding: 0 24upx;
background-color: #fff;
border-radius: 4upx;
}
.title {
display: flex;
align-items: center;
height: 90upx;
font-size: 32upx;
color: #303133;
}
.img-wrapper {
width: 100%;
height: 260upx;
position: relative;
}
.pic {
display: block;
width: 100%;
height: 100%;
border-radius: 6upx;
}
.cover {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .5);
font-size: 36upx;
color: #fff;
}
.introduce {
display: inline-block;
padding: 16upx 0;
font-size: 28upx;
color: #606266;
line-height: 38upx;
}
.bot {
display: flex;
align-items: center;
justify-content: space-between;
height: 80upx;
font-size: 24upx;
color: #707070;
position: relative;
}
.more-icon {
font-size: 32upx;
}
</style>
<template>
<view>
<!-- 地址 -->
<navigator url="/pages/address/address?source=1" class="address-section">
<view class="order-content">
<text class="yticon icon-shouhuodizhi"></text>
<view class="cen">
<view class="top">
<text class="name">{{currentAddress.name}}</text>
<text class="mobile">{{currentAddress.phoneNumber}}</text>
</view>
<text class="address">{{currentAddress.province}} {{currentAddress.city}} {{currentAddress.region}}
{{currentAddress.detailAddress}}</text>
</view>
<text class="yticon icon-you"></text>
</view>
<image class="a-bg" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu4AAAAFCAYAAAAaAWmiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Rjk3RjkzMjM2NzMxMTFFOUI4RkU4OEZGMDcxQzgzOEYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Rjk3RjkzMjQ2NzMxMTFFOUI4RkU4OEZGMDcxQzgzOEYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGOTdGOTMyMTY3MzExMUU5QjhGRTg4RkYwNzFDODM4RiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGOTdGOTMyMjY3MzExMUU5QjhGRTg4RkYwNzFDODM4RiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrEOZlQAAAiuSURBVHjazJp7bFvV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"></image>
</navigator>
<view class="goods-section">
<view class="g-header b-b">
<text class="name">商品信息</text>
</view>
<!-- 商品列表 -->
<view class="g-item" v-for="item in cartPromotionItemList" :key="item.id">
<image :src="item.productPic"></image>
<view class="right">
<text class="title clamp">{{item.productName}}</text>
<text class="spec">{{item.productAttr | formatProductAttr}}</text>
<text class="promotion clamp">{{item.promotionMessage}}</text>
<view class="price-box">
<text class="price">{{item.price}}</text>
<text class="number">x {{item.quantity}}</text>
</view>
</view>
</view>
</view>
<!-- 优惠明细 -->
<view class="yt-list">
<view class="yt-list-cell b-b" @click="toggleMask('show')">
<view class="cell-icon">
</view>
<text class="cell-tit clamp">优惠券</text>
<text class="cell-tip active">
选择优惠券
</text>
<text class="cell-more wanjia wanjia-gengduo-d"></text>
</view>
<view class="yt-list-cell b-b">
<view class="cell-icon hb">
</view>
<text class="cell-tit clamp">积分抵扣</text>
<input class="integration" type="number" v-model="useIntegration" placeholder="使用积分数量" placeholder-class="placeholder"
@input="handleIntegrationInput" />
</view>
</view>
<!-- 金额明细 -->
<view class="yt-list">
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">商品合计</text>
<text class="cell-tip">{{calcAmount.totalAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">运费</text>
<text class="cell-tip">{{calcAmount.freightAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">活动优惠</text>
<text class="cell-tip red">-¥{{calcAmount.promotionAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">优惠券</text>
<text class="cell-tip red" v-if="currCoupon!=null">-¥{{currCoupon.amount}}</text>
<text class="cell-tip red" v-else>-¥0</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">积分抵扣</text>
<text class="cell-tip red">-¥{{calcIntegrationAmount(useIntegration)}}</text>
</view>
<view class="yt-list-cell desc-cell">
<text class="cell-tit clamp">备注</text>
<input class="desc" type="text" v-model="desc" placeholder="请填写备注信息" placeholder-class="placeholder" />
</view>
</view>
<!-- 底部 -->
<view class="footer">
<view class="price-content">
<text>实付款</text>
<text class="price-tip"></text>
<text class="price">{{calcAmount.payAmount}}</text>
</view>
<text class="submit" @click="submit">提交订单</text>
</view>
<!-- 优惠券面板 -->
<view class="mask" :class="maskState===0 ? 'none' : maskState===1 ? 'show' : ''" @click="toggleMask">
<view class="mask-content" @click.stop.prevent="stopPrevent">
<!-- 优惠券页面,仿mt -->
<view class="coupon-item" v-for="(item,index) in couponList" :key="index" @click="selectCoupon(item)">
<view class="con">
<view class="left">
<text class="title">{{item.name}}</text>
<text class="time">有效期至{{item.endTime | formatDateTime}}</text>
</view>
<view class="right">
<text class="price">{{item.amount}}</text>
<text>{{item.minPoint}}可用</text>
</view>
<view class="circle l"></view>
<view class="circle r"></view>
</view>
<text class="tips">{{item.useType | formatCouponUseType}}</text>
</view>
</view>
</view>
</view>
</template>
<script>
import {
generateConfirmOrder,
generateOrder
} from '@/api/order.js';
import {
formatDate
} from '@/utils/date';
export default {
data() {
return {
maskState: 0, //优惠券面板显示状态
desc: '', //备注
payType: 1, //1微信 2支付宝
couponList: [],
memberReceiveAddressList: [],
currentAddress: {},
cartPromotionItemList: [],
calcAmount: {},
currCoupon: null,
useIntegration: 0,
integrationConsumeSetting: {},
memberIntegration: 0,
cartIds: []
}
},
onLoad(option) {
//商品数据
this.cartIds = JSON.parse(option.cartIds);
console.log(this.cartIds);
this.loadData();
},
filters: {
formatProductAttr(jsonAttr) {
let attrArr = JSON.parse(jsonAttr);
let attrStr = '';
for (let attr of attrArr) {
attrStr += attr.key;
attrStr += ":";
attrStr += attr.value;
attrStr += ";";
}
return attrStr
},
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
formatCouponUseType(useType) {
if (useType == 0) {
return "全场通用";
} else if (useType == 1) {
return "指定分类商品可用";
} else if (useType == 2) {
return "指定商品可用";
}
return null;
},
},
methods: {
//生成确认单信息
async loadData() {
generateConfirmOrder(JSON.stringify(this.cartIds)).then(response => {
this.memberReceiveAddressList = response.data.memberReceiveAddressList;
this.currentAddress = this.getDefaultAddress();
this.cartPromotionItemList = response.data.cartPromotionItemList;
this.couponList = [];
for (let item of response.data.couponHistoryDetailList) {
this.couponList.push(item.coupon);
}
this.calcAmount = response.data.calcAmount;
this.integrationConsumeSetting = response.data.integrationConsumeSetting;
this.memberIntegration = response.data.memberIntegration;
});
},
//显示优惠券面板
toggleMask(type) {
let timer = type === 'show' ? 10 : 300;
let state = type === 'show' ? 1 : 0;
this.maskState = 2;
setTimeout(() => {
this.maskState = state;
}, timer)
},
numberChange(data) {
this.number = data.number;
},
changePayType(type) {
this.payType = type;
},
submit() {
let orderParam = {
payType: 0,
couponId: null,
cartIds:this.cartIds,
memberReceiveAddressId:this.currentAddress.id,
useIntegration:this.useIntegration
}
if(this.currCoupon!=null){
orderParam.couponId = this.currCoupon.id;
}
generateOrder(orderParam).then(response => {
let orderId = response.data.order.id;
uni.showModal({
title: '提示',
content: '订单创建成功,是否要立即支付?',
confirmText:'去支付',
cancelText:'取消',
success: function(res) {
if (res.confirm) {
uni.redirectTo({
url: `/pages/money/pay?orderId=${orderId}`
})
} else if (res.cancel) {
console.log("cancel")
uni.redirectTo({
url: '/pages/order/order?state=0'
})
}
}
});
});
},
stopPrevent() {},
//获取默认收货地址
getDefaultAddress() {
for (let item of this.memberReceiveAddressList) {
if (item.defaultStatus == 1) {
return item;
}
}
if(this.memberReceiveAddressList!=null&&this.memberReceiveAddressList.length>0){
return this.memberReceiveAddressList[0];
}
return {};
},
selectCoupon(coupon) {
this.currCoupon = coupon;
this.calcPayAmount();
this.toggleMask();
},
//计算支付金额
calcPayAmount() {
this.calcAmount.payAmount = this.calcAmount.totalAmount - this.calcAmount.promotionAmount - this.calcAmount.freightAmount;
if (this.currCoupon != null) {
this.calcAmount.payAmount = this.calcAmount.payAmount - this.currCoupon.amount;
}
if (this.useIntegration != 0) {
this.calcAmount.payAmount = this.calcAmount.payAmount - this.calcIntegrationAmount();
}
},
//积分转金额
calcIntegrationAmount(integration) {
if (this.integrationConsumeSetting == undefined || this.integrationConsumeSetting == null) {
return 0;
}
if (this.integrationConsumeSetting.couponStatus == 0) {
return 0;
}
return integration / this.integrationConsumeSetting.deductionPerAmount;
},
handleIntegrationInput(event) {
if (event.detail.value > this.memberIntegration) {
this.useIntegration = this.memberIntegration;
uni.showToast({
title: `您的积分只有${this.memberIntegration}`,
duration: 1000
})
}
},
}
}
</script>
<style lang="scss">
page {
background: $page-color-base;
padding-bottom: 100upx;
}
.address-section {
padding: 30upx 0;
background: #fff;
position: relative;
.order-content {
display: flex;
align-items: center;
}
.icon-shouhuodizhi {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 90upx;
color: #888;
font-size: 44upx;
}
.cen {
display: flex;
flex-direction: column;
flex: 1;
font-size: 28upx;
color: $font-color-dark;
}
.name {
font-size: 34upx;
margin-right: 24upx;
}
.address {
margin-top: 16upx;
margin-right: 20upx;
color: $font-color-light;
}
.icon-you {
font-size: 32upx;
color: $font-color-light;
margin-right: 30upx;
}
.a-bg {
position: absolute;
left: 0;
bottom: 0;
display: block;
width: 100%;
height: 5upx;
}
}
.goods-section {
margin-top: 16upx;
background: #fff;
padding-bottom: 1px;
.g-header {
display: flex;
align-items: center;
height: 84upx;
padding: 0 30upx;
position: relative;
}
.logo {
display: block;
width: 50upx;
height: 50upx;
border-radius: 100px;
}
.name {
font-size: 30upx;
color: $font-color-base;
margin-left: 24upx;
}
.g-item {
display: flex;
margin: 20upx 30upx;
image {
flex-shrink: 0;
display: block;
width: 140upx;
height: 140upx;
border-radius: 4upx;
}
.right {
flex: 1;
padding-left: 24upx;
overflow: hidden;
}
.title {
font-size: 30upx;
color: $font-color-dark;
}
.spec {
font-size: 26upx;
color: $font-color-light;
}
.promotion {
font-size: 24upx;
color: $base-color;
}
.price-box {
display: flex;
align-items: center;
font-size: 32upx;
color: $font-color-dark;
padding-top: 10upx;
.price {
margin-bottom: 4upx;
}
.number {
font-size: 26upx;
color: $font-color-base;
margin-left: 20upx;
}
}
.step-box {
position: relative;
}
}
}
.yt-list {
margin-top: 16upx;
background: #fff;
}
.yt-list-cell {
display: flex;
align-items: center;
padding: 10upx 30upx 10upx 40upx;
line-height: 70upx;
position: relative;
&.cell-hover {
background: #fafafa;
}
&.b-b:after {
left: 30upx;
}
.cell-icon {
height: 32upx;
width: 32upx;
font-size: 22upx;
color: #fff;
text-align: center;
line-height: 32upx;
background: #f85e52;
border-radius: 4upx;
margin-right: 12upx;
&.hb {
background: #ffaa0e;
}
&.lpk {
background: #3ab54a;
}
}
.cell-more {
align-self: center;
font-size: 24upx;
color: $font-color-light;
margin-left: 8upx;
margin-right: -10upx;
}
.cell-tit {
flex: 1;
font-size: 26upx;
color: $font-color-light;
margin-right: 10upx;
}
.cell-tip {
font-size: 26upx;
color: $font-color-dark;
&.disabled {
color: $font-color-light;
}
&.active {
color: $base-color;
}
&.red {
color: $base-color;
}
}
&.desc-cell {
.cell-tit {
max-width: 90upx;
}
}
.desc {
flex: 1;
font-size: $font-base;
color: $font-color-dark;
}
.integration {
flex: 1;
font-size: $font-base;
color: $font-color-dark;
text-align: right;
}
}
/* 支付列表 */
.pay-list {
padding-left: 40upx;
margin-top: 16upx;
background: #fff;
.pay-item {
display: flex;
align-items: center;
padding-right: 20upx;
line-height: 1;
height: 110upx;
position: relative;
}
.icon-weixinzhifu {
width: 80upx;
font-size: 40upx;
color: #6BCC03;
}
.icon-alipay {
width: 80upx;
font-size: 40upx;
color: #06B4FD;
}
.icon-xuanzhong2 {
display: flex;
align-items: center;
justify-content: center;
width: 60upx;
height: 60upx;
font-size: 40upx;
color: $base-color;
}
.tit {
font-size: 32upx;
color: $font-color-dark;
flex: 1;
}
}
.footer {
position: fixed;
left: 0;
bottom: 0;
z-index: 995;
display: flex;
align-items: center;
width: 100%;
height: 90upx;
justify-content: space-between;
font-size: 30upx;
background-color: #fff;
z-index: 998;
color: $font-color-base;
box-shadow: 0 -1px 5px rgba(0, 0, 0, .1);
.price-content {
padding-left: 30upx;
}
.price-tip {
color: $base-color;
margin-left: 8upx;
}
.price {
font-size: 36upx;
color: $base-color;
}
.submit {
display: flex;
align-items: center;
justify-content: center;
width: 280upx;
height: 100%;
color: #fff;
font-size: 32upx;
background-color: $base-color;
}
}
/* 优惠券面板 */
.mask {
display: flex;
align-items: flex-end;
position: fixed;
left: 0;
top: var(--window-top);
bottom: 0;
width: 100%;
background: rgba(0, 0, 0, 0);
z-index: 9995;
transition: .3s;
.mask-content {
width: 100%;
min-height: 30vh;
max-height: 70vh;
background: #f3f3f3;
transform: translateY(100%);
transition: .3s;
overflow-y: scroll;
}
&.none {
display: none;
}
&.show {
background: rgba(0, 0, 0, .4);
.mask-content {
transform: translateY(0);
}
}
}
/* 优惠券列表 */
.coupon-item {
display: flex;
flex-direction: column;
margin: 20upx 24upx;
background: #fff;
.con {
display: flex;
align-items: center;
position: relative;
height: 120upx;
padding: 0 30upx;
&:after {
position: absolute;
left: 0;
bottom: 0;
content: '';
width: 100%;
height: 0;
border-bottom: 1px dashed #f3f3f3;
transform: scaleY(50%);
}
}
.left {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
overflow: hidden;
height: 100upx;
}
.title {
font-size: 32upx;
color: $font-color-dark;
margin-bottom: 10upx;
}
.time {
font-size: 24upx;
color: $font-color-light;
}
.right {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 26upx;
color: $font-color-base;
height: 100upx;
}
.price {
font-size: 44upx;
color: $base-color;
&:before {
content: '¥';
font-size: 34upx;
}
}
.tips {
font-size: 24upx;
color: $font-color-light;
line-height: 60upx;
padding-left: 30upx;
}
.circle {
position: absolute;
left: -6upx;
bottom: -10upx;
z-index: 10;
width: 20upx;
height: 20upx;
background: #f3f3f3;
border-radius: 100px;
&.r {
left: auto;
right: -6upx;
}
}
}
</style>
<template>
<view class="content">
<view class="navbar">
<view v-for="(item, index) in navList" :key="index" class="nav-item" :class="{current: tabCurrentIndex === index}"
@click="tabClick(index)">
{{item.text}}
</view>
</view>
<swiper :current="tabCurrentIndex" class="swiper-box" duration="300" @change="changeTab">
<swiper-item class="tab-content" v-for="(tabItem,tabIndex) in navList" :key="tabIndex">
<scroll-view class="list-scroll-content" scroll-y @scrolltolower="loadData('add')">
<!-- 空白页 -->
<empty v-if="orderList==null||orderList.length === 0"></empty>
<!-- 订单列表 -->
<view v-for="(item,index) in orderList" :key="index" class="order-item">
<view class="i-top b-b">
<text class="time" @click="showOrderDetail(item.id)">{{item.createTime | formatDateTime}}</text>
<text class="state" :style="{color: '#fa436a'}">{{item.status | formatStatus}}</text>
<text v-if="item.status===3||item.status===4" class="del-btn yticon icon-iconfontshanchu1" @click="deleteOrder(item.id)"></text>
</view>
<view class="goods-box-single" v-for="(orderItem, itemIndex) in item.orderItemList"
:key="itemIndex">
<image class="goods-img" :src="orderItem.productPic" mode="aspectFill"></image>
<view class="right">
<text class="title clamp">{{orderItem.productName}}</text>
<text class="attr-box">{{orderItem.productAttr | formatProductAttr}} x {{orderItem.productQuantity}}</text>
<text class="price">{{orderItem.productPrice}}</text>
</view>
</view>
<view class="price-box">
<text class="num">{{calcTotalQuantity(item)}}</text>
件商品 实付款
<text class="price">{{item.payAmount}}</text>
</view>
<view class="action-box b-t" v-if="item.status == 0">
<button class="action-btn" @click="cancelOrder(item.id)">取消订单</button>
<button class="action-btn recom" @click="payOrder(item.id)">立即付款</button>
</view>
<view class="action-box b-t" v-if="item.status == 2">
<button class="action-btn" >查看物流</button>
<button class="action-btn recom" @click="receiveOrder(item.id)">确认收货</button>
</view>
<view class="action-box b-t" v-if="item.status == 3">
<button class="action-btn recom" >评价商品</button>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</scroll-view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
import empty from "@/components/empty";
import {
formatDate
} from '@/utils/date';
import {
fetchOrderList,
cancelUserOrder,
confirmReceiveOrder,
deleteUserOrder
} from '@/api/order.js';
export default {
components: {
uniLoadMore,
empty
},
data() {
return {
tabCurrentIndex: 0,
orderParam: {
status: -1,
pageNum: 1,
pageSize: 5
},
orderList: [],
loadingType:'more',
navList: [{
state: -1,
text: '全部'
},
{
state: 0,
text: '待付款'
},
{
state: 2,
text: '待收货'
},
{
state: 3,
text: '已完成'
},
{
state: 4,
text: '已取消'
}
],
};
},
onLoad(options) {
/**
* 修复app端点击除全部订单外的按钮进入时不加载数据的问题
* 替换onLoad下代码即可
*/
this.tabCurrentIndex = +options.state;
// #ifndef MP
this.loadData()
// #endif
// #ifdef MP
if (options.state == 0) {
this.loadData()
}
// #endif
},
filters: {
formatStatus(status) {
let statusTip = '';
switch (+status) {
case 0:
statusTip = '等待付款';
break;
case 1:
statusTip = '等待发货';
break;
case 2:
statusTip = '等待收货';
break;
case 3:
statusTip = '交易完成';
break;
case 4:
statusTip = '交易关闭';
break;
}
return statusTip;
},
formatProductAttr(jsonAttr) {
let attrArr = JSON.parse(jsonAttr);
let attrStr = '';
for (let attr of attrArr) {
attrStr += attr.key;
attrStr += ":";
attrStr += attr.value;
attrStr += ";";
}
return attrStr
},
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
//获取订单列表
loadData(type='refresh') {
if(type=='refresh'){
this.orderParam.pageNum=1;
}else{
this.orderParam.pageNum++;
}
//这里是将订单挂载到tab列表下
let index = this.tabCurrentIndex;
let navItem = this.navList[index];
let state = navItem.state;
if (this.loadingType === 'loading') {
//防止重复加载
return;
}
this.orderParam.status = navItem.state;
this.loadingType = 'loading';
fetchOrderList(this.orderParam).then(response => {
let list = response.data.list;
if(type=='refresh'){
this.orderList = list;
this.loadingType = 'more';
}else{
if(list!=null&&list.length>0){
this.orderList = this.orderList.concat(list);
this.loadingType = 'more';
}else{
this.orderParam.pageNum--;
this.loadingType = 'noMore';
}
}
});
},
//swiper 切换
changeTab(e) {
this.tabCurrentIndex = e.target.current;
this.loadData();
},
//顶部tab点击
tabClick(index) {
this.tabCurrentIndex = index;
},
//删除订单
deleteOrder(orderId) {
let superThis = this;
uni.showModal({
title: '提示',
content: '是否要删除该订单?',
success: function (res) {
if (res.confirm) {
uni.showLoading({
title: '请稍后'
})
deleteUserOrder({orderId:orderId}).then(response=>{
uni.hideLoading();
superThis.loadData();
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
//取消订单
cancelOrder(orderId) {
let superThis = this;
uni.showModal({
title: '提示',
content: '是否要取消该订单?',
success: function (res) {
if (res.confirm) {
uni.showLoading({
title: '请稍后'
})
cancelUserOrder({orderId:orderId}).then(response=>{
uni.hideLoading();
superThis.loadData();
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
//支付订单
payOrder(orderId){
uni.redirectTo({
url: `/pages/money/pay?orderId=${orderId}`
});
},
//确认收货
receiveOrder(orderId){
let superThis = this;
uni.showModal({
title: '提示',
content: '是否要确认收货?',
success: function (res) {
if (res.confirm) {
uni.showLoading({
title: '请稍后'
})
confirmReceiveOrder({orderId:orderId}).then(response=>{
uni.hideLoading();
superThis.loadData();
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
//查看订单详情
showOrderDetail(orderId){
uni.navigateTo({
url: `/pages/order/orderDetail?orderId=${orderId}`
})
},
//计算商品总数量
calcTotalQuantity(order){
let totalQuantity = 0;
if(order.orderItemList!=null&&order.orderItemList.length>0){
for(let item of order.orderItemList){
totalQuantity+=item.productQuantity
}
}
return totalQuantity;
},
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
height: 100%;
}
.swiper-box {
height: calc(100% - 40px);
}
.list-scroll-content {
height: 100%;
}
.navbar {
display: flex;
height: 40px;
padding: 0 5px;
background: #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, .06);
position: relative;
z-index: 10;
.nav-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 15px;
color: $font-color-dark;
position: relative;
&.current {
color: $base-color;
&:after {
content: '';
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 44px;
height: 0;
border-bottom: 2px solid $base-color;
}
}
}
}
.uni-swiper-item {
height: auto;
}
.order-item {
display: flex;
flex-direction: column;
padding-left: 30upx;
background: #fff;
margin-top: 16upx;
.i-top {
display: flex;
align-items: center;
height: 80upx;
padding-right: 30upx;
font-size: $font-base;
color: $font-color-dark;
position: relative;
.time {
flex: 1;
}
.state {
color: $base-color;
}
.del-btn {
padding: 10upx 0 10upx 36upx;
font-size: $font-lg;
color: $font-color-light;
position: relative;
&:after {
content: '';
width: 0;
height: 30upx;
border-left: 1px solid $border-color-dark;
position: absolute;
left: 20upx;
top: 50%;
transform: translateY(-50%);
}
}
}
/* 多条商品 */
.goods-box {
height: 160upx;
padding: 20upx 0;
white-space: nowrap;
.goods-item {
width: 120upx;
height: 120upx;
display: inline-block;
margin-right: 24upx;
}
.goods-img {
display: block;
width: 100%;
height: 100%;
}
}
/* 单条商品 */
.goods-box-single {
display: flex;
padding: 20upx 0;
.goods-img {
display: block;
width: 120upx;
height: 120upx;
}
.right {
flex: 1;
display: flex;
flex-direction: column;
padding: 0 30upx 0 24upx;
overflow: hidden;
.title {
font-size: $font-base + 2upx;
color: $font-color-dark;
line-height: 1;
}
.attr-box {
font-size: $font-sm + 2upx;
color: $font-color-light;
padding: 10upx 12upx;
}
.price {
font-size: $font-base + 2upx;
color: $font-color-dark;
&:before {
content: '¥';
font-size: $font-sm;
margin: 0 2upx 0 8upx;
}
}
}
}
.price-box {
display: flex;
justify-content: flex-end;
align-items: baseline;
padding: 20upx 30upx;
font-size: $font-sm + 2upx;
color: $font-color-light;
.num {
margin: 0 8upx;
color: $font-color-dark;
}
.price {
font-size: $font-lg;
color: $font-color-dark;
&:before {
content: '¥';
font-size: $font-sm;
margin: 0 2upx 0 8upx;
}
}
}
.action-box {
display: flex;
justify-content: flex-end;
align-items: center;
height: 100upx;
position: relative;
padding-right: 30upx;
}
.action-btn {
width: 160upx;
height: 60upx;
margin: 0;
margin-left: 24upx;
padding: 0;
text-align: center;
line-height: 60upx;
font-size: $font-sm + 2upx;
color: $font-color-dark;
background: #fff;
border-radius: 100px;
&:after {
border-radius: 100px;
}
&.recom {
background: #fff9f9;
color: $base-color;
&:after {
border-color: #f7bcc8;
}
}
}
}
/* load-more */
.uni-load-more {
display: flex;
flex-direction: row;
height: 80upx;
align-items: center;
justify-content: center
}
.uni-load-more__text {
font-size: 28upx;
color: #999
}
.uni-load-more__img {
height: 24px;
width: 24px;
margin-right: 10px
}
.uni-load-more__img>view {
position: absolute
}
.uni-load-more__img>view view {
width: 6px;
height: 2px;
border-top-left-radius: 1px;
border-bottom-left-radius: 1px;
background: #999;
position: absolute;
opacity: .2;
transform-origin: 50%;
animation: load 1.56s ease infinite
}
.uni-load-more__img>view view:nth-child(1) {
transform: rotate(90deg);
top: 2px;
left: 9px
}
.uni-load-more__img>view view:nth-child(2) {
transform: rotate(180deg);
top: 11px;
right: 0
}
.uni-load-more__img>view view:nth-child(3) {
transform: rotate(270deg);
bottom: 2px;
left: 9px
}
.uni-load-more__img>view view:nth-child(4) {
top: 11px;
left: 0
}
.load1,
.load2,
.load3 {
height: 24px;
width: 24px
}
.load2 {
transform: rotate(30deg)
}
.load3 {
transform: rotate(60deg)
}
.load1 view:nth-child(1) {
animation-delay: 0s
}
.load2 view:nth-child(1) {
animation-delay: .13s
}
.load3 view:nth-child(1) {
animation-delay: .26s
}
.load1 view:nth-child(2) {
animation-delay: .39s
}
.load2 view:nth-child(2) {
animation-delay: .52s
}
.load3 view:nth-child(2) {
animation-delay: .65s
}
.load1 view:nth-child(3) {
animation-delay: .78s
}
.load2 view:nth-child(3) {
animation-delay: .91s
}
.load3 view:nth-child(3) {
animation-delay: 1.04s
}
.load1 view:nth-child(4) {
animation-delay: 1.17s
}
.load2 view:nth-child(4) {
animation-delay: 1.3s
}
.load3 view:nth-child(4) {
animation-delay: 1.43s
}
@-webkit-keyframes load {
0% {
opacity: 1
}
100% {
opacity: .2
}
}
</style>
<template>
<view>
<view class="status-section">
<image :src="orderStatus.image" class="icon" />
<text class="label-text">{{orderStatus.text}}</text>
</view>
<!-- 地址 -->
<view class="address-section">
<view class="order-content">
<text class="yticon icon-shouhuodizhi"></text>
<view class="cen">
<view class="top">
<text class="name">{{order.receiverName}}</text>
<text class="mobile">{{order.receiverPhone}}</text>
</view>
<text class="address">{{order.receiverProvince}} {{order.receiverCity}} {{order.receiverRegion}}
{{order.receiverDetailAddress}}</text>
</view>
</view>
<image class="a-bg" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAu4AAAAFCAYAAAAaAWmiAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Rjk3RjkzMjM2NzMxMTFFOUI4RkU4OEZGMDcxQzgzOEYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Rjk3RjkzMjQ2NzMxMTFFOUI4RkU4OEZGMDcxQzgzOEYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpGOTdGOTMyMTY3MzExMUU5QjhGRTg4RkYwNzFDODM4RiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpGOTdGOTMyMjY3MzExMUU5QjhGRTg4RkYwNzFDODM4RiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PrEOZlQAAAiuSURBVHjazJp7bFvV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"></image>
</view>
<view class="goods-section">
<view class="g-header b-b">
<text class="name">商品信息</text>
</view>
<!-- 商品列表 -->
<view class="g-item" v-for="item in order.orderItemList" :key="item.id">
<image :src="item.productPic"></image>
<view class="right">
<text class="title clamp">{{item.productName}}</text>
<text class="spec">{{item.productAttr | formatProductAttr}}</text>
<text class="promotion clamp">{{item.promotionName}}</text>
<view class="price-box">
<text class="price">{{item.productPrice}}</text>
<text class="number">x {{item.productQuantity}}</text>
</view>
</view>
</view>
</view>
<!-- 金额明细 -->
<view class="yt-list">
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">商品合计</text>
<text class="cell-tip">{{order.totalAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">运费</text>
<text class="cell-tip">{{order.freightAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">活动优惠</text>
<text class="cell-tip red">-¥{{order.promotionAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">优惠券</text>
<text class="cell-tip red">-¥{{order.couponAmount}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">积分抵扣</text>
<text class="cell-tip red">-¥{{order.integrationAmount}}</text>
</view>
<view class="yt-list-cell desc-cell">
<text class="cell-tit clamp">备注</text>
<text class="cell-tip">{{order.note}}</text>
</view>
</view>
<!-- 订单明细 -->
<view class="yt-list">
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">订单编号</text>
<text class="cell-tip">{{order.orderSn}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">提交时间</text>
<text class="cell-tip">{{order.createTime | formatDateTime}}</text>
</view>
<view class="yt-list-cell b-b">
<text class="cell-tit clamp">支付方式</text>
<text class="cell-tip">{{order.payType | formatPayType}}</text>
</view>
<view class="yt-list-cell b-b" v-if="order.status==1||order.status==2||order.status==3">
<text class="cell-tit clamp">实付金额</text>
<text class="cell-tip">{{order.payAmount}}</text>
</view>
<view class="yt-list-cell b-b" v-if="order.status==1||order.status==2||order.status==3">
<text class="cell-tit clamp">付款时间</text>
<text class="cell-tip">{{order.paymentTime | formatDateTime}}</text>
</view>
</view>
<!-- 底部 -->
<view class="footer" v-if="order.status==0||order.status==2||order.status==3">
<view class="action-box b-t" v-if="order.status==0">
<button class="action-btn" @click="cancelOrder(order.id)">取消订单</button>
<button class="action-btn recom" @click="payOrder(order.id)">立即付款</button>
</view>
<view class="action-box b-t" v-if="order.status == 2">
<button class="action-btn">查看物流</button>
<button class="action-btn recom" @click="receiveOrder(order.id)">确认收货</button>
</view>
<view class="action-box b-t" v-if="order.status == 3">
<button class="action-btn">申请售后</button>
<button class="action-btn recom">评价商品</button>
</view>
<view class="price-content" v-if="order.status==0">
<text>应付金额</text>
<text class="price-tip"></text>
<text class="price">{{order.payAmount}}</text>
</view>
</view>
</view>
</template>
<script>
import {
fetchOrderDetail,
cancelUserOrder,
confirmReceiveOrder
} from '@/api/order.js';
import {
formatDate
} from '@/utils/date';
export default {
data() {
return {
orderId: null,
order: {},
orderStatus: {}
}
},
onLoad(option) {
//商品数据
this.orderId = option.orderId;
this.loadData();
},
filters: {
formatProductAttr(jsonAttr) {
let attrArr = JSON.parse(jsonAttr);
let attrStr = '';
for (let attr of attrArr) {
attrStr += attr.key;
attrStr += ":";
attrStr += attr.value;
attrStr += ";";
}
return attrStr
},
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
formatPayType(payType) {
if (payType == 0) {
return "未支付";
} else if (payType == 1) {
return "支付宝支付";
} else if (payType == 2) {
return "微信支付";
}
return null;
},
},
methods: {
//生成确认单信息
async loadData() {
fetchOrderDetail(this.orderId).then(response => {
this.order = response.data;
this.setOrderStatus(this.order.status);
});
},
submit() {},
stopPrevent() {},
//取消订单
cancelOrder(orderId) {
let superThis = this;
uni.showModal({
title: '提示',
content: '是否要取消该订单?',
success: function(res) {
if (res.confirm) {
uni.showLoading({
title: '请稍后'
})
cancelUserOrder({
orderId: orderId
}).then(response => {
uni.hideLoading();
superThis.loadData();
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
//支付订单
payOrder(orderId) {
uni.redirectTo({
url: `/pages/money/pay?orderId=${orderId}`
});
},
//确认收货
receiveOrder(orderId) {
let superThis = this;
uni.showModal({
title: '提示',
content: '是否要确认收货?',
success: function(res) {
if (res.confirm) {
uni.showLoading({
title: '请稍后'
})
confirmReceiveOrder({
orderId: orderId
}).then(response => {
uni.hideLoading();
superThis.loadData();
});
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
},
//设置订单状态信息
setOrderStatus(status) {
switch (status) {
case 0:
this.orderStatus = {
text: '等待付款',
image: '/static/icon_wait.png'
}
break;
case 1:
this.orderStatus = {
text: '等待发货',
image: '/static/icon_deliver.png'
}
break;
case 2:
this.orderStatus = {
text: '等待收货',
image: '/static/icon_receive.png'
}
break;
case 3:
this.orderStatus = {
text: '交易完成',
image: '/static/icon_finish.png'
}
break;
case 4:
this.orderStatus = {
text: '交易关闭',
image: '/static/icon_close.png'
}
break;
};
}
}
}
</script>
<style lang="scss">
page {
background: $page-color-base;
padding-bottom: 100upx;
}
.status-section {
height: 200upx;
background-color: $base-color;
display: flex;
align-items: center;
padding: 30upx;
.icon {
width: 48upx;
height: 48upx;
}
.label-text {
color: #fff;
margin-left: 30upx;
}
}
.address-section {
padding: 30upx 0;
background: #fff;
position: relative;
.order-content {
display: flex;
align-items: center;
}
.icon-shouhuodizhi {
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
width: 90upx;
color: #888;
font-size: 44upx;
}
.cen {
display: flex;
flex-direction: column;
flex: 1;
font-size: 28upx;
color: $font-color-dark;
}
.name {
font-size: 34upx;
margin-right: 24upx;
}
.address {
margin-top: 16upx;
margin-right: 20upx;
color: $font-color-light;
}
.icon-you {
font-size: 32upx;
color: $font-color-light;
margin-right: 30upx;
}
.a-bg {
position: absolute;
left: 0;
bottom: 0;
display: block;
width: 100%;
height: 5upx;
}
}
.goods-section {
margin-top: 16upx;
background: #fff;
padding-bottom: 1px;
.g-header {
display: flex;
align-items: center;
height: 84upx;
padding: 0 30upx;
position: relative;
}
.logo {
display: block;
width: 50upx;
height: 50upx;
border-radius: 100px;
}
.name {
font-size: 30upx;
color: $font-color-base;
margin-left: 24upx;
}
.g-item {
display: flex;
margin: 20upx 30upx;
image {
flex-shrink: 0;
display: block;
width: 140upx;
height: 140upx;
border-radius: 4upx;
}
.right {
flex: 1;
padding-left: 24upx;
overflow: hidden;
}
.title {
font-size: 30upx;
color: $font-color-dark;
}
.spec {
font-size: 26upx;
color: $font-color-light;
}
.promotion {
font-size: 24upx;
color: $base-color;
}
.price-box {
display: flex;
align-items: center;
font-size: 32upx;
color: $font-color-dark;
padding-top: 10upx;
.price {
margin-bottom: 4upx;
}
.number {
font-size: 26upx;
color: $font-color-base;
margin-left: 20upx;
}
}
.step-box {
position: relative;
}
}
}
.yt-list {
margin-top: 16upx;
background: #fff;
}
.yt-list-cell {
display: flex;
align-items: center;
padding: 10upx 30upx 10upx 40upx;
line-height: 70upx;
position: relative;
&.cell-hover {
background: #fafafa;
}
&.b-b:after {
left: 30upx;
}
.cell-icon {
height: 32upx;
width: 32upx;
font-size: 22upx;
color: #fff;
text-align: center;
line-height: 32upx;
background: #f85e52;
border-radius: 4upx;
margin-right: 12upx;
&.hb {
background: #ffaa0e;
}
&.lpk {
background: #3ab54a;
}
}
.cell-more {
align-self: center;
font-size: 24upx;
color: $font-color-light;
margin-left: 8upx;
margin-right: -10upx;
}
.cell-tit {
flex: 1;
font-size: 26upx;
color: $font-color-light;
margin-right: 10upx;
}
.cell-tip {
font-size: 26upx;
color: $font-color-dark;
&.disabled {
color: $font-color-light;
}
&.active {
color: $base-color;
}
&.red {
color: $base-color;
}
}
&.desc-cell {
.cell-tit {
max-width: 90upx;
}
}
.desc {
flex: 1;
font-size: $font-base;
color: $font-color-dark;
}
.integration {
flex: 1;
font-size: $font-base;
color: $font-color-dark;
text-align: right;
}
}
/* 支付列表 */
.pay-list {
padding-left: 40upx;
margin-top: 16upx;
background: #fff;
.pay-item {
display: flex;
align-items: center;
padding-right: 20upx;
line-height: 1;
height: 110upx;
position: relative;
}
.icon-weixinzhifu {
width: 80upx;
font-size: 40upx;
color: #6BCC03;
}
.icon-alipay {
width: 80upx;
font-size: 40upx;
color: #06B4FD;
}
.icon-xuanzhong2 {
display: flex;
align-items: center;
justify-content: center;
width: 60upx;
height: 60upx;
font-size: 40upx;
color: $base-color;
}
.tit {
font-size: 32upx;
color: $font-color-dark;
flex: 1;
}
}
.footer {
position: fixed;
flex-direction: row-reverse;
left: 0;
bottom: 0;
z-index: 995;
display: flex;
align-items: center;
width: 100%;
height: 90upx;
justify-content: space-between;
font-size: 30upx;
background-color: #fff;
z-index: 998;
color: $font-color-base;
box-shadow: 0 -1px 5px rgba(0, 0, 0, .1);
.price-content {
padding-left: 30upx;
}
.price-tip {
color: $base-color;
margin-left: 8upx;
}
.price {
font-size: 36upx;
color: $base-color;
}
.submit {
display: flex;
align-items: center;
justify-content: center;
width: 280upx;
height: 100%;
color: #fff;
font-size: 32upx;
background-color: $base-color;
}
}
/* 优惠券面板 */
.mask {
display: flex;
align-items: flex-end;
position: fixed;
left: 0;
top: var(--window-top);
bottom: 0;
width: 100%;
background: rgba(0, 0, 0, 0);
z-index: 9995;
transition: .3s;
.mask-content {
width: 100%;
min-height: 30vh;
max-height: 70vh;
background: #f3f3f3;
transform: translateY(100%);
transition: .3s;
overflow-y: scroll;
}
&.none {
display: none;
}
&.show {
background: rgba(0, 0, 0, .4);
.mask-content {
transform: translateY(0);
}
}
}
/* 优惠券列表 */
.coupon-item {
display: flex;
flex-direction: column;
margin: 20upx 24upx;
background: #fff;
.con {
display: flex;
align-items: center;
position: relative;
height: 120upx;
padding: 0 30upx;
&:after {
position: absolute;
left: 0;
bottom: 0;
content: '';
width: 100%;
height: 0;
border-bottom: 1px dashed #f3f3f3;
transform: scaleY(50%);
}
}
.left {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
overflow: hidden;
height: 100upx;
}
.title {
font-size: 32upx;
color: $font-color-dark;
margin-bottom: 10upx;
}
.time {
font-size: 24upx;
color: $font-color-light;
}
.right {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 26upx;
color: $font-color-base;
height: 100upx;
}
.price {
font-size: 44upx;
color: $base-color;
&:before {
content: '¥';
font-size: 34upx;
}
}
.tips {
font-size: 24upx;
color: $font-color-light;
line-height: 60upx;
padding-left: 30upx;
}
.circle {
position: absolute;
left: -6upx;
bottom: -10upx;
z-index: 10;
width: 20upx;
height: 20upx;
background: #f3f3f3;
border-radius: 100px;
&.r {
left: auto;
right: -6upx;
}
}
}
.action-box {
display: flex;
justify-content: flex-end;
align-items: center;
height: 100upx;
position: relative;
padding-right: 30upx;
}
.action-btn {
width: 160upx;
height: 60upx;
margin: 0;
margin-left: 24upx;
padding: 0;
text-align: center;
line-height: 60upx;
font-size: $font-sm + 2upx;
color: $font-color-dark;
background: #fff;
border-radius: 100px;
&:after {
border-radius: 100px;
}
&.recom {
background: #fff9f9;
color: $base-color;
&:after {
border-color: #f7bcc8;
}
}
}
</style>
<template>
<view class="content">
<image src="/static/hot_product_banner.png" class="banner-image"></image>
<view class="section-tit">相关商品</view>
<view class="goods-list">
<view v-for="(item, index) in productList" :key="index" class="goods-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFit"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2">{{item.subTitle}}</text>
<view class="price-box">
<text class="price">{{item.price}}</text>
<text>已售 {{item.sale}}</text>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import {
fetchHotProductList
} from '@/api/home.js';
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
export default {
components: {
uniLoadMore
},
data() {
return {
loadingType: 'more', //加载更多状态
productList: [],
searchParam: {
pageNum: 1,
pageSize: 6
}
};
},
onLoad(options) {
this.loadData();
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
methods: {
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum=1;
this.productList = [];
}
fetchHotProductList(this.searchParam).then(response => {
let productList = response.data;
if (response.data.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (response.data.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.productList = this.productList.concat(productList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//详情
navToDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.banner-image{
width: 100%;
}
.section-tit {
font-size: $font-base+2upx;
color: $font-color-dark;
background: #fff;
margin-top: 16upx;
text-align: center;
padding-top: 20upx;
padding-bottom: 20upx;
}
/* 商品列表 */
.goods-list {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.goods-item {
display: flex;
flex-direction: column;
width: 48%;
padding-bottom: 40upx;
&:nth-child(2n+1) {
margin-right: 4%;
}
}
.image-wrapper {
width: 100%;
height: 330upx;
border-radius: 3px;
overflow: hidden;
background-color: #fff;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price-box {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10upx;
font-size: 24upx;
color: $font-color-light;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 1;
&:before {
content: '¥';
font-size: 26upx;
}
}
}
</style>
<template>
<view class="content">
<view class="navbar" :style="{position:headerPosition,top:headerTop}">
<view class="nav-item" :class="{current: filterIndex === 0}" @click="tabClick(0)">
综合排序
</view>
<view class="nav-item" :class="{current: filterIndex === 1}" @click="tabClick(1)">
销量优先
</view>
<view class="nav-item" :class="{current: filterIndex === 2}" @click="tabClick(2)">
<text>价格</text>
<view class="p-box">
<text :class="{active: priceOrder === 1 && filterIndex === 2}" class="yticon icon-shang"></text>
<text :class="{active: priceOrder === 2 && filterIndex === 2}" class="yticon icon-shang xia"></text>
</view>
</view>
<text class="cate-item yticon icon-fenlei1" @click="toggleCateMask('show')"></text>
</view>
<view class="goods-list">
<view v-for="(item, index) in productList" :key="index" class="goods-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFill"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2">{{item.subTitle}}</text>
<view class="price-box">
<text class="price">{{item.price}}</text>
<text>已售 {{item.sale}}</text>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
<view class="cate-mask" :class="cateMaskState===0 ? 'none' : cateMaskState===1 ? 'show' : ''" @click="toggleCateMask">
<view class="cate-content" @click.stop.prevent="stopPrevent" @touchmove.stop.prevent="stopPrevent">
<scroll-view scroll-y class="cate-list">
<view v-for="item in cateList" :key="item.id">
<view class="cate-item b-b two">{{item.name}}</view>
<view v-for="tItem in item.children" :key="tItem.id" class="cate-item b-b" :class="{active: tItem.id==searchParam.productCategoryId}"
@click="changeCate(tItem)">
{{tItem.name}}
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
import {
searchProductList,
fetchCategoryTreeList
} from '@/api/product.js';
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
export default {
components: {
uniLoadMore
},
data() {
return {
cateMaskState: 0, //分类面板展开状态
headerPosition: "fixed",
headerTop: "0px",
loadingType: 'more', //加载更多状态
filterIndex: 0,
priceOrder: 0, //1 价格从低到高 2价格从高到低
cateList: [],
productList: [],
searchParam: {
productCategoryId: null,
pageNum: 1,
pageSize: 6,
sort: 0
}
};
},
onLoad(options) {
// #ifdef H5
this.headerTop = document.getElementsByTagName('uni-page-head')[0].offsetHeight + 'px';
// #endif
this.searchParam.productCategoryId = options.sid;
this.loadCateList(options.fid, options.sid);
this.loadData();
},
onPageScroll(e) {
//兼容iOS端下拉时顶部漂移
if (e.scrollTop >= 0) {
this.headerPosition = "fixed";
} else {
this.headerPosition = "absolute";
}
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
methods: {
//加载分类
async loadCateList(fid, sid) {
fetchCategoryTreeList().then(response => {
this.cateList = response.data;
})
},
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum=1;
this.productList = [];
}
if(this.filterIndex==0){
this.searchParam.sort=0;
}
if (this.filterIndex === 1) {
this.searchParam.sort = 2;
}
if (this.filterIndex === 2) {
if (this.priceOrder == 1) {
this.searchParam.sort = 3;
} else {
this.searchParam.sort = 4;
}
}
searchProductList(this.searchParam).then(response => {
let productList = response.data.list;
if (response.data.list.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (response.data.list.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.productList = this.productList.concat(productList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//筛选点击
tabClick(index) {
if (this.filterIndex === index && index !== 2) {
return;
}
this.filterIndex = index;
if (index === 2) {
this.priceOrder = this.priceOrder === 1 ? 2 : 1;
} else {
this.priceOrder = 0;
}
uni.pageScrollTo({
duration: 300,
scrollTop: 0
})
this.loadData('refresh', 1);
uni.showLoading({
title: '正在加载'
})
},
//显示分类面板
toggleCateMask(type) {
let timer = type === 'show' ? 10 : 300;
let state = type === 'show' ? 1 : 0;
this.cateMaskState = 2;
setTimeout(() => {
this.cateMaskState = state;
}, timer)
},
//分类点击
changeCate(item) {
this.searchParam.productCategoryId = item.id;
this.toggleCateMask();
uni.pageScrollTo({
duration: 300,
scrollTop: 0
})
this.loadData('refresh', 1);
uni.showLoading({
title: '正在加载'
})
},
//详情
navToDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.content {
padding-top: 96upx;
}
.navbar {
position: fixed;
left: 0;
top: var(--window-top);
display: flex;
width: 100%;
height: 80upx;
background: #fff;
box-shadow: 0 2upx 10upx rgba(0, 0, 0, .06);
z-index: 10;
.nav-item {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
font-size: 30upx;
color: $font-color-dark;
position: relative;
&.current {
color: $base-color;
&:after {
content: '';
position: absolute;
left: 50%;
bottom: 0;
transform: translateX(-50%);
width: 120upx;
height: 0;
border-bottom: 4upx solid $base-color;
}
}
}
.p-box {
display: flex;
flex-direction: column;
.yticon {
display: flex;
align-items: center;
justify-content: center;
width: 30upx;
height: 14upx;
line-height: 1;
margin-left: 4upx;
font-size: 26upx;
color: #888;
&.active {
color: $base-color;
}
}
.xia {
transform: scaleY(-1);
}
}
.cate-item {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
width: 80upx;
position: relative;
font-size: 44upx;
&:after {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
border-left: 1px solid #ddd;
width: 0;
height: 36upx;
}
}
}
/* 分类 */
.cate-mask {
position: fixed;
left: 0;
top: var(--window-top);
bottom: 0;
width: 100%;
background: rgba(0, 0, 0, 0);
z-index: 95;
transition: .3s;
.cate-content {
width: 630upx;
height: 100%;
background: #fff;
float: right;
transform: translateX(100%);
transition: .3s;
}
&.none {
display: none;
}
&.show {
background: rgba(0, 0, 0, .4);
.cate-content {
transform: translateX(0);
}
}
}
.cate-list {
display: flex;
flex-direction: column;
height: 100%;
.cate-item {
display: flex;
align-items: center;
height: 90upx;
padding-left: 30upx;
font-size: 28upx;
color: #555;
position: relative;
}
.two {
height: 64upx;
color: #303133;
font-size: 30upx;
background: #f8f8f8;
}
.active {
color: $base-color;
}
}
/* 商品列表 */
.goods-list {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.goods-item {
display: flex;
flex-direction: column;
width: 48%;
padding-bottom: 40upx;
&:nth-child(2n+1) {
margin-right: 4%;
}
}
.image-wrapper {
width: 100%;
height: 330upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price-box {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10upx;
font-size: 24upx;
color: $font-color-light;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 1;
&:before {
content: '¥';
font-size: 26upx;
}
}
}
</style>
<template>
<view class="content">
<image src="/static/new_product_banner.png" class="banner-image"></image>
<view class="section-tit">相关商品</view>
<view class="goods-list">
<view v-for="(item, index) in productList" :key="index" class="goods-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.pic" mode="aspectFit"></image>
</view>
<text class="title clamp">{{item.name}}</text>
<text class="title2">{{item.subTitle}}</text>
<view class="price-box">
<text class="price">{{item.price}}</text>
<text>已售 {{item.sale}}</text>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import {
fetchNewProductList
} from '@/api/home.js';
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
export default {
components: {
uniLoadMore
},
data() {
return {
loadingType: 'more', //加载更多状态
productList: [],
searchParam: {
pageNum: 1,
pageSize: 6
}
};
},
onLoad(options) {
this.loadData();
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
methods: {
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum=1;
this.productList = [];
}
fetchNewProductList(this.searchParam).then(response => {
let productList = response.data;
if (response.data.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (response.data.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.productList = this.productList.concat(productList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//详情
navToDetailPage(item) {
let id = item.id;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.banner-image{
width: 100%;
}
.section-tit {
font-size: $font-base+2upx;
color: $font-color-dark;
background: #fff;
margin-top: 16upx;
text-align: center;
padding-top: 20upx;
padding-bottom: 20upx;
}
/* 商品列表 */
.goods-list {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
background: #fff;
.goods-item {
display: flex;
flex-direction: column;
width: 48%;
padding-bottom: 40upx;
&:nth-child(2n+1) {
margin-right: 4%;
}
}
.image-wrapper {
width: 100%;
height: 330upx;
border-radius: 3px;
overflow: hidden;
background-color: #fff;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price-box {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 10upx;
font-size: 24upx;
color: $font-color-light;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 1;
&:before {
content: '¥';
font-size: 26upx;
}
}
}
</style>
<template>
<view class="container">
<view class="carousel">
<swiper indicator-dots circular=true duration="400">
<swiper-item class="swiper-item" v-for="(item,index) in imgList" :key="index">
<view class="image-wrapper">
<image :src="item.src" class="loaded" mode="aspectFill"></image>
</view>
</swiper-item>
</swiper>
</view>
<view class="introduce-section">
<text class="title">{{product.name}}</text><br>
<text class="title2">{{product.subTitle}}</text>
<view class="price-box">
<text class="price-tip">¥</text>
<text class="price">{{product.price}}</text>
<text class="m-price">¥{{product.originalPrice}}</text>
<!-- <text class="coupon-tip">7折</text> -->
</view>
<view class="bot-row">
<text>销量: {{product.sale}}</text>
<text>库存: {{product.stock}}</text>
<text>浏览量: 768</text>
</view>
</view>
<!-- 分享 -->
<view class="share-section" @click="share">
<view class="share-icon">
<text class="yticon icon-xingxing"></text>
</view>
<text class="tit">该商品分享可领49减10红包</text>
<text class="yticon icon-bangzhu1"></text>
<view class="share-btn">
立即分享
<text class="yticon icon-you"></text>
</view>
</view>
<view class="c-list">
<view class="c-row b-b" @click="toggleSpec">
<text class="tit">购买类型</text>
<view class="con">
<text class="selected-text" v-for="(sItem, sIndex) in specSelected" :key="sIndex">
{{sItem.name}}
</text>
</view>
<text class="yticon icon-you"></text>
</view>
<view class="c-row b-b" @click="toggleAttr">
<text class="tit">商品参数</text>
<view class="con">
<text class="con t-r">查看</text>
</view>
<text class="yticon icon-you"></text>
</view>
<view class="c-row b-b" @click="toggleCoupon('show')">
<text class="tit">优惠券</text>
<text class="con t-r red">领取优惠券</text>
<text class="yticon icon-you"></text>
</view>
<view class="c-row b-b">
<text class="tit">促销活动</text>
<view class="con-list">
<text v-for="item in promotionTipList" :key="item">{{item}}</text>
</view>
</view>
<view class="c-row b-b">
<text class="tit">服务</text>
<view class="bz-list con">
<text v-for="item in serviceList" :key="item">{{item}} ·</text>
</view>
</view>
</view>
<!-- 评价 -->
<view class="eva-section">
<view class="e-header">
<text class="tit">评价</text>
<text>(86)</text>
<text class="tip">好评率 100%</text>
<text class="yticon icon-you"></text>
</view>
<view class="eva-box">
<image class="portrait" src="http://img3.imgtn.bdimg.com/it/u=1150341365,1327279810&fm=26&gp=0.jpg" mode="aspectFill"></image>
<view class="right">
<text class="name">Leo yo</text>
<text class="con">商品收到了,79元两件,质量不错,试了一下有点瘦,但是加个外罩很漂亮,我很喜欢</text>
<view class="bot">
<text class="attr">购买类型:XL 红色</text>
<text class="time">2019-04-01 19:21</text>
</view>
</view>
</view>
</view>
<!-- 品牌信息 -->
<view class="brand-info">
<view class="d-header">
<text>品牌信息</text>
</view>
<view class="brand-box" @click="navToBrandDetail()">
<view class="image-wrapper">
<image :src="brand.logo" class="loaded" mode="aspectFit"></image>
</view>
<view class="title">
<text>{{brand.name}}</text>
<text>品牌首字母:{{brand.firstLetter}}</text>
</view>
</view>
</view>
<view class="detail-desc">
<view class="d-header">
<text>图文详情</text>
</view>
<rich-text :nodes="desc"></rich-text>
</view>
<!-- 底部操作菜单 -->
<view class="page-bottom">
<navigator url="/pages/index/index" open-type="switchTab" class="p-b-btn">
<text class="yticon icon-xiatubiao--copy"></text>
<text>首页</text>
</navigator>
<navigator url="/pages/cart/cart" open-type="switchTab" class="p-b-btn">
<text class="yticon icon-gouwuche"></text>
<text>购物车</text>
</navigator>
<view class="p-b-btn" :class="{active: favorite}" @click="toFavorite">
<text class="yticon icon-shoucang"></text>
<text>收藏</text>
</view>
<view class="action-btn-group">
<button type="primary" class=" action-btn no-border buy-now-btn" @click="buy">立即购买</button>
<button type="primary" class=" action-btn no-border add-cart-btn" @click="addToCart">加入购物车</button>
</view>
</view>
<!-- 规格-模态层弹窗 -->
<view class="popup spec" :class="specClass" @touchmove.stop.prevent="stopPrevent" @click="toggleSpec">
<!-- 遮罩层 -->
<view class="mask"></view>
<view class="layer attr-content" @click.stop="stopPrevent">
<view class="a-t">
<image :src="product.pic"></image>
<view class="right">
<text class="price">¥{{product.price}}</text>
<text class="stock">库存:{{product.stock}}</text>
<view class="selected">
已选:
<text class="selected-text" v-for="(sItem, sIndex) in specSelected" :key="sIndex">
{{sItem.name}}
</text>
</view>
</view>
</view>
<view v-for="(item,index) in specList" :key="index" class="attr-list">
<text>{{item.name}}</text>
<view class="item-list">
<text v-for="(childItem, childIndex) in specChildList" v-if="childItem.pid === item.id" :key="childIndex" class="tit"
:class="{selected: childItem.selected}" @click="selectSpec(childIndex, childItem.pid)">
{{childItem.name}}
</text>
</view>
</view>
<button class="btn" @click="toggleSpec">完成</button>
</view>
</view>
<!-- 属性-模态层弹窗 -->
<view class="popup spec" :class="attrClass" @touchmove.stop.prevent="stopPrevent" @click="toggleAttr">
<!-- 遮罩层 -->
<view class="mask"></view>
<view class="layer attr-content no-padding" @click.stop="stopPrevent">
<view class="c-list">
<view v-for="item in attrList" class="c-row b-b" :key="item.key">
<text class="tit">{{item.key}}</text>
<view class="con">
<text class="con t-r">{{item.value}}</text>
</view>
</view>
</view>
</view>
</view>
<!-- 优惠券面板 -->
<view class="mask" :class="couponState===0 ? 'none' : couponState===1 ? 'show' : ''" @click="toggleCoupon">
<view class="mask-content" @click.stop.prevent="stopPrevent">
<!-- 优惠券页面,仿mt -->
<view class="coupon-item" v-for="(item,index) in couponList" :key="index" @click="addCoupon(item)">
<view class="con">
<view class="left">
<text class="title">{{item.name}}</text>
<text class="time">有效期至{{item.endTime | formatDateTime}}</text>
</view>
<view class="right">
<text class="price">{{item.amount}}</text>
<text>{{item.minPoint}}可用</text>
</view>
<view class="circle l"></view>
<view class="circle r"></view>
</view>
<text class="tips">{{item.useType | formatCouponUseType}}</text>
</view>
</view>
</view>
<!-- 分享 -->
<share ref="share" :contentHeight="580" :shareList="shareList"></share>
</view>
</template>
<script>
import share from '@/components/share';
import {
fetchProductDetail
} from '@/api/product.js';
import {
addCartItem
} from '@/api/cart.js';
import {
fetchProductCouponList,
addMemberCoupon
} from '@/api/coupon.js';
import {
createReadHistory
} from '@/api/memberReadHistory.js';
import {
createProductCollection,
deleteProductCollection,
productCollectionDetail
} from '@/api/memberProductCollection.js';
import {
mapState
} from 'vuex';
import {
formatDate
} from '@/utils/date';
const defaultServiceList = [{
id: 1,
name: "无忧退货"
}, {
id: 2,
name: "快速退款"
}, {
id: 3,
name: "免费包邮"
}];
const defaultShareList = [{
type: 1,
icon: '/static/temp/share_wechat.png',
text: '微信好友'
},
{
type: 2,
icon: '/static/temp/share_moment.png',
text: '朋友圈'
},
{
type: 3,
icon: '/static/temp/share_qq.png',
text: 'QQ好友'
},
{
type: 4,
icon: '/static/temp/share_qqzone.png',
text: 'QQ空间'
}
]
export default {
components: {
share
},
data() {
return {
specClass: 'none',
attrClass: 'none',
specSelected: [],
favorite: false,
shareList: [],
imgList: [],
desc: '',
specList: [],
specChildList: [],
product: {},
brand: {},
serviceList: [],
skuStockList: [],
attrList: [],
promotionTipList: [],
couponState: 0,
couponList: []
};
},
async onLoad(options) {
let id = options.id;
this.shareList = defaultShareList;
this.loadData(id);
},
computed: {
...mapState(['hasLogin'])
},
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
formatCouponUseType(useType) {
if (useType == 0) {
return "全场通用";
} else if (useType == 1) {
return "指定分类商品可用";
} else if (useType == 2) {
return "指定商品可用";
}
return null;
},
},
methods: {
async loadData(id) {
fetchProductDetail(id).then(response => {
this.product = response.data.product;
this.skuStockList = response.data.skuStockList;
this.brand = response.data.brand;
this.initImgList();
this.initServiceList();
this.initSpecList(response.data);
this.initAttrList(response.data);
this.initPromotionTipList(response.data);
this.initProductDesc();
this.handleReadHistory();
this.initProductCollection();
});
},
//规格弹窗开关
toggleSpec() {
if (this.specClass === 'show') {
this.specClass = 'hide';
setTimeout(() => {
this.specClass = 'none';
}, 250);
} else if (this.specClass === 'none') {
this.specClass = 'show';
}
},
//属性弹窗开关
toggleAttr() {
if (this.attrClass === 'show') {
this.attrClass = 'hide';
setTimeout(() => {
this.attrClass = 'none';
}, 250);
} else if (this.attrClass === 'none') {
this.attrClass = 'show';
}
},
//优惠券弹窗开关
toggleCoupon(type) {
fetchProductCouponList(this.product.id).then(response => {
this.couponList = response.data;
if(this.couponList==null||this.couponList.length==0){
uni.showToast({
title:"暂无可领优惠券",
icon:"none"
})
return;
}
let timer = type === 'show' ? 10 : 300;
let state = type === 'show' ? 1 : 0;
this.couponState = 2;
setTimeout(() => {
this.couponState = state;
}, timer)
});
},
//选择规格
selectSpec(index, pid) {
let list = this.specChildList;
list.forEach(item => {
if (item.pid === pid) {
this.$set(item, 'selected', false);
}
})
this.$set(list[index], 'selected', true);
//存储已选择
/**
* 修复选择规格存储错误
* 将这几行代码替换即可
* 选择的规格存放在specSelected中
*/
this.specSelected = [];
list.forEach(item => {
if (item.selected === true) {
this.specSelected.push(item);
}
})
this.changeSpecInfo();
},
//领取优惠券
addCoupon(coupon) {
this.toggleCoupon();
addMemberCoupon(coupon.id).then(response => {
uni.showToast({
title: '领取优惠券成功!',
duration: 2000
});
});
},
//分享
share() {
this.$refs.share.toggleMask();
},
//收藏
toFavorite() {
if (!this.checkForLogin()) {
return;
}
if (this.favorite) {
//取消收藏
deleteProductCollection({
productId: this.product.id
}).then(response => {
uni.showToast({
title: "取消收藏成功!",
icon: 'none'
});
this.favorite = !this.favorite;
});
} else {
//收藏
let productCollection = {
productId: this.product.id,
productName: this.product.name,
productPic: this.product.pic,
productPrice: this.product.price,
productSubTitle: this.product.subTitle
}
createProductCollection(productCollection).then(response => {
uni.showToast({
title: "收藏成功!",
icon: 'none'
});
this.favorite = !this.favorite;
});
}
},
buy() {
uni.showToast({
title: "暂时只支持从购物车下单!",
icon: 'none'
});
},
stopPrevent() {},
//设置头图信息
initImgList() {
let tempPics = this.product.albumPics.split(',');
tempPics.unshift(this.product.pic);
for (let item of tempPics) {
if (item != null && item != '') {
this.imgList.push({
src: item
});
}
}
},
//设置服务信息
initServiceList() {
for (let item of defaultServiceList) {
if (this.product.serviceIds.indexOf(item.id) != -1) {
this.serviceList.push(item.name);
}
}
},
//设置商品规格
initSpecList(data) {
for (let i = 0; i < data.productAttributeList.length; i++) {
let item = data.productAttributeList[i];
if (item.type == 0) {
this.specList.push({
id: item.id,
name: item.name
});
if (item.handAddStatus == 1) {
//支持手动新增的
let valueList = data.productAttributeValueList;
let filterValueList = valueList.filter(value => value.productAttributeId == item.id);
let inputList = filterValueList[0].value.split(',');
for (let j = 0; j < inputList.length; j++) {
this.specChildList.push({
pid: item.id,
pname: item.name,
name: inputList[j]
});
}
} else if (item.handAddStatus == 0) {
//不支持手动新增的
let inputList = item.inputList.split(',');
for (let j = 0; j < inputList.length; j++) {
this.specChildList.push({
pid: item.id,
pname: item.name,
name: inputList[j]
});
}
}
}
}
let availAbleSpecSet = new Set();
for (let i = 0; i < this.skuStockList.length; i++) {
let spDataArr = JSON.parse(this.skuStockList[i].spData);
for (let j = 0; j < spDataArr.length; j++) {
availAbleSpecSet.add(spDataArr[j].value);
}
}
// 根据商品sku筛选出可用规格
this.specChildList = this.specChildList.filter(item => {
return availAbleSpecSet.has(item.name)
});
// 规格 默认选中第一条
this.specList.forEach(item => {
for (let cItem of this.specChildList) {
if (cItem.pid === item.id) {
this.$set(cItem, 'selected', true);
this.specSelected.push(cItem);
this.changeSpecInfo();
break;
}
}
})
},
//设置商品参数
initAttrList(data) {
for (let item of data.productAttributeList) {
if (item.type == 1) {
let valueList = data.productAttributeValueList;
let filterValueList = valueList.filter(value => value.productAttributeId == item.id);
let value = filterValueList[0].value;
this.attrList.push({
key: item.name,
value: value
});
}
}
},
//设置促销活动信息
initPromotionTipList(data) {
let promotionType = this.product.promotionType;
if (promotionType == 0) {
this.promotionTipList.push("暂无优惠");
} else if (promotionType == 1) {
this.promotionTipList.push("单品优惠");
} else if (promotionType == 2) {
this.promotionTipList.push("会员优惠");
} else if (promotionType == 3) {
this.promotionTipList.push("多买优惠");
for (let item of data.productLadderList) {
this.promotionTipList.push("满" + item.count + "件打" + item.discount * 10 + "折");
}
} else if (promotionType == 4) {
this.promotionTipList.push("满减优惠");
for (let item of data.productFullReductionList) {
this.promotionTipList.push("满" + item.fullPrice + "元减" + item.reducePrice + "元");
}
} else if (promotionType == 5) {
this.promotionTipList.push("限时优惠");
}
},
//初始化商品详情信息
initProductDesc() {
let rawhtml = this.product.detailMobileHtml;
let tempNode = document.createElement('div');
tempNode.innerHTML = rawhtml;
let imgs = tempNode.getElementsByTagName('img');
for (let i = 0; i < imgs.length; i++) {
imgs[i].style.width = '100%';
imgs[i].style.height = 'auto';
imgs[i].style.display = 'block';
}
this.desc = tempNode.innerHTML;
},
//处理创建浏览记录
handleReadHistory() {
if (this.hasLogin) {
let data = {
productId: this.product.id,
productName: this.product.name,
productPic: this.product.pic,
productPrice: this.product.price,
productSubTitle: this.product.subTitle,
}
createReadHistory(data);
}
},
//当商品规格改变时,修改商品信息
changeSpecInfo() {
let skuStock = this.getSkuStock();
if (skuStock != null) {
this.product.originalPrice = skuStock.price;
if (this.product.promotionType == 1) {
//单品优惠使用促销价
this.product.price = skuStock.promotionPrice;
} else {
this.product.price = skuStock.price;
}
this.product.stock = skuStock.stock;
}
},
//获取当前选中商品的SKU
getSkuStock() {
for (let i = 0; i < this.skuStockList.length; i++) {
let spDataArr = JSON.parse(this.skuStockList[i].spData);
let availAbleSpecSet = new Map();
for (let j = 0; j < spDataArr.length; j++) {
availAbleSpecSet.set(spDataArr[j].key, spDataArr[j].value);
}
let correctCount = 0;
for (let item of this.specSelected) {
let value = availAbleSpecSet.get(item.pname);
if (value != null && value == item.name) {
correctCount++;
}
}
if (correctCount == this.specSelected.length) {
return this.skuStockList[i];
}
}
return null;
},
//将商品加入到购物车
addToCart() {
if (!this.checkForLogin()) {
return;
}
let productSkuStock = this.getSkuStock();
let cartItem = {
price: this.product.price,
productAttr: productSkuStock.spData,
productBrand: this.product.brandName,
productCategoryId: this.product.productCategoryId,
productId: this.product.id,
productName: this.product.name,
productPic: this.product.pic,
productSkuCode: productSkuStock.skuCode,
productSkuId: productSkuStock.id,
productSn: this.product.productSn,
productSubTitle: this.product.subTitle,
quantity: 1
};
addCartItem(cartItem).then(response => {
uni.showToast({
title: response.message,
duration: 1500
})
});
},
//检查登录状态并弹出登录框
checkForLogin() {
if (!this.hasLogin) {
uni.showModal({
title: '提示',
content: '你还没登录,是否要登录?',
confirmText: '去登录',
cancelText: '取消',
success: function(res) {
if (res.confirm) {
uni.navigateTo({
url: '/pages/public/login'
})
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
return false;
} else {
return true;
}
},
//初始化收藏状态
initProductCollection() {
if (this.hasLogin) {
productCollectionDetail({
productId: this.product.id
}).then(response => {
this.favorite = response.data != null;
});
}
},
//跳转到品牌详情页
navToBrandDetail(){
let id = this.brand.id;
uni.navigateTo({
url: `/pages/brand/brandDetail?id=${id}`
})
},
},
}
</script>
<style lang='scss'>
page {
background: $page-color-base;
padding-bottom: 160upx;
}
.icon-you {
font-size: $font-base + 2upx;
color: #888;
}
.carousel {
height: 722upx;
position: relative;
swiper {
height: 100%;
}
.image-wrapper {
width: 100%;
height: 100%;
}
.swiper-item {
display: flex;
justify-content: center;
align-content: center;
height: 750upx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
}
}
/* 标题简介 */
.introduce-section {
background: #fff;
padding: 20upx 30upx;
.title {
font-size: 32upx;
color: $font-color-dark;
height: 50upx;
line-height: 50upx;
}
.title2 {
font-size: 28upx;
color: $font-color-light;
height: 46upx;
line-height: 46upx;
}
.price-box {
display: flex;
align-items: baseline;
height: 64upx;
padding: 10upx 0;
font-size: 26upx;
color: $uni-color-primary;
}
.price {
font-size: $font-lg + 2upx;
}
.m-price {
margin: 0 12upx;
color: $font-color-light;
text-decoration: line-through;
}
.coupon-tip {
align-items: center;
padding: 4upx 10upx;
background: $uni-color-primary;
font-size: $font-sm;
color: #fff;
border-radius: 6upx;
line-height: 1;
transform: translateY(-4upx);
}
.bot-row {
display: flex;
align-items: center;
height: 50upx;
font-size: $font-sm;
color: $font-color-light;
text {
flex: 1;
}
}
}
/* 分享 */
.share-section {
display: flex;
align-items: center;
color: $font-color-base;
background: linear-gradient(left, #fdf5f6, #fbebf6);
padding: 12upx 30upx;
.share-icon {
display: flex;
align-items: center;
width: 70upx;
height: 30upx;
line-height: 1;
border: 1px solid $uni-color-primary;
border-radius: 4upx;
position: relative;
overflow: hidden;
font-size: 22upx;
color: $uni-color-primary;
&:after {
content: '';
width: 50upx;
height: 50upx;
border-radius: 50%;
left: -20upx;
top: -12upx;
position: absolute;
background: $uni-color-primary;
}
}
.icon-xingxing {
position: relative;
z-index: 1;
font-size: 24upx;
margin-left: 2upx;
margin-right: 10upx;
color: #fff;
line-height: 1;
}
.tit {
font-size: $font-base;
margin-left: 10upx;
}
.icon-bangzhu1 {
padding: 10upx;
font-size: 30upx;
line-height: 1;
}
.share-btn {
flex: 1;
text-align: right;
font-size: $font-sm;
color: $uni-color-primary;
}
.icon-you {
font-size: $font-sm;
margin-left: 4upx;
color: $uni-color-primary;
}
}
.c-list {
font-size: $font-sm + 2upx;
color: $font-color-base;
background: #fff;
.c-row {
display: flex;
align-items: center;
padding: 20upx 30upx;
position: relative;
}
.tit {
width: 140upx;
}
.con {
flex: 1;
color: $font-color-dark;
.selected-text {
margin-right: 10upx;
}
}
.bz-list {
height: 40upx;
font-size: $font-sm+2upx;
color: $font-color-dark;
text {
display: inline-block;
margin-right: 30upx;
}
}
.con-list {
flex: 1;
display: flex;
flex-direction: column;
color: $font-color-dark;
line-height: 40upx;
}
.red {
color: $uni-color-primary;
}
}
/* 评价 */
.eva-section {
display: flex;
flex-direction: column;
padding: 20upx 30upx;
background: #fff;
margin-top: 16upx;
.e-header {
display: flex;
align-items: center;
height: 70upx;
font-size: $font-sm + 2upx;
color: $font-color-light;
.tit {
font-size: $font-base + 2upx;
color: $font-color-dark;
margin-right: 4upx;
}
.tip {
flex: 1;
text-align: right;
}
.icon-you {
margin-left: 10upx;
}
}
}
.eva-box {
display: flex;
padding: 20upx 0;
.portrait {
flex-shrink: 0;
width: 80upx;
height: 80upx;
border-radius: 100px;
}
.right {
flex: 1;
display: flex;
flex-direction: column;
font-size: $font-base;
color: $font-color-base;
padding-left: 26upx;
.con {
font-size: $font-base;
color: $font-color-dark;
padding: 20upx 0;
}
.bot {
display: flex;
justify-content: space-between;
font-size: $font-sm;
color: $font-color-light;
}
}
}
/* 详情 */
.detail-desc {
background: #fff;
margin-top: 16upx;
.d-header {
display: flex;
justify-content: center;
align-items: center;
height: 80upx;
font-size: $font-base + 2upx;
color: $font-color-dark;
position: relative;
text {
padding: 0 20upx;
background: #fff;
position: relative;
z-index: 1;
}
&:after {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%);
width: 300upx;
height: 0;
content: '';
border-bottom: 1px solid #ccc;
}
}
}
.detail-desc>>>img {
width: 100%;
height: auto;
}
/* 规格选择弹窗 */
.attr-content {
padding: 10upx 30upx;
.a-t {
display: flex;
image {
width: 170upx;
height: 170upx;
flex-shrink: 0;
margin-top: -40upx;
border-radius: 8upx;
;
}
.right {
display: flex;
flex-direction: column;
padding-left: 24upx;
font-size: $font-sm + 2upx;
color: $font-color-base;
line-height: 42upx;
.price {
font-size: $font-lg;
color: $uni-color-primary;
margin-bottom: 10upx;
}
.selected-text {
margin-right: 10upx;
}
}
}
.attr-list {
display: flex;
flex-direction: column;
font-size: $font-base + 2upx;
color: $font-color-base;
padding-top: 30upx;
padding-left: 10upx;
}
.item-list {
padding: 20upx 0 0;
display: flex;
flex-wrap: wrap;
text {
display: flex;
align-items: center;
justify-content: center;
background: #eee;
margin-right: 20upx;
margin-bottom: 20upx;
border-radius: 100upx;
min-width: 60upx;
height: 60upx;
padding: 0 20upx;
font-size: $font-base;
color: $font-color-dark;
}
.selected {
background: #fbebee;
color: $uni-color-primary;
}
}
}
.no-padding {
padding: 0upx 0upx;
}
/* 弹出层 */
.popup {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 99;
&.show {
display: block;
.mask {
animation: showPopup 0.2s linear both;
}
.layer {
animation: showLayer 0.2s linear both;
}
}
&.hide {
.mask {
animation: hidePopup 0.2s linear both;
}
.layer {
animation: hideLayer 0.2s linear both;
}
}
&.none {
display: none;
}
.mask {
position: fixed;
top: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: rgba(0, 0, 0, 0.4);
}
.layer {
position: fixed;
z-index: 99;
bottom: 0;
width: 100%;
min-height: 40vh;
border-radius: 10upx 10upx 0 0;
background-color: #fff;
.btn {
height: 66upx;
line-height: 66upx;
border-radius: 100upx;
background: $uni-color-primary;
font-size: $font-base + 2upx;
color: #fff;
margin: 30upx auto 20upx;
}
}
@keyframes showPopup {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes hidePopup {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
@keyframes showLayer {
0% {
transform: translateY(120%);
}
100% {
transform: translateY(0%);
}
}
@keyframes hideLayer {
0% {
transform: translateY(0);
}
100% {
transform: translateY(120%);
}
}
}
/* 底部操作菜单 */
.page-bottom {
position: fixed;
left: 30upx;
bottom: 30upx;
z-index: 95;
display: flex;
justify-content: center;
align-items: center;
width: 690upx;
height: 100upx;
background: rgba(255, 255, 255, .9);
box-shadow: 0 0 20upx 0 rgba(0, 0, 0, .5);
border-radius: 16upx;
.p-b-btn {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: $font-sm;
color: $font-color-base;
width: 96upx;
height: 80upx;
.yticon {
font-size: 40upx;
line-height: 48upx;
color: $font-color-light;
}
&.active,
&.active .yticon {
color: $uni-color-primary;
}
.icon-fenxiang2 {
font-size: 42upx;
transform: translateY(-2upx);
}
.icon-shoucang {
font-size: 46upx;
}
}
.action-btn-group {
display: flex;
height: 76upx;
border-radius: 100px;
overflow: hidden;
box-shadow: 0 20upx 40upx -16upx #fa436a;
box-shadow: 1px 2px 5px rgba(219, 63, 96, 0.4);
background: linear-gradient(to right, #ffac30, #fa436a, #F56C6C);
margin-left: 20upx;
position: relative;
&:after {
content: '';
position: absolute;
top: 50%;
right: 50%;
transform: translateY(-50%);
height: 28upx;
width: 0;
border-right: 1px solid rgba(255, 255, 255, .5);
}
.action-btn {
display: flex;
align-items: center;
justify-content: center;
width: 180upx;
height: 100%;
font-size: $font-base;
padding: 0;
border-radius: 0;
background: transparent;
}
}
}
/* 优惠券面板 */
.mask {
display: flex;
align-items: flex-end;
position: fixed;
left: 0;
top: var(--window-top);
bottom: 0;
width: 100%;
background: rgba(0, 0, 0, 0);
z-index: 9995;
transition: .3s;
.mask-content {
width: 100%;
min-height: 30vh;
max-height: 70vh;
background: #f3f3f3;
transform: translateY(100%);
transition: .3s;
overflow-y: scroll;
}
&.none {
display: none;
}
&.show {
background: rgba(0, 0, 0, .4);
.mask-content {
transform: translateY(0);
}
}
}
/* 优惠券列表 */
.coupon-item {
display: flex;
flex-direction: column;
margin: 20upx 24upx;
background: #fff;
.con {
display: flex;
align-items: center;
position: relative;
height: 120upx;
padding: 0 30upx;
&:after {
position: absolute;
left: 0;
bottom: 0;
content: '';
width: 100%;
height: 0;
border-bottom: 1px dashed #f3f3f3;
transform: scaleY(50%);
}
}
.left {
display: flex;
flex-direction: column;
justify-content: center;
flex: 1;
overflow: hidden;
height: 100upx;
}
.title {
font-size: 32upx;
color: $font-color-dark;
margin-bottom: 10upx;
}
.time {
font-size: 24upx;
color: $font-color-light;
}
.right {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
font-size: 26upx;
color: $font-color-base;
height: 100upx;
}
.price {
font-size: 44upx;
color: $base-color;
&:before {
content: '¥';
font-size: 34upx;
}
}
.tips {
font-size: 24upx;
color: $font-color-light;
line-height: 60upx;
padding-left: 30upx;
}
.circle {
position: absolute;
left: -6upx;
bottom: -10upx;
z-index: 10;
width: 20upx;
height: 20upx;
background: #f3f3f3;
border-radius: 100px;
&.r {
left: auto;
right: -6upx;
}
}
}
.brand-info {
margin-top: 16upx;
background-color: #fff;
display: flex;
flex-direction: column;
.brand-box {
display: flex;
align-items: center;
padding: 30upx 50upx;
.image-wrapper {
width: 210upx;
height: 70upx;
image {
width: 100%;
height: 100%;
}
}
.title {
flex: 1;
display: flex;
flex-direction: column;
font-size: $font-lg+4upx;
margin-left: 30upx;
color: $font-color-dark;
text:last-child {
font-size: $font-sm;
color: $font-color-light;
margin-top: 8upx;
&.Skeleton {
width: 220upx;
}
}
}
}
.d-header {
display: flex;
justify-content: center;
align-items: center;
height: 80upx;
font-size: $font-base + 2upx;
color: $font-color-dark;
position: relative;
text {
padding: 0 20upx;
background: #fff;
position: relative;
z-index: 1;
}
&:after {
position: absolute;
left: 50%;
top: 50%;
transform: translateX(-50%);
width: 300upx;
height: 0;
content: '';
border-bottom: 1px solid #ccc;
}
}
}
</style>
<template>
<view class="container">
<view class="left-bottom-sign"></view>
<view class="back-btn yticon icon-zuojiantou-up" @click="navBack"></view>
<view class="right-top-sign"></view>
<!-- 设置白色背景防止软键盘把下部绝对定位元素顶上来盖住输入框等 -->
<view class="wrapper">
<view class="left-top-sign">LOGIN</view>
<view class="welcome">
欢迎回来!
</view>
<view class="input-content">
<view class="input-item">
<text class="tit">用户名</text>
<input type="text" v-model="username" placeholder="请输入用户名" maxlength="11"/>
</view>
<view class="input-item">
<text class="tit">密码</text>
<input type="text" v-model="password" placeholder="8-18位不含特殊字符的数字、字母组合" placeholder-class="input-empty" maxlength="20"
password @confirm="toLogin" />
</view>
</view>
<button class="confirm-btn" @click="toLogin" :disabled="logining">登录</button>
<button class="confirm-btn2" @click="toRegist" >获取体验账号</button>
<view class="forget-section" @click="toRegist">
忘记密码?
</view>
</view>
<view class="register-section">
还没有账号?
<text @click="toRegist">马上注册</text>
</view>
</view>
</template>
<script>
import {
mapMutations
} from 'vuex';
import {
memberLogin,memberInfo
} from '@/api/member.js';
export default {
data() {
return {
username: '',
password: '',
logining: false
}
},
onLoad() {
this.username = uni.getStorageSync('username') || '';
this.password = uni.getStorageSync('password') || '';
},
methods: {
...mapMutations(['login']),
navBack() {
uni.navigateBack();
},
toRegist() {
uni.navigateTo({url:'/pages/public/register'});
},
async toLogin() {
this.logining = true;
memberLogin({
username: this.username,
password: this.password
}).then(response => {
let token = response.data.tokenHead+response.data.token;
uni.setStorageSync('token',token);
uni.setStorageSync('username',this.username);
uni.setStorageSync('password',this.password);
memberInfo().then(response=>{
this.login(response.data);
uni.navigateBack();
});
}).catch(() => {
this.logining = false;
});
},
},
}
</script>
<style lang='scss'>
page {
background: #fff;
}
.container {
padding-top: 115px;
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
background: #fff;
}
.wrapper {
position: relative;
z-index: 90;
background: #fff;
padding-bottom: 40upx;
}
.back-btn {
position: absolute;
left: 40upx;
z-index: 9999;
padding-top: var(--status-bar-height);
top: 40upx;
font-size: 40upx;
color: $font-color-dark;
}
.left-top-sign {
font-size: 120upx;
color: $page-color-base;
position: relative;
left: -16upx;
}
.right-top-sign {
position: absolute;
top: 80upx;
right: -30upx;
z-index: 95;
&:before,
&:after {
display: block;
content: "";
width: 400upx;
height: 80upx;
background: #b4f3e2;
}
&:before {
transform: rotate(50deg);
border-radius: 0 50px 0 0;
}
&:after {
position: absolute;
right: -198upx;
top: 0;
transform: rotate(-50deg);
border-radius: 50px 0 0 0;
/* background: pink; */
}
}
.left-bottom-sign {
position: absolute;
left: -270upx;
bottom: -320upx;
border: 100upx solid #d0d1fd;
border-radius: 50%;
padding: 180upx;
}
.welcome {
position: relative;
left: 50upx;
top: -90upx;
font-size: 46upx;
color: #555;
text-shadow: 1px 0px 1px rgba(0, 0, 0, .3);
}
.input-content {
padding: 0 60upx;
}
.input-item {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding: 0 30upx;
background: $page-color-light;
height: 120upx;
border-radius: 4px;
margin-bottom: 50upx;
&:last-child {
margin-bottom: 0;
}
.tit {
height: 50upx;
line-height: 56upx;
font-size: $font-sm+2upx;
color: $font-color-base;
}
input {
height: 60upx;
font-size: $font-base + 2upx;
color: $font-color-dark;
width: 100%;
}
}
.confirm-btn {
width: 630upx;
height: 76upx;
line-height: 76upx;
border-radius: 50px;
margin-top: 70upx;
background: $uni-color-primary;
color: #fff;
font-size: $font-lg;
&:after {
border-radius: 100px;
}
}
.confirm-btn2 {
width: 630upx;
height: 76upx;
line-height: 76upx;
border-radius: 50px;
margin-top: 40upx;
background: $uni-color-primary;
color: #fff;
font-size: $font-lg;
&:after {
border-radius: 100px;
}
}
.forget-section {
font-size: $font-sm+2upx;
color: $font-color-spec;
text-align: center;
margin-top: 40upx;
}
.register-section {
position: absolute;
left: 0;
bottom: 50upx;
width: 100%;
font-size: $font-sm+2upx;
color: $font-color-base;
text-align: center;
text {
color: $font-color-spec;
margin-left: 10upx;
}
}
</style>
<template>
<view class="container">
<view class="left-bottom-sign"></view>
<view class="back-btn yticon icon-zuojiantou-up" @click="navBack"></view>
<view class="right-top-sign"></view>
<!-- 设置白色背景防止软键盘把下部绝对定位元素顶上来盖住输入框等 -->
<view class="wrapper">
<view class="empty">
<image src="/static/qrcode_for_macrozheng_258.jpg" mode="aspectFit"></image>
<view class="empty-tips">
扫描上方二维码<view class="navigator">关注公众号</view>
</view>
<view class="empty-tips">
回复<view class="navigator">会员</view>获取体验账号。
</view>
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {}
},
onLoad() {
},
methods: {
navBack() {
uni.navigateBack();
},
},
}
</script>
<style lang='scss'>
page {
background: #fff;
}
.empty {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100vh;
padding-bottom: 100upx;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
background: #fff;
image {
width: 420upx;
height: 420upx;
margin-bottom: 30upx;
}
.empty-tips {
display: flex;
font-size: $font-sm+16upx;
color: $font-color-disabled;
.navigator {
color: $uni-color-primary;
margin-left: 0upx;
}
}
}
.container {
padding-top: 115px;
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
background: #fff;
}
.wrapper {
position: relative;
z-index: 90;
background: #fff;
padding-bottom: 40upx;
}
.back-btn {
position: absolute;
left: 40upx;
z-index: 9999;
padding-top: var(--status-bar-height);
top: 40upx;
font-size: 40upx;
color: $font-color-dark;
}
.left-top-sign {
font-size: 120upx;
color: $page-color-base;
position: relative;
left: -16upx;
}
.right-top-sign {
position: absolute;
top: 80upx;
right: -30upx;
z-index: 95;
&:before,
&:after {
display: block;
content: "";
width: 400upx;
height: 80upx;
background: #b4f3e2;
}
&:before {
transform: rotate(50deg);
border-radius: 0 50px 0 0;
}
&:after {
position: absolute;
right: -198upx;
top: 0;
transform: rotate(-50deg);
border-radius: 50px 0 0 0;
/* background: pink; */
}
}
.left-bottom-sign {
position: absolute;
left: -270upx;
bottom: -320upx;
border: 100upx solid #d0d1fd;
border-radius: 50%;
padding: 180upx;
}
</style>
<template>
<view class="container">
<view class="list-cell b-b m-t" @click="navTo('个人资料')" hover-class="cell-hover" :hover-stay-time="50">
<text class="cell-tit">个人资料</text>
<text class="cell-more yticon icon-you"></text>
</view>
<view class="list-cell b-b" @click="navTo('/pages/address/address')" hover-class="cell-hover" :hover-stay-time="50">
<text class="cell-tit">收货地址</text>
<text class="cell-more yticon icon-you"></text>
</view>
<view class="list-cell" @click="navTo('实名认证')" hover-class="cell-hover" :hover-stay-time="50">
<text class="cell-tit">实名认证</text>
<text class="cell-more yticon icon-you"></text>
</view>
<view class="list-cell m-t">
<text class="cell-tit">消息推送</text>
<switch checked color="#fa436a" @change="switchChange" />
</view>
<view class="list-cell m-t b-b" @click="navTo('清除缓存')" hover-class="cell-hover" :hover-stay-time="50">
<text class="cell-tit">清除缓存</text>
<text class="cell-more yticon icon-you"></text>
</view>
<view class="list-cell b-b" @click="navToOuter('https://github.com/macrozheng/mall')" hover-class="cell-hover" :hover-stay-time="50">
<text class="cell-tit">关于mall-app-web</text>
<text class="cell-more yticon icon-you"></text>
</view>
<view class="list-cell">
<text class="cell-tit">检查更新</text>
<text class="cell-tip">当前版本 1.0.0</text>
<text class="cell-more yticon icon-you"></text>
</view>
<view class="list-cell log-out-btn" @click="toLogout">
<text class="cell-tit">退出登录</text>
</view>
</view>
</template>
<script>
import {
mapMutations
} from 'vuex';
export default {
data() {
return {
};
},
methods:{
...mapMutations(['logout']),
navTo(url){
if(url.indexOf("pages")!=-1){
uni.navigateTo({
url:url
});
}
this.$api.msg(`跳转到${url}`);
},
navToOuter(url){
window.location.href = url;
},
//退出登录
toLogout(){
uni.showModal({
content: '确定要退出登录么',
success: (e)=>{
if(e.confirm){
this.logout();
setTimeout(()=>{
uni.navigateBack();
}, 200)
}
}
});
},
//switch
switchChange(e){
let statusTip = e.detail.value ? '打开': '关闭';
this.$api.msg(`${statusTip}消息推送`);
},
}
}
</script>
<style lang='scss'>
page{
background: $page-color-base;
}
.list-cell{
display:flex;
align-items:baseline;
padding: 20upx $page-row-spacing;
line-height:60upx;
position:relative;
background: #fff;
justify-content: center;
&.log-out-btn{
margin-top: 40upx;
.cell-tit{
color: $uni-color-primary;
text-align: center;
margin-right: 0;
}
}
&.cell-hover{
background:#fafafa;
}
&.b-b:after{
left: 30upx;
}
&.m-t{
margin-top: 16upx;
}
.cell-more{
align-self: baseline;
font-size:$font-lg;
color:$font-color-light;
margin-left:10upx;
}
.cell-tit{
flex: 1;
font-size: $font-base + 2upx;
color: $font-color-dark;
margin-right:10upx;
}
.cell-tip{
font-size: $font-base;
color: $font-color-light;
}
switch{
transform: translateX(16upx) scale(.84);
}
}
</style>
<template>
<view class="content">
<!-- 空白页 -->
<empty v-if="brandList==null||brandList.length === 0"></empty>
<view class="hot-section">
<view v-for="(item, index) in brandList" :key="index" class="guess-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.brandLogo" mode="aspectFit"></image>
</view>
<view class="txt">
<text class="title clamp">{{item.brandName}}</text>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import empty from "@/components/empty";
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
import {
formatDate
} from '@/utils/date';
import {
fetchBrandAttentionList,
clearBrandAttention
} from '@/api/memberBrandAttention.js';
export default {
components: {
uniLoadMore,
empty
},
data() {
return {
loadingType: 'more',
brandList: [],
searchParam: {
pageNum: 1,
pageSize: 6
}
};
},
onLoad(options) {
this.loadData();
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
// #ifndef MP
onNavigationBarButtonTap(e) {
const index = e.index;
let thisObj = this;
if (index === 0) {
uni.showModal({
title: '提示',
content: '是否要清空所有浏览记录?',
success: function (res) {
if (res.confirm) {
clearBrandAttention().then(response=>{
thisObj.loadData('refresh');
});
}
}
});
}
},
// #endif
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum = 1;
this.brandList = [];
}
fetchBrandAttentionList(this.searchParam).then(response => {
let dataList = response.data.list;
if (dataList.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (dataList.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.brandList = this.brandList.concat(dataList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//详情
navToDetailPage(item) {
let id = item.brandId;
uni.navigateTo({
url: `/pages/brand/brandDetail?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.hot-section {
display: flex;
flex-wrap: wrap;
margin-top: 16upx;
.guess-item {
display: flex;
flex-direction: row;
width: 100%;
padding: 0 30upx;
margin-bottom: 16upx;
background-color: #fff;
align-items: center;
}
.image-wrapper {
width: 30%;
height: 170upx;
border-radius: 3px;
overflow: hidden;
background: #fff;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 80upx;
}
.txt {
width: 70%;
display: flex;
flex-direction: row;
padding-left: 40upx;
align-items: center;
}
.hor-txt{
display: flex;
justify-content: space-between;
}
.time {
font-size: $font-sm;
color: $font-color-dark;
line-height: 80upx;
}
}
</style>
<template>
<view class="content">
<!-- 空白页 -->
<empty v-if="productList==null||productList.length === 0"></empty>
<view class="hot-section">
<view v-for="(item, index) in productList" :key="index" class="guess-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.productPic" mode="aspectFill"></image>
</view>
<view class="txt">
<text class="title clamp">{{item.productName}}</text>
<text class="title2">{{item.productSubTitle}}</text>
<view class="hor-txt">
<text class="price">{{item.productPrice}}</text>
</view>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import empty from "@/components/empty";
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
import {
formatDate
} from '@/utils/date';
import {
fetchProductCollectionList,
clearProductCollection
} from '@/api/memberProductCollection.js';
export default {
components: {
uniLoadMore,
empty
},
data() {
return {
loadingType: 'more',
productList: [],
searchParam: {
pageNum: 1,
pageSize: 6
}
};
},
onLoad(options) {
this.loadData();
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
// #ifndef MP
onNavigationBarButtonTap(e) {
const index = e.index;
let thisObj = this;
if (index === 0) {
uni.showModal({
title: '提示',
content: '是否要清空所有浏览记录?',
success: function (res) {
if (res.confirm) {
clearProductCollection().then(response=>{
thisObj.loadData('refresh');
});
}
}
});
}
},
// #endif
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum = 1;
this.productList = [];
}
fetchProductCollectionList(this.searchParam).then(response => {
let dataList = response.data.list;
if (dataList.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (dataList.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.productList = this.productList.concat(dataList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//详情
navToDetailPage(item) {
let id = item.productId;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.hot-section {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
margin-top: 16upx;
background: #fff;
.guess-item {
display: flex;
flex-direction: row;
width: 100%;
padding-bottom: 40upx;
}
.image-wrapper {
width: 30%;
height: 250upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 80upx;
}
.txt {
width: 70%;
display: flex;
flex-direction: column;
padding-left: 40upx;
}
.hor-txt{
display: flex;
justify-content: space-between;
}
.time {
font-size: $font-sm;
color: $font-color-dark;
line-height: 80upx;
}
}
</style>
<template>
<view class="content">
<!-- 空白页 -->
<empty v-if="productList==null||productList.length === 0"></empty>
<view class="hot-section">
<view v-for="(item, index) in productList" :key="index" class="guess-item" @click="navToDetailPage(item)">
<view class="image-wrapper">
<image :src="item.productPic" mode="aspectFill"></image>
</view>
<view class="txt">
<text class="title clamp">{{item.productName}}</text>
<text class="title2">{{item.productSubTitle}}</text>
<view class="hor-txt">
<text class="price">{{item.productPrice}}</text>
<text class="time">{{item.createTime | formatDateTime}}</text>
</view>
</view>
</view>
</view>
<uni-load-more :status="loadingType"></uni-load-more>
</view>
</template>
<script>
import empty from "@/components/empty";
import uniLoadMore from '@/components/uni-load-more/uni-load-more.vue';
import {
formatDate
} from '@/utils/date';
import {
fetchReadHistoryList,
clearReadHistory
} from '@/api/memberReadHistory.js';
export default {
components: {
uniLoadMore,
empty
},
data() {
return {
loadingType: 'more',
productList: [],
searchParam: {
pageNum: 1,
pageSize: 6
}
};
},
onLoad(options) {
this.loadData();
},
//下拉刷新
onPullDownRefresh() {
this.loadData('refresh');
},
//加载更多
onReachBottom() {
this.searchParam.pageNum++;
this.loadData();
},
// #ifndef MP
onNavigationBarButtonTap(e) {
const index = e.index;
let thisObj = this;
if (index === 0) {
uni.showModal({
title: '提示',
content: '是否要清空所有浏览记录?',
success: function (res) {
if (res.confirm) {
clearReadHistory().then(response=>{
thisObj.loadData('refresh');
});
}
}
});
}
},
// #endif
filters: {
formatDateTime(time) {
if (time == null || time === '') {
return 'N/A';
}
let date = new Date(time);
return formatDate(date, 'yyyy-MM-dd hh:mm:ss')
},
},
methods: {
//加载商品 ,带下拉刷新和上滑加载
async loadData(type = 'add', loading) {
//没有更多直接返回
if (type === 'add') {
if (this.loadingType === 'nomore') {
return;
}
this.loadingType = 'loading';
} else {
this.loadingType = 'more'
}
if (type === 'refresh') {
this.searchParam.pageNum = 1;
this.productList = [];
}
fetchReadHistoryList(this.searchParam).then(response => {
let dataList = response.data.list;
if (dataList.length === 0) {
//没有更多了
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
if (dataList.length < this.searchParam.pageSize) {
this.loadingType = 'nomore';
this.searchParam.pageNum--;
} else {
this.loadingType = 'more';
}
this.productList = this.productList.concat(dataList);
}
if (type === 'refresh') {
if (loading == 1) {
uni.hideLoading()
} else {
uni.stopPullDownRefresh();
}
}
});
},
//详情
navToDetailPage(item) {
let id = item.productId;
uni.navigateTo({
url: `/pages/product/product?id=${id}`
})
},
stopPrevent() {}
},
}
</script>
<style lang="scss">
page,
.content {
background: $page-color-base;
}
.hot-section {
display: flex;
flex-wrap: wrap;
padding: 0 30upx;
margin-top: 16upx;
background: #fff;
.guess-item {
display: flex;
flex-direction: row;
width: 100%;
padding-bottom: 40upx;
}
.image-wrapper {
width: 30%;
height: 250upx;
border-radius: 3px;
overflow: hidden;
image {
width: 100%;
height: 100%;
opacity: 1;
}
}
.title {
font-size: $font-lg;
color: $font-color-dark;
line-height: 80upx;
}
.title2 {
font-size: $font-sm;
color: $font-color-light;
line-height: 40upx;
height: 80upx;
overflow: hidden;
text-overflow: ellipsis;
display: block;
}
.price {
font-size: $font-lg;
color: $uni-color-primary;
line-height: 80upx;
}
.txt {
width: 70%;
display: flex;
flex-direction: column;
padding-left: 40upx;
}
.hor-txt{
display: flex;
justify-content: space-between;
}
.time {
font-size: $font-sm;
color: $font-color-dark;
line-height: 80upx;
}
}
</style>
<template>
<view class="container">
<view class="user-section">
<image class="bg" src="/static/user-bg.jpg"></image>
<view class="user-info-box">
<view class="portrait-box">
<image class="portrait" :src="userInfo.icon || '/static/missing-face.png'"></image>
</view>
<view class="info-box">
<text class="username">{{userInfo.nickname || '游客'}}</text>
</view>
</view>
<view class="vip-card-box">
<image class="card-bg" src="/static/vip-card-bg.png" mode=""></image>
<view class="b-btn">
立即开通
</view>
<view class="tit">
<text class="yticon icon-iLinkapp-"></text>
黄金会员
</view>
<text class="e-m">mall移动端商城</text>
<text class="e-b">黄金及以上会员可享有会员价优惠商品。</text>
</view>
</view>
<view
class="cover-container"
:style="[{
transform: coverTransform,
transition: coverTransition
}]"
@touchstart="coverTouchstart"
@touchmove="coverTouchmove"
@touchend="coverTouchend"
>
<image class="arc" src="/static/arc.png"></image>
<view class="tj-sction">
<view class="tj-item">
<text class="num">{{userInfo.integration || '暂无'}}</text>
<text>积分</text>
</view>
<view class="tj-item">
<text class="num">{{userInfo.growth || '暂无'}}</text>
<text>成长值</text>
</view>
<view class="tj-item" @click="navTo('/pages/coupon/couponList')">
<text class="num">{{couponCount || '暂无'}}</text>
<text>优惠券</text>
</view>
</view>
<!-- 订单 -->
<view class="order-section">
<view class="order-item" @click="navTo('/pages/order/order?state=0')" hover-class="common-hover" :hover-stay-time="50">
<text class="yticon icon-shouye"></text>
<text>全部订单</text>
</view>
<view class="order-item" @click="navTo('/pages/order/order?state=1')" hover-class="common-hover" :hover-stay-time="50">
<text class="yticon icon-daifukuan"></text>
<text>待付款</text>
</view>
<view class="order-item" @click="navTo('/pages/order/order?state=2')" hover-class="common-hover" :hover-stay-time="50">
<text class="yticon icon-yishouhuo"></text>
<text>待收货</text>
</view>
<view class="order-item" hover-class="common-hover" :hover-stay-time="50">
<text class="yticon icon-shouhoutuikuan"></text>
<text>退款/售后</text>
</view>
</view>
<!-- 浏览历史 -->
<view class="history-section icon">
<list-cell icon="icon-dizhi" iconColor="#5fcda2" title="地址管理" @eventClick="navTo('/pages/address/address')"></list-cell>
<list-cell icon="icon-lishijilu" iconColor="#e07472" title="我的足迹" @eventClick="navTo('/pages/user/readHistory')"></list-cell>
<list-cell icon="icon-shoucang" iconColor="#5fcda2" title="我的关注" @eventClick="navTo('/pages/user/brandAttention')"></list-cell>
<list-cell icon="icon-shoucang_xuanzhongzhuangtai" iconColor="#54b4ef" title="我的收藏" @eventClick="navTo('/pages/user/productCollection')"></list-cell>
<list-cell icon="icon-pingjia" iconColor="#ee883b" title="我的评价"></list-cell>
<list-cell icon="icon-shezhi1" iconColor="#e07472" title="设置" border="" @eventClick="navTo('/pages/set/set')"></list-cell>
</view>
</view>
</view>
</template>
<script>
import listCell from '@/components/mix-list-cell';
import {
fetchMemberCouponList
} from '@/api/coupon.js';
import {
mapState
} from 'vuex';
let startY = 0, moveY = 0, pageAtTop = true;
export default {
components: {
listCell
},
data(){
return {
coverTransform: 'translateY(0px)',
coverTransition: '0s',
moving: false,
couponCount:null
}
},
onLoad(){
},
onShow(){
if(this.hasLogin){
fetchMemberCouponList(0).then(response=>{
if(response.data!=null&&response.data.length>0){
this.couponCount = response.data.length;
}
});
}else{
this.couponCount=null;
}
},
// #ifndef MP
onNavigationBarButtonTap(e) {
const index = e.index;
if (index === 0) {
this.navTo('/pages/set/set');
}else if(index === 1){
// #ifdef APP-PLUS
const pages = getCurrentPages();
const page = pages[pages.length - 1];
const currentWebview = page.$getAppWebview();
currentWebview.hideTitleNViewButtonRedDot({
index
});
// #endif
uni.navigateTo({
url: '/pages/notice/notice'
})
}
},
// #endif
computed: {
...mapState(['hasLogin','userInfo'])
},
methods: {
/**
* 统一跳转接口,拦截未登录路由
* navigator标签现在默认没有转场动画,所以用view
*/
navTo(url){
if(!this.hasLogin){
url = '/pages/public/login';
}
uni.navigateTo({
url
})
},
/**
* 会员卡下拉和回弹
* 1.关闭bounce避免ios端下拉冲突
* 2.由于touchmove事件的缺陷(以前做小程序就遇到,比如20跳到40,h5反而好很多),下拉的时候会有掉帧的感觉
* transition设置0.1秒延迟,让css来过渡这段空窗期
* 3.回弹效果可修改曲线值来调整效果,推荐一个好用的bezier生成工具 http://cubic-bezier.com/
*/
coverTouchstart(e){
if(pageAtTop === false){
return;
}
this.coverTransition = 'transform .1s linear';
startY = e.touches[0].clientY;
},
coverTouchmove(e){
moveY = e.touches[0].clientY;
let moveDistance = moveY - startY;
if(moveDistance < 0){
this.moving = false;
return;
}
this.moving = true;
if(moveDistance >= 80 && moveDistance < 100){
moveDistance = 80;
}
if(moveDistance > 0 && moveDistance <= 80){
this.coverTransform = `translateY(${moveDistance}px)`;
}
},
coverTouchend(){
if(this.moving === false){
return;
}
this.moving = false;
this.coverTransition = 'transform 0.3s cubic-bezier(.21,1.93,.53,.64)';
this.coverTransform = 'translateY(0px)';
}
}
}
</script>
<style lang='scss'>
%flex-center {
display:flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
%section {
display:flex;
justify-content: space-around;
align-content: center;
background: #fff;
border-radius: 10upx;
}
.user-section{
height: 520upx;
padding: 100upx 30upx 0;
position:relative;
.bg{
position:absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
filter: blur(1px);
opacity: .7;
}
}
.user-info-box{
height: 180upx;
display:flex;
align-items:center;
position:relative;
z-index: 1;
.portrait{
width: 130upx;
height: 130upx;
border:5upx solid #fff;
border-radius: 50%;
}
.username{
font-size: $font-lg + 6upx;
color: $font-color-dark;
margin-left: 20upx;
}
}
.vip-card-box{
display:flex;
flex-direction: column;
color: #f7d680;
height: 240upx;
background: linear-gradient(left, rgba(0,0,0,.7), rgba(0,0,0,.8));
border-radius: 16upx 16upx 0 0;
overflow: hidden;
position: relative;
padding: 20upx 24upx;
.card-bg{
position:absolute;
top: 20upx;
right: 0;
width: 380upx;
height: 260upx;
}
.b-btn{
position: absolute;
right: 20upx;
top: 16upx;
width: 132upx;
height: 40upx;
text-align: center;
line-height: 40upx;
font-size: 22upx;
color: #36343c;
border-radius: 20px;
background: linear-gradient(left, #f9e6af, #ffd465);
z-index: 1;
}
.tit{
font-size: $font-base+2upx;
color: #f7d680;
margin-bottom: 28upx;
.yticon{
color: #f6e5a3;
margin-right: 16upx;
}
}
.e-b{
font-size: $font-sm;
color: #d8cba9;
margin-top: 10upx;
}
}
.cover-container{
background: $page-color-base;
margin-top: -150upx;
padding: 0 30upx;
position:relative;
background: #f5f5f5;
padding-bottom: 20upx;
.arc{
position:absolute;
left: 0;
top: -34upx;
width: 100%;
height: 36upx;
}
}
.tj-sction{
@extend %section;
.tj-item{
@extend %flex-center;
flex-direction: column;
height: 140upx;
font-size: $font-sm;
color: #75787d;
}
.num{
font-size: $font-lg;
color: $font-color-dark;
margin-bottom: 8upx;
}
}
.order-section{
@extend %section;
padding: 28upx 0;
margin-top: 20upx;
.order-item{
@extend %flex-center;
width: 120upx;
height: 120upx;
border-radius: 10upx;
font-size: $font-sm;
color: $font-color-dark;
}
.yticon{
font-size: 48upx;
margin-bottom: 18upx;
color: #fa436a;
}
.icon-shouhoutuikuan{
font-size:44upx;
}
}
.history-section{
padding: 30upx 0 0;
margin-top: 20upx;
background: #fff;
border-radius:10upx;
.sec-header{
display:flex;
align-items: center;
font-size: $font-base;
color: $font-color-dark;
line-height: 40upx;
margin-left: 30upx;
.yticon{
font-size: 44upx;
color: #5eba8f;
margin-right: 16upx;
line-height: 40upx;
}
}
.h-list{
white-space: nowrap;
padding: 30upx 30upx 0;
image{
display:inline-block;
width: 160upx;
height: 160upx;
margin-right: 20upx;
border-radius: 10upx;
}
}
}
</style>
\ No newline at end of file
<template>
<view>
<view class="user-section">
<image class="bg" src="/static/user-bg.jpg"></image>
<text class="bg-upload-btn yticon icon-paizhao"></text>
<view class="portrait-box">
<image class="portrait" :src="userInfo.portrait || '/static/missing-face.png'"></image>
<text class="pt-upload-btn yticon icon-paizhao"></text>
</view>
</view>
</view>
</template>
<script>
import {
mapState,
mapMutations
} from 'vuex';
export default {
data() {
return {
};
},
computed:{
...mapState(['userInfo']),
}
}
</script>
<style lang="scss">
page{
background: $page-color-base;
}
.user-section{
display:flex;
align-items:center;
justify-content: center;
height: 460upx;
padding: 40upx 30upx 0;
position:relative;
.bg{
position:absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
filter: blur(1px);
opacity: .7;
}
.portrait-box{
width: 200upx;
height: 200upx;
border:6upx solid #fff;
border-radius: 50%;
position:relative;
z-index: 2;
}
.portrait{
position: relative;
width: 100%;
height: 100%;
border-radius: 50%;
}
.yticon{
position:absolute;
line-height: 1;
z-index: 5;
font-size: 48upx;
color: #fff;
padding: 4upx 6upx;
border-radius: 6upx;
background: rgba(0,0,0,.4);
}
.pt-upload-btn{
right: 0;
bottom: 10upx;
}
.bg-upload-btn{
right: 20upx;
bottom: 16upx;
}
}
</style>
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
hasLogin: false,
userInfo: {},
},
mutations: {
login(state, provider) {
state.hasLogin = true;
state.userInfo = provider;
uni.setStorage({//缓存用户登陆状态
key: 'userInfo',
data: provider
})
console.log(state.userInfo);
},
logout(state) {
state.hasLogin = false;
state.userInfo = {};
uni.removeStorage({
key: 'userInfo'
});
uni.removeStorage({
key: 'token'
})
}
},
actions: {
}
})
export default store
/* 页面左右间距 */
$page-row-spacing: 30upx;
$page-color-base: #f8f8f8;
$page-color-light: #f8f6fc;
$base-color: #fa436a;
/* 文字尺寸 */
$font-sm: 24upx;
$font-base: 28upx;
$font-lg: 32upx;
/*文字颜色*/
$font-color-dark: #303133;
$font-color-base: #606266;
$font-color-light: #909399;
$font-color-disabled: #C0C4CC;
$font-color-spec: #4399fc;
/* 边框颜色 */
$border-color-dark: #DCDFE6;
$border-color-base: #E4E7ED;
$border-color-light: #EBEEF5;
/* 图片加载中颜色 */
$image-bg-color: #eee;
/* 行为相关颜色 */
$uni-color-primary:#fa436a;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
// appConfig.js
//配置API请求的基础路径
export const API_BASE_URL = 'https://portal-api.macrozheng.com'
//是否启用支付宝支付
export const USE_ALIPAY = false
// date.js
export function formatDate(date, fmt) {
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length));
}
let o = {
'M+': date.getMonth() + 1,
'd+': date.getDate(),
'h+': date.getHours(),
'm+': date.getMinutes(),
's+': date.getSeconds()
};
for (let k in o) {
if (new RegExp(`(${k})`).test(fmt)) {
let str = o[k] + '';
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? str : padLeftZero(str));
}
}
return fmt;
}
function padLeftZero(str) {
return ('00' + str).substr(str.length);
}
export function str2Date(dateStr, separator) {
if (!separator) {
separator = "-";
}
let dateArr = dateStr.split(separator);
let year = parseInt(dateArr[0]);
let month;
//处理月份为04这样的情况
if (dateArr[1].indexOf("0") == 0) {
month = parseInt(dateArr[1].substring(1));
} else {
month = parseInt(dateArr[1]);
}
let day = parseInt(dateArr[2]);
let date = new Date(year, month - 1, day);
return date;
}
import Request from '@/js_sdk/luch-request/request.js'
import { API_BASE_URL} from '@/utils/appConfig.js';
const http = new Request()
http.setConfig((config) => { /* 设置全局配置 */
config.baseUrl = API_BASE_URL /* 根域名不同 */
config.header = {
...config.header
}
return config
})
/**
* 自定义验证器,如果返回true 则进入响应拦截器的响应成功函数(resolve),否则进入响应拦截器的响应错误函数(reject)
* @param { Number } statusCode - 请求响应体statusCode(只读)
* @return { Boolean } 如果为true,则 resolve, 否则 reject
*/
http.validateStatus = (statusCode) => {
return statusCode === 200
}
http.interceptor.request((config, cancel) => { /* 请求之前拦截器 */
const token = uni.getStorageSync('token');
if(token){
config.header = {
'Authorization':token,
...config.header
}
}else{
config.header = {
...config.header
}
}
/*
if (!token) { // 如果token不存在,调用cancel 会取消本次请求,但是该函数的catch() 仍会执行
cancel('token 不存在') // 接收一个参数,会传给catch((err) => {}) err.errMsg === 'token 不存在'
}
*/
return config
})
http.interceptor.response((response) => { /* 请求之后拦截器 */
const res = response.data;
if (res.code !== 200) {
//提示错误信息
uni.showToast({
title:res.message,
duration:1500
})
//401未登录处理
if (res.code === 401) {
uni.showModal({
title: '提示',
content: '你已被登出,可以取消继续留在该页面,或者重新登录',
confirmText:'重新登录',
cancelText:'取消',
success: function(res) {
if (res.confirm) {
uni.navigateTo({
url: '/pages/public/login'
})
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}
return Promise.reject(response);
} else {
return response.data;
}
}, (response) => {
//提示错误信息
console.log('response error', response);
uni.showToast({
title:response.errMsg,
duration:1500
})
return Promise.reject(response);
})
export function request (options = {}) {
return http.request(options);
}
export default request
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论