仓库迁移

msb_beta
ch 5 years ago
parent 61943ec0a2
commit 83d9986789

5
.gitignore vendored

@ -0,0 +1,5 @@
.hbuilderx/
.history/
unpackage/
node_moudel/
package-lock.json

@ -0,0 +1,21 @@
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*每个页面公共css */
</style>
<style lang="scss">
/* 注意要写在第一行,同时给style标签加入lang="scss"属性 */
@import "uview-ui/index.scss";
</style>

@ -1,92 +1,76 @@
# shop-app
马士兵严选APP / h5
## Getting started
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
## Add your files
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
<!--
* @Author: ch
* @Date: 2022-03-17 11:30:06
* @LastEditors: ch
* @LastEditTime: 2022-03-22 18:07:54
* @Description: file content
-->
# shopping-app
严选移动端
## 公共方法utils
- 公共方法统一放置utils文件夹内,可以按分类建方法文件 如:验证类verify.js 请求类request.js;
- 所有公共方法采用大驼峰命名法
- 所有的方法都从index.js输出,引入时统一引入index,不允许直接引入方法文件
- 所有方法文件如果导出的是多个方法,不允许在定义方法时导出,必须在文件底部一一导出,并附上方法简单的注释
``` js
// 正确
import {Req, IsPhone} from '@/common/utils';
// 错误
import {Req} from '@/common/utils/request';
import {IsPhone} from '@/common/utils/utils';
// 正确
const IsPhone = (str) => {....}
const IsEmail = (str) => {....}
export {
// 判断手机号
IsPhone,
// 判断邮箱
IsEmail
}
// 错误
export const IsPhone = (str) => {....}
export const IsEmail = (str) => {....}
```
cd existing_repo
git remote add origin http://internel-git.mashibing.cn/yanxuan-frontend/shop-app.git
git branch -M main
git push -uf origin main
```
## Integrate with your tools
- [ ] [Set up project integrations](http://internel-git.mashibing.cn/yanxuan-frontend/shop-app/-/settings/integrations)
## Collaborate with your team
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
## Test and Deploy
Use the built-in continuous integration in GitLab.
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
***
# Editing this README
## 组件
- 请务必使用easycom模式引入第三方组件
- 根目录的components 只放置真正的组件,某个页面的业务模块应该在pages的相应目录下新建components目录放置
- 所有的自定义组件文件名以大驼峰命名,且在templet中使用也用大驼峰形式使用
## 请求
- 所有请求方法命名以(Api+请求类型+具体方) 法命名
- 所有请求使用ToAsyncAwait 包裹
- 不允许使用try catch 和 then 处理返回结果
``` js
// 使用示例
// xxapi.js
import {ToAsyncAwait, ReqestTk} from '@/common/utils'
const ApiGetUserInfo = (parapms) => ToAsyncAwait(ReqestTk.get('xxxxUrl',{params}));
exprot {
// 获取用户信息
ApiGetUserInfo
}
// user.vue
improt {ApiGetUserInfo} from '@/common/api/xxapi.js';
const getUserInfo = async () =>{
const {error, result} = await ApiGetUserInfo();
if(error){
alert(error);
return false;
}
app.userInfo = result;
}
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
## Suggestions for a good README
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
## Name
Choose a self-explaining name for your project.
## Description
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
## Badges
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
## Visuals
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
## Installation
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
## Usage
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
## Support
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
## Roadmap
If you have ideas for releases in the future, it is a good idea to list them in the README.
## Contributing
State if you are open to contributions and what your requirements are for accepting them.
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
## Authors and acknowledgment
Show your appreciation to those who have contributed to the project.
## License
For open source projects, say how it is licensed.
## Project status
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
```
## 图片
- 按pages目录结果放置图片
- 有公共使用的图片请放到static/common文件夹内

@ -0,0 +1,7 @@
/*
* @Author: ch
* @Date: 2022-03-17 16:38:39
* @LastEditors: ch
* @LastEditTime: 2022-03-17 16:38:39
* @Description: file content
*/

@ -0,0 +1,7 @@
/*
* @Author: ch
* @Date: 2022-03-17 16:38:26
* @LastEditors: ch
* @LastEditTime: 2022-03-17 16:38:26
* @Description: file content
*/

@ -0,0 +1,19 @@
/*
* @Author: ch
* @Date: 2022-03-17 16:37:45
* @LastEditors: ch
* @LastEditTime: 2022-03-22 17:25:29
* @Description: file content
*/
import {Request, ToAsyncAwait} from '@/common/utils';
/**
* 获取测试信息
* @param {*} params
*/
export const ApiGetBannerData = (params) =>
ToAsyncAwait(Request.get('/edu-course/carousel/app',params));

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:优惠券适用范围
* ApplyRangeEnum
*/
export default new Enum([
{ key: 'ALL', name: '全部商品', value: 10 },
{ key: 'SOME_GOODS', name: '指定商品', value: 20 }
])

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:优惠券类型
* CouponTypeEnum
*/
export default new Enum([
{ key: 'FULL_DISCOUNT', name: '满减券', value: 10 },
{ key: 'DISCOUNT', name: '折扣券', value: 20 }
])

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:优惠券到期类型
* ExpireTypeEnum
*/
export default new Enum([
{ key: 'RECEIVE', name: '领取后', value: 10 },
{ key: 'FIXED_TIME', name: '固定时间', value: 20 }
])

@ -0,0 +1,5 @@
import ApplyRangeEnum from './ApplyRange'
import ExpireTypeEnum from './ExpireType'
import CouponTypeEnum from './CouponType'
export { ApplyRangeEnum, CouponTypeEnum, ExpireTypeEnum }

@ -0,0 +1,85 @@
/**
* 枚举类
* Enum.IMAGE.name => "图片"
* Enum.getNameByKey('IMAGE') => "图片"
* Enum.getValueByKey('IMAGE') => 10
* Enum.getNameByValue(10) => "图片"
* Enum.getData() => [{key: "IMAGE", name: "图片", value: 10}]
*/
class Enum {
constructor (param) {
const keyArr = []
const valueArr = []
if (!Array.isArray(param)) {
throw new Error('param is not an array!')
}
param.map(element => {
if (!element.key || !element.name) {
return
}
// 保存key值组成的数组,方便A.getName(name)类型的调用
keyArr.push(element.key)
valueArr.push(element.value)
// 根据key生成不同属性值,以便A.B.name类型的调用
this[element.key] = element
if (element.key !== element.value) {
this[element.value] = element
}
})
// 保存源数组
this.data = param
this.keyArr = keyArr
this.valueArr = valueArr
// 防止被修改
// Object.freeze(this)
}
// 根据key得到对象
keyOf (key) {
return this.data[this.keyArr.indexOf(key)]
}
// 根据key得到对象
valueOf (key) {
return this.data[this.valueArr.indexOf(key)]
}
// 根据key获取name值
getNameByKey (key) {
const prop = this.keyOf(key)
if (!prop) {
throw new Error('No enum constant' + key)
}
return prop.name
}
// 根据value获取name值
getNameByValue (value) {
const prop = this.valueOf(value)
if (!prop) {
throw new Error('No enum constant' + value)
}
return prop.name
}
// 根据key获取value值
getValueByKey (key) {
const prop = this.keyOf(key)
if (!prop) {
throw new Error('No enum constant' + key)
}
return prop.key
}
// 返回源数组
getData () {
return this.data
}
}
export default Enum

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:订单发货状态
* DeliveryStatusEnum
*/
export default new Enum([
{ key: 'NOT_DELIVERED', name: '未发货', value: 10 },
{ key: 'DELIVERED', name: '已发货', value: 20 }
])

@ -0,0 +1,9 @@
import Enum from '../enum'
/**
* 枚举类:配送方式
* DeliveryTypeEnum
*/
export default new Enum([
{ key: 'EXPRESS', name: '快递配送', value: 10 }
])

@ -0,0 +1,11 @@
import Enum from '../enum'
/**
* 枚举类:订单来源
* OrderSourceEnum
*/
export default new Enum([
{ key: 'MASTER', name: '普通订单', value: 10 },
{ key: 'BARGAIN', name: '砍价订单', value: 20 },
{ key: 'SHARP', name: '秒杀订单', value: 30 }
])

@ -0,0 +1,12 @@
import Enum from '../enum'
/**
* 枚举类:订单状态
* OrderStatusEnum
*/
export default new Enum([
{ key: 'NORMAL', name: '进行中', value: 10 },
{ key: 'CANCELLED', name: '已取消', value: 20 },
{ key: 'APPLY_CANCEL', name: '待取消', value: 21 },
{ key: 'COMPLETED', name: '已完成', value: 30 }
])

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:订单支付状态
* PayStatusEnum
*/
export default new Enum([
{ key: 'PENDING', name: '待支付', value: 10 },
{ key: 'SUCCESS', name: '已支付', value: 20 }
])

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:订单支付方式
* PayTypeEnum
*/
export default new Enum([
{ key: 'BALANCE', name: '余额支付', value: 10 },
{ key: 'WECHAT', name: '微信支付', value: 20 }
])

@ -0,0 +1,10 @@
import Enum from '../enum'
/**
* 枚举类:订单收货状态
* ReceiptStatusEnum
*/
export default new Enum([
{ key: 'NOT_RECEIVED', name: '未收货', value: 10 },
{ key: 'RECEIVED', name: '已收货', value: 20 }
])

@ -0,0 +1,17 @@
import DeliveryStatusEnum from './DeliveryStatus'
import DeliveryTypeEnum from './DeliveryType'
import OrderSourceEnum from './OrderSource'
import OrderStatusEnum from './OrderStatus'
import PayStatusEnum from './PayStatus'
import PayTypeEnum from './PayType'
import ReceiptStatusEnum from './ReceiptStatus'
export {
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderSourceEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum
}

@ -0,0 +1,11 @@
import Enum from '../../enum'
/**
* 枚举类:商家审核状态
* AuditStatusEnum
*/
export default new Enum([
{ key: 'WAIT', name: '待审核', value: 0 },
{ key: 'REVIEWED', name: '已同意', value: 10 },
{ key: 'REJECTED', name: '已拒绝', value: 20 }
])

@ -0,0 +1,12 @@
import Enum from '../../enum'
/**
* 枚举类:售后单状态
* RefundStatusEnum
*/
export default new Enum([
{ key: 'NORMAL', name: '进行中', value: 0 },
{ key: 'REJECTED', name: '已拒绝', value: 10 },
{ key: 'COMPLETED', name: '已完成', value: 20 },
{ key: 'CANCELLED', name: '已取消', value: 30 }
])

@ -0,0 +1,10 @@
import Enum from '../../enum'
/**
* 枚举类:售后类型
* RefundTypeEnum
*/
export default new Enum([
{ key: 'RETURN', name: '退货退款', value: 10 },
{ key: 'EXCHANGE', name: '换货', value: 20 }
])

@ -0,0 +1,9 @@
import AuditStatusEnum from './AuditStatus'
import RefundStatusEnum from './RefundStatus'
import RefundTypeEnum from './RefundType'
export {
AuditStatusEnum,
RefundStatusEnum,
RefundTypeEnum
}

@ -0,0 +1,27 @@
import Enum from '../enum'
/**
* 枚举类:设置项索引
* SettingKeyEnum
*/
export default new Enum([{
key: 'REGISTER',
name: '账户注册设置',
value: 'register'
},
{
key: 'PAGE_CATEGORY_TEMPLATE',
name: '分类页模板',
value: 'page_category_template'
},
{
key: 'POINTS',
name: '积分设置',
value: 'points'
},
{
key: 'RECHARGE',
name: '充值设置',
value: 'recharge'
}
])

@ -0,0 +1,11 @@
import Enum from '../../../enum'
/**
* 枚举类:地址类型
* PageCategoryStyleEnum
*/
export default new Enum([
{ key: 'ONE_LEVEL_BIG', name: '一级分类[大图]', value: 10 },
{ key: 'ONE_LEVEL_SMALL', name: '一级分类[小图]', value: 11 },
{ key: 'TWO_LEVEL', name: '二级分类', value: 20 }
])

@ -0,0 +1,3 @@
import PageCategoryStyleEnum from './Style'
export { PageCategoryStyleEnum }

@ -0,0 +1,89 @@
/*
* @Author: ch
* @Date: 2022-03-17 16:36:59
* @LastEditors: ch
* @LastEditTime: 2022-03-18 16:05:47
* @Description: 针对uniapp request请求做了一次封装,使用思维参考axios
*
*
* 方法
* method(option) 自定义请求,同uni.request
* get(url, params, header) url 请求地址 params 请求参数
* header 请求头会针对当前请求头设置特定的请求头。传了此参数request拦截器会失效
* post(url, data, header) 同上
* put(url, data, header) 同上
* delete(url, data, header) 同上
* use(hookName, callback) 注入hook拦截器 hookName 拦截器名(request/response/error) callback拦截器具体见拦截器说明
*
* 属性
* baseUrl 请求地址前缀
*
* 拦截器
* request 请求前拦截,在这可统一设置请求头,请求体等参数。uni.request的第一个参数option都可以重置
* success 请求成功结果拦截
* error 请求错误拦截
*
* 示例
* const myReq = new MsbUniRequest();
* myReq.baseUrl = 'xxxx'
* myReq.use('request', (option)=>{
* // option 返回请求配置
* .....这里可以对option做一系列操作
* return option //最后返回一个正确的请求配置
* })
* myReq.use('success', (res)=>{
* //res 返回请求结果
* let newRes = ..... //这里可以对请求结果做统一处理
* return newRes
* })
*
* myReq.use('error', (error)=>{
* //error 返回错误结果
* let newError = ..... //这里可以对请求错误做统一处理
* return newError
* })
*/
class MsbUniRequest {
constructor (option){
this.baseUrl = '';
this.hook = {
request : null,
success : null,
error : null
}
}
method(option){
option.url = this.baseUrl + option.url;
if(this.hook.request && !option.header){
option = this.hook.request(option);
}
if(!option){
throw new Error('没有请求配置,或是request拦截未做return');
}
return new Promise((resolve, reject)=>{
uni.request(option).then(response => {
resolve(this.hook.success ? this.hook.success(response[1]) : response);
}).catch(error => {
reject(this.hook.error ? this.hook.error(error) : error);
});
});
}
use(hookName, cb){
this.hook[hookName] = cb;
}
get(url, data, header){
return this.method({method : 'get', url, data, header});
}
post(url, data, header){
return this.method({method : 'post', url, data, header});
}
put(url, data, header){
return this.method({method : 'put', url, data, header});
}
delete(url, data){
return this.method({method : 'delete', url, data, header});
}
}
export default MsbUniRequest;

@ -0,0 +1,26 @@
/*
* @Author: ch
* @Date: 2022-03-18 11:11:05
* @LastEditors: ch
* @LastEditTime: 2022-03-18 11:11:05
* @Description: file content
*/
import {RouterMount,createRouter} from 'uni-simple-router';
const router = createRouter({
platform: process.env.VUE_APP_PLATFORM,
routes: [...ROUTES]
});
//全局路由前置守卫
router.beforeEach((to, from, next) => {
next();
});
// 全局路由后置守卫
router.afterEach((to, from) => {
console.log('跳转结束')
})
export {
router,
RouterMount
}

@ -0,0 +1,22 @@
/*
* @Author: ch
* @Date: 2022-03-22 18:28:52
* @LastEditors: ch
* @LastEditTime: 2022-03-24 14:20:14
* @Description: file content
*/
import Vue from 'vue'
import Vuex from 'vuex';
export default new Vuex.Store({
state : {
},
mutations:{
},
getters:{
}
})

@ -0,0 +1,14 @@
/*
* @Author: ch
* @Date: 2022-03-22 16:52:28
* @LastEditors: ch
* @LastEditTime: 2022-03-22 17:26:44
* @Description: 所有工具类统一在这输出
*/
import * as util from './utils';
import * as requset from './requset';
export * from './utils';
export * from './requset';
export default { ...util, requset}

@ -0,0 +1,56 @@
/*
* @Author: ch
* @Date: 2022-03-17 17:42:32
* @LastEditors: ch
* @LastEditTime: 2022-03-22 17:46:43
* @Description: 项目接口请求统一处理器,返回一个需要token和不需要token的请求封装方法
*/
const { default: MsbUniRequest } = require("../plugins/msbUniRequest");
const ENV = 'test';
const BASE_URL = {
'test' : 'http://39.103.236.147/api',
'release' : '',
'prod' : ''
};
const successIntercept = (response) =>{
if(response.statusCode === 200){
const result = response.data;
if(result.code === 200){
return result.data;
}else{
return result;
}
}
return response.errMsg;
}
const errorIntercept = (error) =>{
}
// 不需要token的接口封装
const Request = new MsbUniRequest();
Request.baseUrl = BASE_URL[ENV];
Request.use('request', (option) => {
return option;
})
Request.use('success', successIntercept);
Request.use('error', errorIntercept);
// 需要token的接口
const RequestTk = new MsbUniRequest();
RequestTk.baseUrl = BASE_URL[ENV];
Request.use('request', (option) => {
return option;
})
Request.use('success', successIntercept);
Request.use('error', errorIntercept);
export {
// 不需要Token的接口请求用这个实例
Request,
// 需要token的请求
RequestTk
}

@ -0,0 +1,22 @@
/*
* @Author: ch
* @Date: 2022-03-23 14:07:07
* @LastEditors: ch
* @LastEditTime: 2022-03-23 14:14:37
* @Description: file content
*/
import { ToAsyncAwait } from '@/common/utils';
const getStorageSync = (key )=> ToAsyncAwait(async ()=>{
return Promise((resolve, reject)=>{
try{
cosnt val = await uni.getStorageSync(key);
resolve(val)
}catch (e){
reject(e)
}
})
})

@ -0,0 +1,91 @@
/*
* @Author: ch
* @Date: 2022-03-17 19:15:10
* @LastEditors: ch
* @LastEditTime: 2022-03-22 17:30:38
* @Description: 一些无法归类的公共方法容器
*/
/**
* 处理async await 标识结果处理
*
* @param {*} promise promise对象
* @param {*} isFromatResult 是否处理结果
* @returns {error,result} error有错为错误对象,没错为null , result正确的返回结果
* isFromatResult 为false时直接返回promise对象,不做任何处理
*
*/
const ToAsyncAwait = (promise, isFromatResult = true) => {
if(!isFromatResult){
return promise;
}else{
return promise.then((res) => ({error:null,result:res})).catch((err) => ({error:err,result:null}));
}
}
/** 防抖函数
* 首次运行时把定时器赋值给一个变量, 第二次执行时,
* 如果间隔没超过定时器设定的时间则会清除掉定时器,
* 重新设定定时器, 依次反复, 当我们停止下来时,
* 没有执行清除定时器, 超过一定时间后触发回调函数。
*/
const Debounce = (fn, delay) => {
let timer
return function() {
const that = this
const _args = arguments // 存一下传入的参数
if (timer) {
clearTimeout(timer)
}
timer = setTimeout(function() {
fn.apply(that, _args)
}, delay)
}
}
/**
* 匹配phone
*/
const IsPhone = (str) => /^(1[3-9]\d{9})$/.test(str);
/**
* 判断数值类型,包括整数和浮点数
*/
const IsNumber = (str) => (isDouble(str) || isInteger(str)) ? true : false;
/**
* 匹配integer
*/
const IsInteger = (str) => {
if (str == null || str == "") return false
var result = str.match(/^[-\+]?\d+$/)
if (result == null) return false
return true
}
/**
* 匹配double或float
*/
const IsDouble = (str) => {
if (str == null || str == "") return false
var result = str.match(/^[-\+]?\d+(\.\d+)?$/)
if (result == null) return false
return true
}
// 工具类的文件需要把文件提供的工具类统一放最下方做一个统一输出
export {
// async await 标识结果处理
ToAsyncAwait,
// 防抖函数
Debounce,
// 判断是否为手机号
IsPhone,
// 判断是否为数字
IsNumber,
// 判断是否为整数
IsInteger,
// 判断是否double或float
IsDouble
}

@ -0,0 +1,63 @@
<!--
* @Author: ch
* @Date: 2022-03-20 13:11:55
* @LastEditors: ch
* @LastEditTime: 2022-03-24 16:38:41
* @Description: file content
-->
<template>
<view class="empty-content">
<view class="empty-icon">
<slot name="icon">
<!-- <image class="image" src="@/static/empty.png" mode="widthFix"></image> -->
</slot>
</view>
<view class="tips">{{ tips }}</view>
<slot name="btn"></slot>
</view>
</template>
<script>
export default {
/**
* 组件的属性列表
* 用于组件自定义设置
*/
props: {
// 提示的问题
tips: {
type: String,
default: '亲,暂无相关数据'
}
},
data() {
return {}
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.empty-content {
box-sizing: border-box;
width: 100%;
padding: 50rpx;
text-align: center;
.tips {
font-size: 26rpx;
color: #999;
margin: 30rpx 0 50rpx;
}
.empty-icon .image {
width: 280rpx;
}
}
</style>

@ -0,0 +1,169 @@
<!--
* @Author: ch
* @Date: 2022-03-20 16:45:27
* @LastEditors: ch
* @LastEditTime: 2022-03-21 17:36:52
* @Description: file content
-->
<template>
<view class="goods">
<view class="goods--column">
<view class="goods-item" @click="$Router.push('/pages/goods/detail')">
<image class="goods-item--img" src="@/static/testImg/2.png" mode="widthFix"></image>
<view class="goods-item--title"><text>JAVA语言程序员必备的技术书籍</text></view>
<view class="goods-item--pirce-box">
<view>
<text class="goods-item--pirce">¥29</text>
<text class="goods-item--original-pirce">¥50</text>
</view>
<view>
<image class="goods-item--icon" src='@/static/index/bz.png'></image>
</view>
</view>
<view class="goods-item--activity">
<text class="goods-item--activity-title">秒杀</text>
<text class="goods-item--activity-desc">正在抢购中</text>
</view>
</view>
<view class="goods-item">
<image class="goods-item--img" src="@/static/testImg/3.png" mode="widthFix"></image>
<view class="goods-item--title"><text>JAVA语言程序员必备的技术书籍</text></view>
<view class="goods-item--pirce-box">
<view>
<text class="goods-item--pirce">¥29</text>
<text class="goods-item--original-pirce">¥50</text>
</view>
<view>
<image class="goods-item--icon" src='@/static/index/bz.png'></image>
</view>
</view>
<view class="goods-item--activity">
<text class="goods-item--activity-title">秒杀</text>
<text class="goods-item--activity-desc">正在抢购中</text>
</view>
</view>
<view class="goods-item">
<image class="goods-item--img" src="@/static/testImg/2.png" mode="widthFix"></image>
<view class="goods-item--title"><text>JAVA语言程序员必备的技术书籍</text></view>
<view class="goods-item--pirce-box">
<view>
<text class="goods-item--pirce">¥29</text>
<text class="goods-item--original-pirce">¥50</text>
</view>
<view>
<image class="goods-item--icon" src='@/static/index/bz.png'></image>
</view>
</view>
<view class="goods-item--activity">
<text class="goods-item--activity-title">秒杀</text>
<text class="goods-item--activity-desc">正在抢购中</text>
</view>
</view>
</view>
<view class="goods--column">
<view class="goods-item">
<image class="goods-item--img" src="@/static/testImg/3.png" mode="widthFix"></image>
<view class="goods-item--title"><text>JAVA语言程序员必备的技术书籍</text></view>
<view class="goods-item--pirce-box">
<view>
<text class="goods-item--pirce">¥29</text>
<text class="goods-item--original-pirce">¥50</text>
</view>
<view>
<image class="goods-item--icon" src='@/static/index/bz.png'></image>
</view>
</view>
<view class="goods-item--activity">
<text class="goods-item--activity-title">秒杀</text>
<text class="goods-item--activity-desc">正在抢购中</text>
</view>
</view>
<view class="goods-item">
<image class="goods-item--img" src="@/static/testImg/2.png" mode="widthFix"></image>
<view class="goods-item--title"><text>JAVA语言程序员必备的技术书籍</text></view>
<view class="goods-item--pirce-box">
<view>
<text class="goods-item--pirce">¥29</text>
<text class="goods-item--original-pirce">¥50</text>
</view>
<view>
<image class="goods-item--icon" src='@/static/index/bz.png'></image>
</view>
</view>
<view class="goods-item--activity">
<text class="goods-item--activity-title">秒杀</text>
<text class="goods-item--activity-desc">正在抢购中</text>
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.goods{
padding: 0 30rpx;
display: flex;
justify-content: space-between;
flex-flow:wrap;
&--column{
width: 335rpx;
}
&-item{
width: 335rpx;
border-radius: 8rpx;
background: #fff;
margin-bottom: 30rpx;
overflow: hidden;
padding-bottom: 20rpx;
&--img{
width: 100%;
}
&--title{
font-size: 28rpx;
padding: 20rpx 20rpx;
}
&--pirce-box{
padding:0 20rpx;
display: flex;
justify-content: space-between;
}
&--pirce{
font-size: 32rpx;
color: #FF512B;
}
&--original-pirce{
color: #ccc;
font-size: 20rpx;
text-decoration: line-through;
margin-left: 15rpx;
}
&--icon{
width: 22rpx;
height: 24rpx;
}
&--activity{
height: 40rpx;
line-height: 40rpx;
margin:20rpx 20rpx 0;
background: #FFEDE9;
border-radius: 20rpx;
font-size: 22rpx;
color: #FF512B;
display: flex;
&-title{
height: 40rpx;
display: block;
border-radius: 20rpx;
padding: 0 14rpx;
background: #FF512B;
color: #fff;
}
&-desc{
flex: 1 1 auto;
text-align: center;
}
}
}
}
</style>

@ -0,0 +1,56 @@
<!--
* @Author: ch
* @Date: 2022-03-23 16:05:10
* @LastEditors: ch
* @LastEditTime: 2022-03-23 16:32:42
* @Description: file content
-->
<template>
<view class="search">
<image class="search--icon" src="@/static/search/arrow.png" @click="$Router.back()"></image>
<u--input class="search--input" :placeholder="placeholder" @focus="$Router.push('/pages/goods/search')"
prefixIcon="search" clearable prefixIconStyle="font-size:48rpx;color:#ccc"
v-model="value" placeholderClass="search--input__placeholder"></u--input>
</view>
</template>
<script>
export default {
props: {
value : {
type : String,
default : ''
},
placeholder : {
type : String,
default : '请输入您想搜索的商品名称'
}
}
}
</script>
<style lang="scss" scoped>
.search{
height: 88rpx;
padding: 0 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
&--icon{
width: 14rpx;
height: 28rpx;
}
&--input{
display: block;
margin-left:40rpx;
height: 70rpx;
border:none;
box-sizing: border-box;
background: #F8F8F8;
&__placeholder{
font-size: 26rpx;
color: #666;
}
}
}
</style>

@ -0,0 +1,204 @@
<template>
<view class="container">
<view v-if="isLoading" class="loading">
<u-loading-icon mode="circle"></u-loading-icon>
</view>
<view v-else class="field-body" @click="handleSelect()">
<view class="field-value oneline-hide">{{ valueText ? valueText: placeholder }}</view>
</view>
<u-picker :show="show" keyName="label" :columns="options" @cancel="show = false" @change="changeRegion" @confirm="onConfirm"></u-picker>
</view>
</template>
<script>
import Emitter from 'uview-ui/libs/util/emitter'
import regionData from '@/mock/region.json';
// import RegionModel from '@/common/model/Region'
// 根据选中的value集获取索引集keys
// 用于设置默认选中
const findOptionsKey = (data, searchValue, deep = 1, keys = []) => {
const index = data.findIndex(item => item.value === searchValue[deep - 1])
if (index > -1) {
keys.push(index)
if (data[index].children) {
findOptionsKey(data[index].children, searchValue, ++deep, keys)
}
}
return keys
}
export default {
name: 'SelectRegion',
mixins: [Emitter],
model: {
prop: 'value',
event: 'change'
},
props: {
// v-model 指定选中项
value: {
type: Array,
default: () => {
return []
}
},
// 未选中时的提示文字
placeholder: {
type: String,
default: '请选择省/市/区'
}
},
data() {
return {
// 正在加载
isLoading: true,
// 是否显示
show: false,
// 默认选中的值
defaultValue: [],
// 选中项内容(文本展示)
valueText: '',
// 级联选择器数据
options: []
}
},
watch: {
// 监听v-model
value(val) {
// 设置默认选中的值
this.valueText = val.map(item => item.label).join('/')
this.setDefaultValue(val)
// 将当前的值发送到 u-form-item 进行校验
this.dispatch('u-form-item', 'on-form-change', val)
},
},
created() {
// 获取地区数据
this.getTreeData()
},
methods: {
// 打开选择器
handleSelect() {
this.show = true
},
// 获取地区数据
getTreeData() {
const app = this
app.isLoading = true;
this.options = [[
{
label : '北京',
id : 11000000,
children : [
{
label : '东城',
id : 11100000
},
{
label : '西城',
id : 11200000
}
]
},
{
label : '湖南',
id : 43000000,
children : [
{
label : '长沙',
id : 11100000
},
{
label : '邵阳',
id : 11200000
}
]
}
],[{
label : '东城',
id : 11100000
},
{
label : '西城',
id : 11200000
}]];
app.isLoading = false;
// RegionModel.getTreeData()
// .then(regions => {
// // 格式化级联选择器数据
// this.options = this.getOptions(regions)
// })
// .finally(() => app.isLoading = false)
},
changeRegion(val){
this.options = [this.options[0],val.value[0].children]
},
// 确认选择后的回调
onConfirm(value) {
// 绑定到v-model执行的值
this.$emit('input', value)
this.$emit('change', value)
},
/**
* 设置默认选中的值
* 该操作是为了每次打开选择器时聚焦到上次选择
* @param {Object} value
*/
setDefaultValue(value) {
const values = value.map(item => item.value)
const options = this.options
this.defaultValue = findOptionsKey(options, values)
},
/**
* 格式化级联选择器数据
* @param {*} regions 地区数据
*/
getOptions(regions) {
const { getOptions, getChildren } = this
const options = []
for (const index in regions) {
const item = regions[index]
const children = getChildren(item)
const optionItem = {
value: item.id,
label: item.name
}
if (children !== false) {
optionItem.children = getOptions(children)
}
options.push(optionItem)
}
return options
},
// 获取子集地区
getChildren(item) {
if (item.city) {
return item.city
}
if (item.region) {
return item.region
}
return false
}
}
}
</script>
<style lang="scss" scoped>
.container {
width: 100%;
}
.loading {
padding-left: 10rpx;
// text-align: center;
}
</style>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,450 @@
<!-- 步进器 -->
<template>
<view class="number-box">
<view class="u-icon-minus" @touchstart.prevent="btnTouchStart('minus')" @touchend.stop.prevent="clearTimer" :class="{ 'u-icon-disabled': disabled || inputVal <= min }"
:style="{
background: bgColor,
height: inputHeight + 'rpx',
color: color,
fontSize: size + 'rpx',
minHeight: '1.4em'
}">
<view :style="'font-size:'+(Number(size)+10)+'rpx'" class="num-btn">-</view>
</view>
<input :disabled="disabledInput || disabled" :cursor-spacing="getCursorSpacing" :class="{ 'u-input-disabled': disabled }"
v-model="inputVal" class="u-number-input" @blur="onBlur"
type="number" :style="{
color: color,
fontSize: size + 'rpx',
background: bgColor,
height: inputHeight + 'rpx',
width: inputWidth + 'rpx',
}" />
<view class="u-icon-plus" @touchstart.prevent="btnTouchStart('plus')" @touchend.stop.prevent="clearTimer" :class="{ 'u-icon-disabled': disabled || inputVal >= max }"
:style="{
background: bgColor,
height: inputHeight + 'rpx',
color: color,
fontSize: size + 'rpx',
minHeight: '1.4em',
}">
<view :style="'font-size:'+(Number(size)+10)+'rpx'" class="num-btn">+</view>
</view>
</view>
</template>
<script>
/**
* numberBox 步进器
* @description 该组件一般用于商城购物选择物品数量的场景。注意:该输入框只能输入大于或等于0的整数,不支持小数输入
* @tutorial https://www.uviewui.com/components/numberBox.html
* @property {Number} value 输入框初始值(默认1)
* @property {String} bg-color 输入框和按钮的背景颜色(默认#F2F3F5)
* @property {Number} min 用户可输入的最小值(默认0)
* @property {Number} max 用户可输入的最大值(默认99999)
* @property {Number} step 步长,每次加或减的值(默认1)
* @property {Number} stepFirst 步进值,首次增加或最后减的值(默认step值和一致)
* @property {Boolean} disabled 是否禁用操作,禁用后无法加减或手动修改输入框的值(默认false)
* @property {Boolean} disabled-input 是否禁止输入框手动输入值(默认false)
* @property {Boolean} positive-integer 是否只能输入正整数(默认true)
* @property {String | Number} size 输入框文字和按钮字体大小,单位rpx(默认26)
* @property {String} color 输入框文字和加减按钮图标的颜色(默认#323233)
* @property {String | Number} input-width 输入框宽度,单位rpx(默认80)
* @property {String | Number} input-height 输入框和按钮的高度,单位rpx(默认50)
* @property {String | Number} index 事件回调时用以区分当前发生变化的是哪个输入框
* @property {Boolean} long-press 是否开启长按连续递增或递减(默认true)
* @property {String | Number} press-time 开启长按触发后,每触发一次需要多久,单位ms(默认250)
* @property {String | Number} cursor-spacing 指定光标于键盘的距离,避免键盘遮挡输入框,单位rpx(默认200)
* @event {Function} change 输入框内容发生变化时触发,对象形式
* @event {Function} blur 输入框失去焦点时触发,对象形式
* @event {Function} minus 点击减少按钮时触发(按钮可点击情况下),对象形式
* @event {Function} plus 点击增加按钮时触发(按钮可点击情况下),对象形式
* @example <number-box :min="1" :max="100"></number-box>
*/
export default {
name: "NumberBox",
emits: ["update:modelValue", "input", "change", "blur", "plus", "minus"],
props: {
// 预显示的数字
value: {
type: Number,
default: 1
},
modelValue: {
type: Number,
default: 1
},
// 背景颜色
bgColor: {
type: String,
default: '#F2F3F5'
},
// 最小值
min: {
type: Number,
default: 0
},
// 最大值
max: {
type: Number,
default: 99999
},
// 步进值,每次加或减的值
step: {
type: Number,
default: 1
},
// 步进值,首次增加或最后减的值
stepFirst: {
type: Number,
default: 0
},
// 是否只能输入 step 的倍数
stepStrictly: {
type: Boolean,
default: false
},
// 是否禁用加减操作
disabled: {
type: Boolean,
default: false
},
// input的字体大小,单位rpx
size: {
type: [Number, String],
default: 26
},
// 加减图标的颜色
color: {
type: String,
default: '#323233'
},
// input宽度,单位rpx
inputWidth: {
type: [Number, String],
default: 80
},
// input高度,单位rpx
inputHeight: {
type: [Number, String],
default: 50
},
// index索引,用于列表中使用,让用户知道是哪个numberbox发生了变化,一般使用for循环出来的index值即可
index: {
type: [Number, String],
default: ''
},
// 是否禁用输入框,与disabled作用于输入框时,为OR的关系,即想要禁用输入框,又可以加减的话
// 设置disabled为false,disabledInput为true即可
disabledInput: {
type: Boolean,
default: false
},
// 输入框于键盘之间的距离
cursorSpacing: {
type: [Number, String],
default: 100
},
// 是否开启长按连续递增或递减
longPress: {
type: Boolean,
default: true
},
// 开启长按触发后,每触发一次需要多久
pressTime: {
type: [Number, String],
default: 250
},
// 是否只能输入大于或等于0的整数(正整数)
positiveInteger: {
type: Boolean,
default: true
}
},
watch: {
value(v1, v2) {
// 只有value的改变是来自外部的时候,才去同步inputVal的值,否则会造成循环错误
if(!this.changeFromInner) {
this.inputVal = v1;
// 因为inputVal变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
// 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
// 将changeFromInner设置为false
this.$nextTick(function(){
this.changeFromInner = false;
})
}
},
modelValue(v1, v2) {
// 只有value的改变是来自外部的时候,才去同步inputVal的值,否则会造成循环错误
if(!this.changeFromInner) {
this.inputVal = v1;
// 因为inputVal变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
// 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
// 将changeFromInner设置为false
this.$nextTick(function(){
this.changeFromInner = false;
})
}
},
inputVal(v1, v2) {
// 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
if (v1 == '') return;
let value = 0;
// 首先判断是否数值,并且在min和max之间,如果不是,使用原来值
let tmp = this.isNumber(v1);
if (tmp && v1 >= this.min && v1 <= this.max) value = v1;
else value = v2;
// 判断是否只能输入大于等于0的整数
if(this.positiveInteger) {
// 小于0,或者带有小数点,
if(v1 < 0 || String(v1).indexOf('.') !== -1) {
value = v2;
// 双向绑定input的值,必须要使用$nextTick修改显示的值
this.$nextTick(() => {
this.inputVal = v2;
})
}
}
// 发出change事件
this.handleChange(value, 'change');
},
min(v1){
if(v1 !== undefined && v1!="" && this.getValue() < v1){
this.$emit("input",v1);
}
},
max(v1){
if(v1 !== undefined && v1!="" && this.getValue() > v1){
this.$emit("input",v1);
}
}
},
data() {
return {
inputVal: 1, // 输入框中的值,不能直接使用props中的value,因为应该改变props的状态
timer: null, // 用作长按的定时器
changeFromInner: false, // 值发生变化,是来自内部还是外部
innerChangeTimer: null, // 内部定时器
};
},
created() {
this.inputVal = Number(this.getValue());
},
computed: {
getCursorSpacing() {
// 先将值转为px单位,再转为数值
return Number(uni.upx2px(this.cursorSpacing));
}
},
methods: {
getValue(){
// #ifndef VUE3
return this.value;
// #endif
// #ifdef VUE3
return this.modelValue;
// #endif
},
// 点击退格键
btnTouchStart(callback) {
// 先执行一遍方法,否则会造成松开手时,就执行了clearTimer,导致无法实现功能
this[callback]();
// 如果没开启长按功能,直接返回
if (!this.longPress) return;
clearInterval(this.timer); //再次清空定时器,防止重复注册定时器
this.timer = null;
this.timer = setInterval(() => {
// 执行加或减函数
this[callback]();
}, this.pressTime);
},
clearTimer() {
this.$nextTick(() => {
clearInterval(this.timer);
this.timer = null;
})
},
minus() {
this.computeVal('minus');
},
plus() {
this.computeVal('plus');
},
// 为了保证小数相加减出现精度溢出的问题
calcPlus(num1, num2) {
let baseNum, baseNum1, baseNum2;
try {
baseNum1 = num1.toString().split('.')[1].length;
} catch (e) {
baseNum1 = 0;
}
try {
baseNum2 = num2.toString().split('.')[1].length;
} catch (e) {
baseNum2 = 0;
}
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2; //精度
return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision);
},
// 为了保证小数相加减出现精度溢出的问题
calcMinus(num1, num2) {
let baseNum, baseNum1, baseNum2;
try {
baseNum1 = num1.toString().split('.')[1].length;
} catch (e) {
baseNum1 = 0;
}
try {
baseNum2 = num2.toString().split('.')[1].length;
} catch (e) {
baseNum2 = 0;
}
baseNum = Math.pow(10, Math.max(baseNum1, baseNum2));
let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2;
return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision);
},
computeVal(type) {
uni.hideKeyboard();
if (this.disabled) return;
let value = 0;
// 新增stepFirst开始
// 减
if (type === 'minus') {
if(this.stepFirst > 0 && this.inputVal == this.stepFirst){
value = this.min;
}else{
value = this.calcMinus(this.inputVal, this.step);
}
} else if (type === 'plus') {
if(this.stepFirst > 0 && this.inputVal < this.stepFirst){
value = this.stepFirst;
}else{
value = this.calcPlus(this.inputVal, this.step);
}
}
if(this.stepStrictly){
let strictly = value % this.step;
if(strictly > 0){
value -= strictly;
}
}
if (value > this.max ) {
value = this.max;
}else if (value < this.min) {
value = this.min;
}
// 新增stepFirst结束
this.inputVal = value;
this.handleChange(value, type);
},
// 处理用户手动输入的情况
onBlur(event) {
let val = 0;
let value = event.detail.value;
// 如果为非0-9数字组成,或者其第一位数值为0,直接让其等于min值
// 这里不直接判断是否正整数,是因为用户传递的props min值可能为0
if (!/(^\d+$)/.test(value) || value[0] == 0) val = this.min;
val = +value;
// 新增stepFirst开始
if(this.stepFirst > 0 && this.inputVal < this.stepFirst && this.inputVal>0){
val = this.stepFirst;
}
// 新增stepFirst结束
if(this.stepStrictly){
let strictly = val % this.step;
if(strictly > 0){
val -= strictly;
}
}
if (val > this.max) {
val = this.max;
} else if (val < this.min) {
val = this.min;
}
this.$nextTick(() => {
this.inputVal = val;
})
this.handleChange(val, 'blur');
},
handleChange(value, type) {
if (this.disabled) return;
// 清除定时器,避免造成混乱
if(this.innerChangeTimer) {
clearTimeout(this.innerChangeTimer);
this.innerChangeTimer = null;
}
// 发出input事件,修改通过v-model绑定的值,达到双向绑定的效果
this.changeFromInner = true;
// 一定时间内,清除changeFromInner标记,否则内部值改变后
// 外部通过程序修改value值,将会无效
this.innerChangeTimer = setTimeout(() => {
this.changeFromInner = false;
}, 150);
this.$emit('input', Number(value));
this.$emit("update:modelValue", Number(value));
this.$emit(type, {
// 转为Number类型
value: Number(value),
index: this.index
})
},
/**
* 验证十进制数字
*/
isNumber(value) {
return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
}
}
};
</script>
<style lang="scss" scoped>
.number-box {
display: inline-flex;
align-items: center;
}
.u-number-input {
position: relative;
text-align: center;
padding: 0;
margin: 0 6rpx;
display: flex;
align-items: center;
justify-content: center;
}
.u-icon-plus,
.u-icon-minus {
width: 60rpx;
display: flex;
justify-content: center;
align-items: center;
}
.u-icon-plus {
border-radius: 0 8rpx 8rpx 0;
}
.u-icon-minus {
border-radius: 8rpx 0 0 8rpx;
}
.u-icon-disabled {
color: #c8c9cc !important;
background: #f7f8fa !important;
}
.u-input-disabled {
color: #c8c9cc !important;
background-color: #f2f3f5 !important;
}
.num-btn{
font-weight:550;
position: relative;
top:-4rpx;
}
</style>

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<script>
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
</script>
<title></title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/main.js"></script>
</body>
</html>

@ -0,0 +1,29 @@
/*
* @Author: ch
* @Date: 2021-07-26 23:22:16
* @LastEditors: ch
* @LastEditTime: 2022-03-22 17:47:44
* @Description: file content
*/
import Vue from 'vue';
import App from './App';
import {router,RouterMount} from './common/router/index.js';
import uView from 'uview-ui';
Vue.use(router);
Vue.use(uView);
Vue.config.productionTip = false;
App.mpType = 'app'
const app = new Vue({
...App
})
// #ifdef H5
RouterMount(app,router,'#app')
// #endif
// #ifndef H5
app.$mount(); //为了兼容小程序及app端必须这样写才有效果
// #endif

@ -0,0 +1,77 @@
{
"name" : "demo",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App特有相关 */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"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.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.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<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.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios打包配置 */
"ios" : {},
/* SDK配置 */
"sdkConfigs" : {}
}
},
/* 快应用特有相关 */
"quickapp" : {},
/* 小程序特有相关 */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "2",
"h5" : {
"router" : {
"mode" : "history"
}
}
}

@ -0,0 +1 @@
{"status":200,"message":"success","data":{"assets":{"balance":"0.00","points":0,"coupon":0}}}

@ -0,0 +1,758 @@
{
"status": 200,
"message": "success",
"data": {
"cartTotal": 2,
"list": [
{
"id": 11922,
"goods_id": 10009,
"goods_sku_id": "10019_10021_10023",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:45:02",
"update_time": "2022-03-18 19:45:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10172,
"goods_sku_id": "10019_10021_10023",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1499.00",
"line_price": "0.00",
"stock_num": 98,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "魅海蓝",
"id": 10019
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "耳机套装",
"id": 10023
}
}
],
"spec_value_ids": [
10019,
10021,
10023
]
}
}
},
{
"id": 11923,
"goods_id": 10009,
"goods_sku_id": "10018_10021_10022",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:50:02",
"update_time": "2022-03-18 19:50:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
}
}
},
{
"id": 11923,
"goods_id": 10009,
"goods_sku_id": "10018_10021_10022",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:50:02",
"update_time": "2022-03-18 19:50:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
}
}
},
{
"id": 11923,
"goods_id": 10009,
"goods_sku_id": "10018_10021_10022",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:50:02",
"update_time": "2022-03-18 19:50:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
}
}
},
{
"id": 11923,
"goods_id": 10009,
"goods_sku_id": "10018_10021_10022",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:50:02",
"update_time": "2022-03-18 19:50:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
}
}
},
{
"id": 11923,
"goods_id": 10009,
"goods_sku_id": "10018_10021_10022",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:50:02",
"update_time": "2022-03-18 19:50:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
}
}
},
{
"id": 11923,
"goods_id": 10009,
"goods_sku_id": "10018_10021_10022",
"goods_num": 1,
"user_id": 12872,
"is_delete": 0,
"store_id": 10001,
"create_time": "2022-03-18 19:50:02",
"update_time": "2022-03-18 19:50:02",
"goods": {
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
}
}
}
]
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
{"status":200,"message":"success","data":{"setting":{"page_category_template":{"style":20,"shareTitle":""},"points":{"points_name":"积分","describe":"a) 积分不可兑现、不可转让,仅可在本平台使用;\nb) 您在本平台参加特定活动也可使用积分,详细使用规则以具体活动时的规则为准;\nc) 积分的数值精确到个位(小数点后全部舍弃,不进行四舍五入)\nd) 买家在完成该笔交易(订单状态为“已签收”)后才能得到此笔交易的相应积分,如购买商品参加店铺其他优惠,则优惠的金额部分不享受积分获取;"},"recharge":{"is_entrance":1,"is_custom":1,"describe":"1. 账户充值仅限微信在线方式支付,充值金额实时到账;\n2. 账户充值套餐赠送的金额即时到账;\n3. 账户余额有效期:自充值日起至用完即止;\n4. 若有其它疑问,可拨打客服电话400-000-1234"},"register":{"registerMethod":10,"isOauthMpweixin":1,"isManualBind":1},"_other":{"h5Url":"https:\/\/shop2.client.yiovo.com\/"}}}}

@ -0,0 +1,270 @@
{
"status": 200,
"message": "success",
"data": {
"order": {
"goodsList": [
{
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10172,
"goods_sku_id": "10019_10021_10023",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1499.00",
"line_price": "0.00",
"stock_num": 98,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "魅海蓝",
"id": 10019
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "耳机套装",
"id": 10023
}
}
],
"spec_value_ids": [
10019,
10021,
10023
]
},
"goods_price": "1499.00",
"total_num": 1,
"goods_sku_id": "10019_10021_10023",
"total_price": "1499.00",
"grade_ratio": 0,
"grade_goods_price": 0,
"grade_total_money": 0,
"coupon_money": 0,
"max_points_num": 0,
"pointsNum": 0,
"points_money": 0,
"total_pay_price": "1499.00",
"expressPrice": 0,
"points_bonus": 0
},
{
"goods_id": 10009,
"goods_name": "荣耀9i 4GB+64GB 幻夜黑 移动联通电信4G全面屏手机 双卡双待",
"goods_no": "",
"video_id": 0,
"video_cover_id": 0,
"selling_point": "",
"spec_type": 20,
"goods_price_min": "1399.00",
"goods_price_max": "1499.00",
"line_price_min": "0.00",
"line_price_max": "0.00",
"stock_total": 571,
"content": "<p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20842\/114\/772658709\/51280\/97cce0f1\/5b175981N7290ead0.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t20566\/33\/742373676\/46768\/fb7a4cc4\/5b17599cNea911d0e.jpg\"\/><\/p><p><img src=\"https:\/\/m.360buyimg.com\/mobilecms\/jfs\/t21448\/12\/771050934\/55571\/1207e815\/5b1759ddN64ebf417.jpg\"\/><\/p>",
"delivery_id": 10001,
"is_points_gift": 1,
"is_points_discount": 1,
"is_alone_points_discount": 0,
"points_discount_config": "",
"is_enable_grade": 1,
"is_alone_grade": 0,
"alone_grade_equity": [],
"status": 10,
"goods_images": [
{
"file_id": 10080,
"group_id": 0,
"channel": 10,
"storage": "qiniu",
"domain": "http:\/\/static.yoshop.xany6.com",
"file_type": 10,
"file_name": "2018071717370507f183424.jpg",
"file_path": "2018071717370507f183424.jpg",
"file_size": 186872,
"file_ext": "jpg",
"cover": "",
"uploader_id": 0,
"is_recycle": 0,
"is_delete": 0,
"update_time": "2021-03-01 08:00:00",
"preview_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"external_url": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg"
}
],
"goods_image": "http:\/\/static.yoshop.xany6.com\/2018071717370507f183424.jpg",
"goods_sales": 0,
"is_user_grade": false,
"skuInfo": {
"id": 10169,
"goods_sku_id": "10018_10021_10022",
"goods_id": 10009,
"image_id": 0,
"goods_sku_no": "",
"goods_price": "1399.00",
"line_price": "0.00",
"stock_num": 97,
"goods_weight": 0.14,
"goods_props": [
{
"group": {
"name": "颜色",
"id": 10001
},
"value": {
"name": "幻夜黑",
"id": 10018
}
},
{
"group": {
"name": "版本",
"id": 10002
},
"value": {
"name": "全网通4+64G",
"id": 10021
}
},
{
"group": {
"name": "套装",
"id": 10004
},
"value": {
"name": "官方标配",
"id": 10022
}
}
],
"spec_value_ids": [
10018,
10021,
10022
]
},
"goods_price": "1399.00",
"total_num": 1,
"goods_sku_id": "10018_10021_10022",
"total_price": "1399.00",
"grade_ratio": 0,
"grade_goods_price": 0,
"grade_total_money": 0,
"coupon_money": 0,
"max_points_num": 0,
"pointsNum": 0,
"points_money": 0,
"total_pay_price": "1399.00",
"expressPrice": 0,
"points_bonus": 0
}
],
"orderTotalNum": 2,
"couponList": [],
"hasError": false,
"errorMsg": "",
"delivery": 10,
"address": {
"address_id": 11379,
"name": "x",
"phone": "13245678909",
"province_id": 1,
"city_id": 2,
"region_id": 3,
"detail": "ddd",
"user_id": 12872,
"region": {
"province": "北京",
"city": "北京市",
"region": "东城区"
}
},
"existAddress": true,
"expressPrice": "0.00",
"isIntraRegion": true,
"isAllowPoints": false,
"isUsePoints": "0",
"pointsMoney": 0,
"pointsBonus": 0,
"payType": 10,
"setting": {
"deliveryType": [
10
],
"points_name": "积分",
"points_describe": "a) 积分不可兑现、不可转让,仅可在本平台使用;\nb) 您在本平台参加特定活动也可使用积分,详细使用规则以具体活动时的规则为准;\nc) 积分的数值精确到个位(小数点后全部舍弃,不进行四舍五入)\nd) 买家在完成该笔交易(订单状态为“已签收”)后才能得到此笔交易的相应积分,如购买商品参加店铺其他优惠,则优惠的金额部分不享受积分获取;"
},
"orderTotalPrice": "2898.00",
"couponId": 0,
"couponMoney": 0,
"orderPrice": "2898.00",
"orderPayPrice": "2898.00"
}
}
}

@ -0,0 +1 @@
{"status":200,"message":"success","data":{"counts":{"payment":0,"delivery":0,"received":0}}}

@ -0,0 +1 @@
{"status":200,"message":"success","data":{"userInfo":{"user_id":12872,"mobile":"189****6782","nick_name":"189****6782","avatar_id":0,"gender":"未知","country":"","province":"","city":"","address_id":11379,"balance":"0.00","points":0,"pay_money":"0.00","expend_money":"0.00","grade_id":0,"platform":"H5","last_login_time":1647603880,"avatar":null,"grade":null}}}

@ -0,0 +1,25 @@
{
"name": "shopping-app",
"version": "1.0.0",
"description": "严选",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "http://internel-git.mashibing.cn/chenhui/shopping-app.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"mp-html": "^2.2.2",
"uni-read-pages": "^1.0.5",
"uni-simple-router": "^2.0.7",
"uview-ui": "^2.0.29"
},
"devDependencies": {
"@dcloudio/uni-helper-json": "^1.0.13"
}
}

@ -0,0 +1,158 @@
{
"pages": [
{
"path": "pages/index/index",
"style": {
"navigationStyle" : "custom",
"backgroundColor" : "#69ADE5"
}
},
{
"path": "pages/sekill/index",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/goods/category",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/goods/search",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/goods/list/index",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/goods/detail",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/goods/detail/index",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/index",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/login",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/cart",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/setting",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/address/list",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/address/create",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/order/submit",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
},
{
"path": "pages/account/order/list",
"style": {
"navigationStyle" : "custom",
"navigationBarTitleText": "uni-app"
}
}
],
"tabBar": {
"color": "#BBBBBB",
"selectedColor": "#FF875B",
"backgroundColor": "#FFF",
"borderStyle" : "white",
"fontSize" : "22rpx",
"height" : "120rpx",
"list": [
{
"pagePath": "pages/index/index",
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/home-active.png",
"text": "首页"
},
{
"pagePath": "pages/goods/category",
"iconPath": "static/tabbar/category.png",
"selectedIconPath": "static/tabbar/category-active.png",
"text": "分类"
},
{
"pagePath": "pages/account/cart",
"iconPath": "static/tabbar/shopping-cart.png",
"selectedIconPath": "static/tabbar/shopping-cart-active.png",
"text": "购物车"
},
{
"pagePath": "pages/account/index",
"iconPath": "static/tabbar/account.png",
"selectedIconPath": "static/tabbar/account-active.png",
"text": "我的"
}
]
},
"globalStyle": {
"navigationBarTextStyle": "white",
"backgroundColor": "#F8F8F8"
},
"easycom" : {
"autoscan": true,
"custom": {
"^u-(.*)": "uview-ui/components/u-$1/u-$1.vue"
}
}
}

@ -0,0 +1,188 @@
<!--
* @Author: ch
* @Date: 2022-03-22 14:12:18
* @LastEditors: ch
* @LastEditTime: 2022-03-22 16:31:24
* @Description: file content
-->
<template>
<view class="container">
<!-- 标题 -->
<view class="page-title">收货地址</view>
<!-- 表单组件 -->
<view class="form-wrapper">
<u-form :model="form" ref="uForm" label-width="140rpx">
<u-form-item label="姓名" prop="name">
<u-input v-model="form.name" placeholder="请输入收货人姓名" />
</u-form-item>
<u-form-item label="电话" prop="phone">
<u-input v-model="form.phone" placeholder="请输入收货人手机号" />
</u-form-item>
<u-form-item label="地区" prop="region">
<select-region v-model="form.region" />
</u-form-item>
<u-form-item label="详细地址" prop="detail" :border-bottom="false">
<u-input v-model="form.detail" placeholder="街道门牌、楼层等信息" />
</u-form-item>
</u-form>
</view>
<!-- 操作按钮 -->
<view class="footer">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" :class="{ disabled }" @click="handleSubmit()">保存</view>
</view>
</view>
</view>
</template>
<script>
import SelectRegion from '@/components/SelectRegion.vue'
import { isPhone } from '@/common/utils/utils'
// import * as AddressApi from '@/api/address'
// 表单字段元素
const form = {
name: '',
phone: '',
region: [],
detail: ''
}
// 表单验证规则
const rules = {
name: [{
required: true,
message: '请输入姓名',
trigger: ['blur', 'change']
}],
phone: [{
required: true,
message: '请输入手机号',
trigger: ['blur', 'change']
}, {
// 自定义验证函数
validator: (rule, value, callback) => {
// 返回true表示校验通过,返回false表示不通过
return isPhone(value)
},
message: '手机号码不正确',
// 触发器可以同时用blur和change
trigger: ['blur'],
}],
region: [{
required: true,
message: '请选择省市区',
trigger: ['blur', 'change'],
type: 'array'
}],
detail: [{
required: true,
message: '请输入详细地址',
trigger: ['blur', 'change']
}],
}
export default {
components: {
SelectRegion
},
data() {
return {
form,
rules,
// 按钮禁用
disabled: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {},
// 必须要在onReady生命周期,因为onLoad生命周期组件可能尚未创建完毕
onReady() {
this.$refs.uForm.setRules(this.rules)
},
methods: {
// 表单提交
handleSubmit() {
const app = this
if (app.disabled) {
return false
}
app.$refs.uForm.validate(valid => {
if (valid) {
app.disabled = true
AddressApi.add(app.form)
.then(result => {
app.$toast(result.message)
uni.navigateBack()
})
.finally(() => app.disabled = false)
}
})
}
}
}
</script>
<style>
page {
background: #f7f8fa;
}
</style>
<style lang="scss" scoped>
.page-title {
width: 94%;
margin: 0 auto;
padding-top: 40rpx;
font-size: 28rpx;
color: rgba(69, 90, 100, 0.6);
}
.form-wrapper {
margin: 20rpx auto 20rpx auto;
padding: 0 40rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
border-radius: 16rpx;
background: #fff;
}
/* 底部操作栏 */
.footer {
margin-top: 60rpx;
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
padding: 0 20rpx;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 100rpx;
line-height: 100rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
}
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
// 禁用按钮
&.disabled {
background: #ff9779;
}
}
}
</style>

@ -0,0 +1,318 @@
<!--
* @Author: ch
* @Date: 2022-03-22 13:54:15
* @LastEditors: ch
* @LastEditTime: 2022-03-23 10:55:44
* @Description: file content
-->
<template>
<view class="container">
<view class="addres-list">
<view class="address-item" v-for="(item, index) in list" :key="index">
<view class="contacts">
<text class="name">{{ item.name }}</text>
<text class="phone">{{ item.phone }}</text>
</view>
<view class="address">
<text class="region" v-for="(region, idx) in item.region" :key="idx">{{ region }}</text>
<text class="detail">{{ item.detail }}</text>
</view>
<view class="line"></view>
<view class="item-option">
<view class="_left">
<label class="item-radio" @click.stop="handleSetDefault(item.address_id)">
<radio class="radio" color="#fa2209" :checked="item.address_id == defaultId"></radio>
<text class="text">{{ item.address_id == defaultId ? '默认' : '选择' }}</text>
</label>
</view>
<view class="_right">
<view class="events">
<view class="event-item" @click="handleUpdate(item.address_id)">
<text class="iconfont icon-edit"></text>
<text class="title">编辑</text>
</view>
<view class="event-item" @click="handleRemove(item.address_id)">
<text class="iconfont icon-delete"></text>
<text class="title">删除</text>
</view>
</view>
</view>
</view>
</view>
</view>
<BsEmpty v-if="!list.length" :isLoading="isLoading" tips="亲,暂无收货地址" />
<!-- 底部操作按钮 -->
<view class="footer-fixed">
<view class="btn-wrapper">
<view class="btn-item btn-item-main" @click="handleCreate()">添加新地址</view>
</view>
</view>
</view>
</template>
<script>
// import * as AddressApi from '@/api/address'
import BsEmpty from '@/components/BsEmpty'
export default {
components: {
BsEmpty
},
data() {
return {
//当前页面参数
options: {},
// 正在加载
isLoading: true,
// 收货地址列表
list: [],
// 默认收货地址
defaultId: null
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 当前页面参数
this.options = options
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 获取页面数据
this.getPageData()
},
methods: {
// 获取页面数据
getPageData() {
const app = this
app.isLoading = true
Promise.all([app.getDefaultId(), app.getAddressList()])
.then(() => {
// 列表排序把默认收货地址放到最前
app.onReorder()
})
.finally(() => app.isLoading = false)
},
// 获取收货地址列表
getAddressList() {
const app = this
return new Promise((resolve, reject) => {
app.list = []
resolve({})
// AddressApi.list()
// .then(result => {
// app.list = result.data.list
// resolve(result)
// })
// .catch(reject)
})
},
// 获取默认的收货地址
getDefaultId() {
return new Promise((resolve, reject) => {
const app = this
app.defaultId = null
resolve({})
// AddressApi.defaultId()
// .then(result => {
// app.defaultId = result.data.defaultId
// resolve(result)
// })
// .catch(reject)
})
},
// 列表排序把默认收货地址放到最前
onReorder() {
const app = this
app.list.sort(item => {
return item.address_id == app.defaultId ? -1 : 1
})
},
/**
* 添加新地址
*/
handleCreate() {
this.$navTo('pages/address/create')
},
/**
* 编辑地址
* @param {int} addressId 收货地址ID
*/
handleUpdate(addressId) {
this.$navTo('pages/address/update', { addressId })
},
/**
* 删除收货地址
* @param {int} addressId 收货地址ID
*/
handleRemove(addressId) {
const app = this
uni.showModal({
title: "提示",
content: "您确定要删除当前收货地址吗?",
success({ confirm }) {
confirm && app.onRemove(addressId)
}
});
},
/**
* 确认删除收货地址
* @param {int} addressId 收货地址ID
*/
onRemove(addressId) {
const app = this
// AddressApi.remove(addressId)
// .then(result => {
// app.getPageData()
// })
},
/**
* 设置为默认地址
* @param {Object} addressId
*/
handleSetDefault(addressId) {
const app = this
// AddressApi.setDefault(addressId)
// .then(result => {
// app.defaultId = addressId
// app.options.from === 'checkout' && uni.navigateBack()
// })
}
}
}
</script>
<style lang="scss" scoped>
.addres-list {
padding-bottom: calc(constant(safe-area-inset-bottom) + 120rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 120rpx);
}
// 项目内容
.address-item {
margin: 20rpx auto 20rpx auto;
padding: 30rpx 40rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
border-radius: 16rpx;
background: #fff;
}
.contacts {
font-size: 30rpx;
margin-bottom: 16rpx;
.name {
margin-right: 16rpx;
}
}
.address {
font-size: 28rpx;
.region {
margin-right: 10rpx;
}
}
.line {
margin: 20rpx 0;
border-bottom: 1rpx solid #f3f3f3;
}
.item-option {
display: flex;
justify-content: space-between;
height: 48rpx;
// 单选框
.item-radio {
font-size: 28rpx;
.radio {
vertical-align: middle;
transform: scale(0.76)
}
.text {
vertical-align: middle;
}
}
// 操作
.events {
display: flex;
align-items: center;
line-height: 48rpx;
.event-item {
font-size: 28rpx;
margin-right: 26rpx;
color: #4c4c4c;
&:last-child {
margin-right: 0;
}
.title {
margin-left: 8rpx;
}
}
}
}
// 底部操作栏
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
min-height: 120rpx;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// 设置ios刘海屏底部横线安全区域
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-wrapper {
height: 120rpx;
display: flex;
align-items: center;
padding: 0 40rpx;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
}
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
}
}
</style>

@ -0,0 +1,524 @@
<template>
<view class="container">
<!-- 页面顶部 -->
<view class="head">
<view class="head--title">
<text>购物车</text>
<text v-if="total">({{total}})</text>
</view>
<block v-if="list.length">
<view v-if="mode == 'normal'" class="head--edit" @click="handleToggleMode">编辑</view>
<view v-else class="head--finsh" @click="handleToggleMode">完成</view>
</block>
</view>
<!-- 购物车商品列表 -->
<view v-if="list.length" class="cart-list">
<view class="cart-item" v-for="(item, index) in list" :key="index"
@click="onTargetGoods(item.goods_id)">
<label class="cart-item--radio" @click.stop="handleCheckItem(item.id)">
<radio class="radio" color="#FF875B"
:checked="checkedIds.length ? checkedIds.find(i => i === item.id) : false" />
</label>
<image class="cart-item--image" :src="item.goods.goods_image" mode="scaleToFill"></image>
<view class="cart-item--content">
<view class="cart-item--title"><text>{{ item.goods.goods_name }}</text></view>
<view class="cart-item--props">
<view class="cart-item--props-item" v-for="(props, idx) in item.goods.skuInfo.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
<view class="cart-item--footer">
<view class="cart-item--price">
<text class="unit">¥</text>
<text class="value">{{ item.goods.skuInfo.goods_price }}</text>
</view>
<view class="cart-item--stepper">
<u-number-box :min="1" button-size="40rpx" bgColor="#F5F6FA"
:value="item.goods_num" @change="onChangeStepper($event, item)" >
<text slot="minus" class="cart-item--stepper-icon">-</text>
<!-- <text slot="input" class="cart-item--stepper-input">{{item.goods_num}}</text> -->
<text slot="plus" class="cart-item--stepper-icon">+</text>
</u-number-box>
</view>
</view>
</view>
</view>
</view>
<!-- 购物车数据为空 -->
<BsEmpty v-if="!list.length" class="empty"
tips="去挑点喜欢的好货吧~">
<image slot="icon" class="empty--icon" src="@/static/goods/cart.png"></image>
<view slot="btn" class="empty--btn" @click="onTargetIndex">去逛逛</view>
</BsEmpty>
<view class="title">为您推荐</view>
<BsGoodsGroup></BsGoodsGroup>
<!-- 底部操作栏 -->
<view v-if="list.length" class="footer">
<label class="all-radio" @click="handleCheckAll">
<radio class="radio" color="#fa2209" :checked="checkedIds.length > 0 && checkedIds.length === list.length" />
<text>全选</text>
</label>
<view class="total-info">
<text>合计:</text>
<view class="goods-price">
<text class="unit">¥</text>
<text class="value">{{ totalPrice }}</text>
</view>
</view>
<view class="cart-action">
<view class="btn-wrapper">
<!-- dev:下面的disabled条件使用checkedIds.join方式判断 -->
<!-- dev:通常情况下vue项目使用checkedIds.length更合理, 但是length属性在微信小程序中不起作用 -->
<view v-if="mode == 'normal'" class="btn-item btn-main" :class="{ disabled: checkedIds.length === 0 }"
@click="handleOrder()">
<text>去结算 {{ checkedIds.length > 0 ? `(${checkedIds.length})` : '' }}</text>
</view>
<view v-if="mode == 'edit'" class="btn-item btn-main" :class="{ disabled: !checkedIds.length }"
@click="handleDelete()">
<text>删除</text>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { debounce } from '@/common/utils/utils';
import BsEmpty from '@/components/BsEmpty.vue';
import BsGoodsGroup from '@/components/BsGoodsGroup.vue';
import cartData from '@/mock/cart.json'
const CartIdsIndex = 'CartIds'
export default {
components: {
BsEmpty,
BsGoodsGroup
},
data() {
return {
// 正在加载
isLoading: true,
// 当前模式: normal正常 edit编辑
mode: 'normal',
// 购物车商品列表
list: [],
// 购物车商品总数量
total: null,
// 选中的商品ID记录
checkedIds: [],
// 选中的商品总金额
totalPrice: '0.00'
}
},
watch: {
// 监听选中的商品
checkedIds: {
handler(val) {
// 计算合计金额
this.onCalcTotalPrice()
// 记录到缓存中
uni.setStorageSync(CartIdsIndex, val)
},
immediate: false
},
// 监听购物车商品总数量
total(val) {
// 缓存并设置角标
// setCartTotalNum(val)
// setCartTabBadge()
}
},
/**
* 生命周期函数--监听页面显示
*/
onShow(options) {
// 获取购物车商品列表
// checkLogin() ? this.getCartList() : this.isLoading = false
this.isLoading = false
// 获取缓存中的选中记录
this.checkedIds = uni.getStorageSync(CartIdsIndex)
},
methods: {
// 计算合计金额 (根据选中的商品)
onCalcTotalPrice() {
const app = this
// 选中的商品记录
const checkedList = app.list.filter(item => app.checkedIds.length ? app.checkedIds.find(i => i === item.id) : false)
// 计算总金额
let tempPrice = 0;
checkedList.forEach(item => {
// 商品单价, 为了方便计算先转换单位为分 (整数)
const unitPrice = item.goods.skuInfo.goods_price * 100
tempPrice += unitPrice * item.goods_num
})
app.totalPrice = (tempPrice / 100).toFixed(2)
},
// 获取购物车商品列表
getCartList() {
const app = this
app.isLoading = true
// CartApi.list()
// .then(result => {
// app.list = result.data.list
// app.total = result.data.cartTotal
// // 清除checkedIds中无效的ID
// app.onClearInvalidId()
// })
// .finally(() => app.isLoading = false)
},
// 清除checkedIds中无效的ID
onClearInvalidId() {
const app = this
const listIds = app.list.map(item => item.id)
app.checkedIds = listIds.filter(val => checkedIds.indexOf(val) > -1)
},
// 切换当前模式
handleToggleMode() {
this.mode = this.mode == 'normal' ? 'edit' : 'normal'
},
// 监听步进器更改事件
onChangeStepper({ value }, item) {
// 这里是组织首次启动时的执行
if (item.goods_num == value) return
// 记录一个节流函数句柄
if (!item.debounceHandle) {
item.oldValue = item.goods_num
item.debounceHandle = debounce(this.onUpdateCartNum, 500)
}
// 更新商品数量
item.goods_num = value
// 提交更新购物车数量 (节流)
item.debounceHandle(item, item.oldValue, value)
},
// 提交更新购物车数量
onUpdateCartNum(item, oldValue, newValue) {
const app = this
// CartApi.update(item.goods_id, item.goods_sku_id, newValue)
// .then(result => {
// // 更新商品数量
// app.total = result.data.cartTotal
// // 重新计算合计金额
// app.onCalcTotalPrice()
// // 清除节流函数句柄
// item.debounceHandle = null
// })
// .catch(err => {
// // 还原商品数量
// item.goods_num = oldValue
// setTimeout(() => app.$toast(err.errMsg), 10)
// })
},
// 跳转到商品详情页
onTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
// 点击去逛逛按钮, 跳转到首页
onTargetIndex() {
this.$navTo('pages/index/index')
},
// 选中商品
handleCheckItem(cartId) {
const { checkedIds } = this
const index = checkedIds.findIndex(id => id === cartId)
index < 0 ? checkedIds.push(cartId) : checkedIds.splice(index, 1)
},
// 全选事件
handleCheckAll() {
const { checkedIds, list } = this
this.checkedIds = checkedIds.length === list.length ? [] : list.map(item => item.id)
},
// 结算选中的商品
handleOrder() {
const app = this
if (app.checkedIds.length) {
const cartIds = app.checkedIds.join()
app.$Router.push('/pages/order/submit', { mode: 'cart', cartIds })
}
},
// 删除选中的商品弹窗事件
handleDelete() {
const app = this
if (!app.checkedIds.length) {
return false
}
uni.showModal({
title: '友情提示',
content: '您确定要删除该商品吗?',
showCancel: true,
success({ confirm }) {
// 确认删除
confirm && app.onClearCart()
}
})
},
// 确认删除商品
onClearCart() {
const app = this
// CartApi.clear(app.checkedIds)
// .then(result => app.getCartList())
}
}
}
</script>
<style>
page {
background: #f5f5f5;
padding-bottom: 240rpx;
}
</style>
<style lang="scss" scoped>
// 页面顶部
.head {
height: 88rpx;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
background-color: #fff;
position: sticky;
top: var(--window-top);
z-index: 999;
&--totla{
font-size: 36rpx;
color: #333;
}
&--edit,&--finsh{
font-size: 28rpx;
color: #999;
}
&--finsh{
color: #FF512B;
}
}
// 购物车列表
.cart-list {
background: #fff;
}
.cart-item {
width: 690rpx;
display: flex;
align-items: center;
padding: 30rpx 0;
margin: 0 auto 24rpx auto;
border-bottom: 1px solid #eee;
&--radio {
width: 56rpx;
height: 80rpx;
line-height: 80rpx;
margin-right: 10rpx;
text-align: center;
.radio {
transform: scale(0.76)
}
}
&--image {
width: 170rpx;
height: 170rpx;
display: block;
border-radius: 12rpx;
}
&--content {
flex: 1;
padding-left: 24rpx;
}
&--title {
font-size: 28rpx;
line-height: 39rpx;
max-height: 76rpx;
overflow: hidden;
text-overflow: ellipsis;
display:-webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:2;
}
&--props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
&-item {
display: inline-block;
margin-right: 14rpx;
}
}
&--footer {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20rpx;
}
&--price {
vertical-align: bottom;
color: #FF512B;
.unit {
font-size: 20rpx;
}
.value {
font-size: 22rpx;
font-weight: bold;
}
}
&--stepper-icon{
font-size: 38rpx;
color: #ccc;
margin:0 14rpx;
}
&--stepper-input{
height:40rpx;
margin: 0 14rpx;
}
}
// 空数据按钮
.empty{
background: #fff;
&--btn {
width: 220rpx;
margin: 0 auto 10rpx auto;
font-size: 28rpx;
height: 64rpx;
line-height: 64rpx;
text-align: center;
color: #333;
border-radius: 50rpx;
border: 1px solid rgb(192, 185, 185);
}
&--icon{
width: 304rpx;
height: 192rpx;
}
}
.title{
font-size: 32rpx;
text-align: center;
margin: 51rpx auto 30rpx auto;
display: flex;
align-items: center;
justify-content: space-between;
width: 500rpx;
&::after,&::before{
display: inline-block;
content: '';
width: 160rpx;
height: 2rpx;
background: linear-gradient(90deg, #CCCCCC 0%, rgba(204, 204, 204, 0) 100%);
}
&::before{
background: linear-gradient(270deg, #CCCCCC 0%, rgba(204, 204, 204, 0) 100%);
}
}
// 底部操作栏
.footer {
display: flex;
align-items: center;
height: 96rpx;
background: #fff;
padding: 0 30rpx;
position: fixed;
bottom: 120rpx;
left: 0;
right: 0;
z-index: 11;
.all-radio {
width: 140rpx;
display: flex;
align-items: center;
color: #666;
font-size: 24rpx;
.radio {
margin-bottom: -4rpx;
transform: scale(0.76)
}
}
.total-info {
flex: 1;
display: flex;
align-items: center;
justify-content: flex-end;
padding-right: 30rpx;
font-size: 24rpx;
color: #666;
.goods-price {
vertical-align: bottom;
color: #FF512B;
.unit {
font-size: 24rpx;
}
.value {
font-size: 32rpx;
}
}
}
.cart-action {
width: 200rpx;
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 72rpx;
line-height: 72rpx;
text-align: center;
color: #fff;
border-radius: 50rpx;
}
// 立即购买按钮
.btn-main {
background: #FF512B;//linear-gradient(to right, #f9211c, #ff6335);
// 禁用按钮
&.disabled {
background: #ff9779;
}
}
}
}
</style>

@ -0,0 +1,653 @@
<template>
<view v-if="!isFirstload" class="container">
<!-- 页面头部 -->
<view class="main-header"
:style="{ height: $platform == 'H5' ? '240rpx' : '320rpx', paddingTop: $platform == 'H5' ? '0' : '50rpx' }">
<image class="bg-image" src="/static/background/user-header.png" mode="scaleToFill"></image>
<!-- 用户信息 -->
<view v-if="isLogin" class="user-info">
<view class="user-avatar">
<!-- <avatar-image :url="userInfo.avatar_url" :width="100" /> -->
</view>
<view class="user-content">
<!-- 会员昵称 -->
<view class="nick-name oneline-hide">{{ userInfo.nick_name }}</view>
<!-- 会员等级 -->
<view v-if="userInfo.grade_id > 0 && userInfo.grade" class="user-grade">
<view class="user-grade_icon">
<image class="image"
src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAA0lBMVEUAAAD/tjL/tzH/uDP/uC7/tjH/tzH/tzL/tTH+tTL+tjP/tDD/tTD+tzD/tjL/szD/uDH/tjL/tjL+tjD/tjT/szb/tzL/tTL+uTH+tjL/tjL/tjL/tTT/tjL/tjL+tjH/uTL/vDD/tjL/tjH/tzL9uS//tTL/nBr/sS7/tjH/ujL/szD/uTv+rzf/tzL+tzH+vDP+uzL+tjP+ry7+tDL9ki/7szf/sEX/tTL/tjL+tjL/tTH/tTT/tzH/tzL/tjP/sTX/uTP/wzX+rTn/vDX9vC8m8ckhAAAAOXRSTlMAlnAMB/vjxKWGMh0S6drMiVxPRkEY9PLy0ru0sKagmo5+dGtgVCMgBP716eXWyMGxqJGRe2o5KSmFNjaYAAABP0lEQVQ4y8XS13KDMBAF0AWDDe4t7r3ETu9lVxJgJ/n/X8rKAzHG5TE+Twz3zki7I/g/KXdghIbGJewrU4yzn08Ebgl6TuZzzuOC6W5es3HX6qsSz3NFShRU0MpucytDmOSpu3yULx3CA9RD1HjVedc0jSjqm6ZzhUjDsFDQhSp/OKj5GQvg0+ZCOixsbtDLAeTTOm/yGi8GyIphIVsgH737FEDV44LJa88IRKK/SetrwT9G/GUIr6vXjoy4GXn7+RboVXnghuSjaoGecwQxL2su3CwAKlO+QFoqxI4FMctHQhQd2OhxTu184jWUlI+rMTBTn1/IQcJHQ6GQdZ7pWiDaNdhTt330efISeiqYwQEzQpTlsURJLhzkEmpCPsERfeIUVyXr6MNuIyp5uziW6xURtt7hhGwzmMNJExfO4Bd9X0ZPqAxdNwAAAABJRU5ErkJggg==">
</image>
</view>
<view class="user-grade_name">
<text>{{ userInfo.grade.name }}</text>
</view>
</view>
<!-- 会员无等级时显示手机号 -->
<view v-else class="mobile">{{ userInfo.mobile }}</view>
</view>
</view>
<!-- 未登录 -->
<view v-else class="user-info" @click="handleLogin">
<view class="user-avatar">
<avatar-image :width="100" />
</view>
<view class="user-content">
<view class="nick-name">未登录</view>
<view class="login-tips">点击登录账号</view>
</view>
</view>
</view>
<!-- 我的钱包 -->
<view class="my-asset">
<view class="asset-left flex-box dis-flex flex-x-around">
<view class="asset-left-item" @click="onTargetWallet">
<view class="item-value dis-flex flex-x-center">
<text>{{ isLogin ? assets.balance : '--' }}</text>
</view>
<view class="item-name dis-flex flex-x-center">
<text>账户余额</text>
</view>
</view>
<view class="asset-left-item" @click="onTargetPoints">
<view class="item-value dis-flex flex-x-center">
<text>{{ isLogin ? assets.points : '--' }}</text>
</view>
<view class="item-name dis-flex flex-x-center">
<text>{{ setting[SettingKeyEnum.POINTS.value].points_name }}</text>
</view>
</view>
<view class="asset-left-item" @click="onTargetMyCoupon">
<view class="item-value dis-flex flex-x-center">
<text>{{ isLogin ? assets.coupon : '--' }}</text>
</view>
<view class="item-name dis-flex flex-x-center">
<text>优惠券</text>
</view>
</view>
</view>
<view class="asset-right">
<view class="asset-right-item" @click="onTargetWallet">
<view class="item-icon dis-flex flex-x-center">
<text class="iconfont icon-qianbao"></text>
</view>
<view class="item-name dis-flex flex-x-center">
<text>我的钱包</text>
</view>
</view>
</view>
</view>
<!-- 订单操作 -->
<view class="order-navbar">
<view class="order-navbar-item" v-for="(item, index) in orderNavbar" :key="index" @click="onTargetOrder(item)">
<view class="item-icon">
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
</view>
<view class="item-name">{{ item.name }}</view>
<view class="item-badge" v-if="item.count && item.count > 0">
<text v-if="item.count <= 99" class="text">{{ item.count }}</text>
<text v-else class="text">99+</text>
</view>
</view>
</view>
<!-- 我的服务 -->
<view class="my-service">
<view class="service-title">我的服务</view>
<view class="service-content clearfix">
<block v-for="(item, index) in service" :key="index">
<view v-if="item.type == 'link'" class="service-item" @click="handleService(item)">
<view class="item-icon">
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
</view>
<view class="item-name">{{ item.name }}</view>
<view class="item-badge" v-if="item.count && item.count > 0">
<text v-if="item.count <= 99" class="text">{{ item.count }}</text>
<text v-else class="text">99+</text>
</view>
</view>
<view v-if="item.type == 'button' && $platform == 'MP-WEIXIN'" class="service-item">
<button class="btn-normal" :open-type="item.openType">
<view class="item-icon">
<text class="iconfont" :class="[`icon-${item.icon}`]"></text>
</view>
<view class="item-name">{{ item.name }}</view>
</button>
</view>
</block>
</view>
</view>
<!-- 退出登录 -->
<view v-if="isLogin" class="my-logout">
<view class="logout-btn" @click="handleLogout()">
<text>退出登录</text>
</view>
</view>
</view>
</template>
<script>
// import store from '@/store'
// import AvatarImage from '@/components/avatar-image'
// import { setCartTabBadge } from '@/core/app'
import SettingKeyEnum from '@/common/enum/setting/Key'
// import SettingModel from '@/common/model/Setting'
// import * as UserApi from '@/api/user'
// import * as OrderApi from '@/api/order'
// import { checkLogin } from '@/core/app'
import userInfoData from '@/mock/userInfo.json';
import addressData from '@/mock/address.json';
import countData from '@/mock/todoCounts.json';
import sessingData from '@/mock/setting.json';
// 订单操作
const orderNavbar = [
{ id: 'all', name: '全部订单', icon: 'qpdingdan' },
{ id: 'payment', name: '待支付', icon: 'daifukuan', count: 0 },
{ id: 'delivery', name: '待发货', icon: 'daifahuo', count: 0 },
{ id: 'received', name: '待收货', icon: 'daishouhuo', count: 0 },
]
/**
* 我的服务
* id: 标识; name: 标题名称; icon: 图标; type 类型(link和button); url: 跳转的链接
*/
const service = [
{ id: 'address', name: '收货地址', icon: 'shouhuodizhi', type: 'link', url: 'pages/address/index' },
{ id: 'coupon', name: '领券中心', icon: 'lingquan', type: 'link', url: 'pages/coupon/index' },
{ id: 'myCoupon', name: '优惠券', icon: 'youhuiquan', type: 'link', url: 'pages/my-coupon/index' },
{ id: 'help', name: '我的帮助', icon: 'bangzhu', type: 'link', url: 'pages/help/index' },
{ id: 'contact', name: '在线客服', icon: 'kefu', type: 'button', openType: 'contact' },
{ id: 'points', name: '我的积分', icon: 'jifen', type: 'link', url: 'pages/points/log' },
{ id: 'refund', name: '退换/售后', icon: 'shouhou', type: 'link', url: 'pages/refund/index', count: 0 },
]
export default {
components: {
// AvatarImage
},
data() {
return {
// 枚举类
SettingKeyEnum,
// 当前运行的终端 (此处并不冗余,因为微信小程序端view层无法直接读取$platform)
$platform: this.$platform,
// 正在加载
isLoading: true,
// 首次加载
isFirstload: true,
// 是否已登录
isLogin: false,
// 系统设置
setting: {},
// 当前用户信息
userInfo: {},
// 账户资产
assets: { balance: '--', points: '--', coupon: '--' },
// 我的服务
service,
// 订单操作
orderNavbar,
// 当前用户待处理的订单数量
todoCounts: { payment: 0, deliver: 0, received: 0 }
}
},
/**
* 生命周期函数--监听页面显示
*/
onShow(options) {
this.onRefreshPage()
},
methods: {
// 刷新页面
onRefreshPage() {
// 更新购物车角标
// setCartTabBadge()
// 判断是否已登录
this.isLogin = true //checkLogin()
// 获取页面数据
this.getPageData()
},
// 获取页面数据
getPageData(callback) {
const app = this
app.isLoading = true;
app.getSetting();
app.getUserInfo();
app.getUserAssets();
app.getTodoCounts();
app.isFirstload = false
// Promise.all([app.getSetting(), app.getUserInfo(), app.getUserAssets(), app.getTodoCounts()])
// .then(result => {
// console.log('----')
// app.isFirstload = false
// // 初始化我的服务数据
app.initService()
// // 初始化订单操作数据
app.initOrderTabbar()
// // 执行回调函数
callback && callback()
// })
// .catch(err => console.log('catch', err))
// .finally(() => app.isLoading = false)
},
// 初始化我的服务数据
initService() {
const app = this
const newService = []
service.forEach(item => {
if (item.id === 'points') {
item.name = '我的' + app.setting[SettingKeyEnum.POINTS.value].points_name
}
// 数据角标
if (item.count != undefined) {
item.count = app.todoCounts[item.id]
}
newService.push(item)
})
app.service = newService
},
// 初始化订单操作数据
initOrderTabbar() {
const app = this
const newOrderNavbar = []
orderNavbar.forEach(item => {
if (item.count != undefined) {
item.count = app.todoCounts[item.id]
}
newOrderNavbar.push(item)
})
app.orderNavbar = newOrderNavbar
},
// 获取商城设置
getSetting() {
const app = this
app.setting = sessingData.data.setting
// return new Promise((resolve, reject) => {
// SettingModel.data()
// .then(setting => {
// app.setting = setting
// resolve(setting)
// }).catch(reject)
// })
},
// 获取当前用户信息
getUserInfo() {
const app = this
app.userInfo = userInfoData.data.userInfo;
// return new Promise((resolve, reject) => {
// !app.isLogin ? resolve(null) :
// UserApi.info({}, { load: app.isFirstload })
// .then(result => {
// app.userInfo = result.data.userInfo
// resolve(app.userInfo)
// })
// .catch(err => {
// if (err.result && err.result.status == 401) {
// app.isLogin = false
// resolve(null)
// } else {
// reject(err)
// }
// })
// })
},
// 获取账户资产
getUserAssets() {
const app = this
app.assets = addressData.data.assets;
// return new Promise((resolve, reject) => {
// !app.isLogin ? resolve(null) :
// UserApi.assets({}, { load: app.isFirstload })
// .then(result => {
// app.assets = result.data.assets
// resolve(app.assets)
// })
// .catch(err => {
// if (err.result && err.result.status == 401) {
// app.isLogin = false
// resolve(null)
// } else {
// reject(err)
// }
// })
// })
},
// 获取当前用户待处理的订单数量
getTodoCounts() {
const app = this
app.todoCounts = countData.data.counts
// return new Promise((resolve, reject) => {
// !app.isLogin ? resolve(null) :
// OrderApi.todoCounts({}, { load: app.isFirstload })
// .then(result => {
// app.todoCounts = result.data.counts
// resolve(app.todoCounts)
// })
// .catch(err => {
// if (err.result && err.result.status == 401) {
// app.isLogin = false
// resolve(null)
// } else {
// reject(err)
// }
// })
// })
},
// 跳转到登录页
handleLogin() {
!this.isLogin && this.$navTo('pages/login/index')
},
// 退出登录
handleLogout() {
const app = this
uni.showModal({
title: '友情提示',
content: '您确定要退出登录吗?',
success(res) {
if (res.confirm) {
// store.dispatch('Logout', {})
// .then(result => app.onRefreshPage())
}
}
})
},
// 跳转到钱包页面
onTargetWallet() {
this.$navTo('pages/wallet/index')
},
// 跳转到订单页
onTargetOrder(item) {
this.$navTo('pages/order/index', { dataType: item.id })
},
// 跳转到我的积分页面
onTargetPoints() {
this.$navTo('pages/points/log')
},
// 跳转到我的优惠券页
onTargetMyCoupon() {
this.$navTo('pages/my-coupon/index')
},
// 跳转到服务页面
handleService({ url }) {
this.$navTo(url)
},
},
/**
* 下拉刷新
*/
onPullDownRefresh() {
// 获取首页数据
this.getPageData(() => {
uni.stopPullDownRefresh()
})
},
}
</script>
<style lang="scss" scoped>
// 页面头部
.main-header {
background-color: #fff;
// background-image: url('/static/background/user-header.png');
position: relative;
width: 100%;
height: 280rpx;
background-size: 100% 100%;
overflow: hidden;
display: flex;
align-items: center;
// padding-top: 40rpx;
padding-left: 30rpx;
.bg-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 0;
}
.user-info {
display: flex;
height: 100rpx;
z-index: 1;
.user-content {
display: flex;
flex-direction: column;
justify-content: center;
margin-left: 30rpx;
color: #c59a46;
.nick-name {
font-size: 32rpx;
font-weight: bold;
max-width: 270rpx;
}
.mobile {
margin-top: 15rpx;
font-size: 26rpx;
}
.user-grade {
align-self: baseline;
display: flex;
align-items: center;
background: #3c3c3c;
margin-top: 12rpx;
border-radius: 10rpx;
padding: 4rpx 12rpx;
.user-grade_icon .image {
display: block;
width: 32rpx;
height: 32rpx;
}
.user-grade_name {
margin-left: 5rpx;
font-size: 24rpx;
color: #EEE0C3;
}
}
.login-tips {
margin-top: 12rpx;
font-size: 28rpx;
}
}
}
}
// 角标组件
.item-badge {
position: absolute;
top: 0;
right: 55rpx;
background: #fa2209;
color: #fff;
border-radius: 100%;
min-width: 38rpx;
height: 38rpx;
display: flex;
justify-content: center;
align-items: center;
padding: 1rpx;
font-size: 22rpx;
}
// 我的钱包
.my-asset {
display: flex;
background: #fff;
padding: 40rpx 0;
.asset-right {
width: 200rpx;
border-left: 1rpx solid #eee;
}
.asset-right-item {
text-align: center;
color: #545454;
.item-icon {
font-size: 40rpx;
}
.item-name {
margin-top: 10rpx;
}
.item-name text {
font-size: 25rpx;
}
}
.asset-left-item {
text-align: center;
color: #666;
padding: 0 42rpx;
.item-value {
font-size: 32rpx;
color: red;
}
.item-name {
margin-top: 6rpx;
}
.item-name {
font-size: 25rpx;
}
}
}
// 订单操作
.order-navbar {
display: flex;
margin: 20rpx auto 20rpx auto;
padding: 20rpx 0;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
font-size: 30rpx;
border-radius: 5rpx;
background: #fff;
&-item {
position: relative;
width: 25%;
.item-icon {
text-align: center;
margin: 0 auto;
padding: 10rpx 0;
color: #545454;
font-size: 40rpx;
}
.item-name {
font-size: 26rpx;
color: #545454;
text-align: center;
margin-right: 10rpx;
}
}
}
// 我的服务
.my-service {
margin: 22rpx auto 22rpx auto;
padding: 20rpx 0;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
border-radius: 5rpx;
background: #fff;
.service-title {
padding-left: 20rpx;
margin-bottom: 30rpx;
font-size: 28rpx;
}
.service-content {
// margin-bottom: -30rpx;
.service-item {
position: relative;
width: 25%;
float: left;
margin-bottom: 30rpx;
.item-icon {
text-align: center;
margin: 0 auto;
padding: 10rpx 0;
color: #ff3800;
font-size: 40rpx;
}
.item-name {
font-size: 26rpx;
color: #545454;
text-align: center;
margin-right: 10rpx;
}
}
}
}
// 退出登录
.my-logout {
display: flex;
justify-content: center;
margin-top: 50rpx;
.logout-btn {
width: 50%;
margin: 0 auto;
font-size: 26rpx;
color: #616161;
border-radius: 20rpx;
border: 1px solid #dcdcdc;
padding: 14rpx 0;
text-align: center;
}
}
</style>

@ -0,0 +1,136 @@
<!--
* @Author: ch
* @Date: 2022-03-22 15:36:46
* @LastEditors: ch
* @LastEditTime: 2022-03-24 18:05:59
* @Description: file content
-->
<template>
<view class="container">
<view class="header">
<image class="arrow" src="@/static/search/arrow.png" @click="$Router.back()"/>
</view>
<view class="logo">马士兵严选</view>
<u--form class="login">
<u-form-item>
<input placeholder="请输入手机号" v-model="phone" class="login--input" border="bottom" />
</u-form-item>
<u-form-item class="login--code">
<input placeholder="验证码" type="number" maxlength="6" class="login--input" v-model="code" border="bottom" />
<u-code ref="uCode" @change="codeChange" changeText="X秒后重新获取"
@start="sendStatus = true" @end="sendStatus = false" seconds="10"></u-code>
<text class="login--send-btn" :class="sendStatus && 'login--send-btn__disabled'"
@click="getCode">{{tips}}</text>
</u-form-item>
</u--form>
<u-button class="login--btn" @click="login">登录</u-button>
<label class="login--agreement">
<radio class="radio" :checked="checked" @click="checked = !checked" color="#FF875B"/>
同意<text class="link">《用户协议》</text>和<text class="link">《隐私协议》</text>,首次登陆将自动注册
</label>
</view>
</template>
<script>
import { IsPhone } from '@/common/utils';
export default {
data(){
return {
tips : '',
sendStatus : false,
checked: false,
phone : '',
code : ''
}
},
methods : {
codeChange(text) {
this.tips = text;
},
getCode() {
if(!this.$refs.uCode.canGetCode){
return false
}
if(!IsPhone(this.phone)){
uni.$u.toast('请输入正确手机号');
return false
}
// 模拟向后端请求验证码
uni.showLoading({
title: '正在获取验证码'
})
setTimeout(() => {
uni.hideLoading();
// 这里此提示会被this.start()方法中的提示覆盖
uni.$u.toast('验证码已发送');
// 通知验证码组件内部开始倒计时
this.$refs.uCode.start();
}, 2000);
},
login(){
}
}
}
</script>
<style lang="scss" scoped>
.header{
height: 88rpx;
padding-left: 40rpx;
display: flex;
align-items: center;
.arrow{
width: 14rpx;
height: 28rpx;
}
}
.logo{
font-size: 46rpx;
text-align: center;
margin: 25rpx 0 84rpx 0;
}
.login{
width: 650rpx;
margin: 0 auto;
&--input{
border-bottom: 2rpx solid #eee;
width: 650rpx;
height: 100rpx;
font-size: 32rpx;
}
&--code{
position: relative;
}
&--send-btn{
position: absolute;
right: 0;
color: #FF875B;
&__disabled{
opacity: .8;
}
}
&--btn{
width: 650rpx;
height: 88rpx;
border-radius: 45rpx;
font-size: 32rpx;
margin: 60rpx 50rpx 40rpx;
color: #fff;
background: linear-gradient(270deg, #FF875B 0%, #FF512B 100%);
}
&--agreement{
width: 650rpx;
padding: 0 50rpx;
color: #999;
font-size: 24rpx;
.radio{
transform: scale(60%);
}
.link{
color: #333;
}
}
}
</style>

@ -0,0 +1,20 @@
<!--
* @Author: ch
* @Date: 2022-03-22 16:13:00
* @LastEditors: ch
* @LastEditTime: 2022-03-22 16:17:04
* @Description: file content
-->
<template>
<view>
</view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,876 @@
<!--
* @Author: ch
* @Date: 2022-03-22 11:17:55
* @LastEditors: ch
* @LastEditTime: 2022-03-22 14:04:38
* @Description: file content
-->
<template>
<view v-if="!isLoading" class="container">
<view class="header">
<!-- 订单状态 -->
<view class="order-status">
<view class="status-icon">
<!-- 进行中的订单 -->
<block v-if="order.order_status == OrderStatusEnum.NORMAL.value">
<!-- 待支付 -->
<block v-if="order.pay_status == PayStatusEnum.PENDING.value">
<image class="image" src="/static/order/status/wait_pay.png" mode="aspectFit"></image>
</block>
<!-- 待发货 -->
<block v-else-if="order.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<image class="image" src="/static/order/status/wait_deliver.png" mode="aspectFit"></image>
</block>
<!-- 待收货 -->
<block v-else-if="order.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<image class="image" src="/static/order/status/wait_receipt.png" mode="aspectFit"></image>
</block>
</block>
<!-- 已完成 -->
<block v-if="order.order_status == OrderStatusEnum.COMPLETED.value">
<image class="image" src="/static/order/status/received.png" mode="aspectFit"></image>
</block>
<!-- 已取消/待取消 -->
<block
v-if="order.order_status == OrderStatusEnum.CANCELLED.value || order.order_status == OrderStatusEnum.APPLY_CANCEL.value">
<image class="image" src="/static/order/status/close.png" mode="aspectFit"></image>
</block>
</view>
<view class="status-text">
<text>{{ order.state_text }}</text>
</view>
</view>
<!-- 下一步操作 -->
<view class="next-action" v-if="order.order_status == OrderStatusEnum.NORMAL.value">
<view v-if="order.pay_status == PayStatusEnum.PENDING.value" class="action-btn" @click="onPay()">去支付</view>
<view
v-if="order.delivery_status == DeliveryStatusEnum.DELIVERED.value && order.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value"
class="action-btn" @click="onReceipt(order.order_id)">确认收货</view>
</view>
</view>
<!-- 快递配送:配送地址 -->
<view class="delivery-address i-card">
<view class="link-man">
<text class="name">{{ order.address.name }}</text>
<text class="phone">{{ order.address.phone }}</text>
</view>
<view class="address">
<text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
<text class="detail">{{ order.address.detail }}</text>
</view>
</view>
<!-- 物流信息 -->
<view
v-if="order.delivery_type == DeliveryTypeEnum.EXPRESS.value && order.delivery_status == DeliveryStatusEnum.DELIVERED.value"
class="express i-card" @click="handleTargetExpress()">
<view class="main">
<view class="info-item">
<view class="item-lable">物流公司</view>
<view class="item-content">
<text>{{ order.express.express_name }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">物流单号</view>
<view class="item-content">
<text>{{ order.express_no }}</text>
<view class="act-copy" @click.stop="handleCopy(order.express_no)">
<text>复制</text>
</view>
</view>
</view>
</view>
<view class="right-arrow">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 商品列表 -->
<view class="goods-list i-card">
<view class="goods-item" v-for="(goods, idx) in order.goods" :key="idx">
<view class="goods-main" @click="handleTargetGoods(goods.goods_id)">
<!-- 商品图片 -->
<view class="goods-image">
<image class="image" :src="goods.goods_image" mode="scaleToFill"></image>
</view>
<!-- 商品信息 -->
<view class="goods-content">
<view class="goods-title twoline-hide"><text>{{ goods.goods_name }}</text></view>
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in goods.goods_props" :key="idx">
<text>{{ props.value.name }}</text>
</view>
</view>
</view>
<!-- 交易信息 -->
<view class="goods-trade">
<view class="goods-price">
<text class="unit">¥</text>
<text class="value">{{ goods.is_user_grade ? goods.grade_goods_price : goods.goods_price }}</text>
</view>
<view class="goods-num">
<text>×{{ goods.total_num }}</text>
</view>
</view>
</view>
<!-- 商品售后 -->
<view class="goods-refund">
<text v-if="goods.refund" class="stata-text">已申请售后</text>
<view v-else-if="order.isAllowRefund" class="action-btn"
@click.stop="handleApplyRefund(goods.order_goods_id)">申请售后</view>
</view>
</view>
</view>
<!-- 订单信息 -->
<view class="order-info i-card">
<view class="info-item">
<view class="item-lable">订单编号</view>
<view class="item-content">
<text>{{ order.order_no }}</text>
<view class="act-copy" @click="handleCopy(order.order_no)">
<text>复制</text>
</view>
</view>
</view>
<view class="info-item">
<view class="item-lable">下单时间</view>
<view class="item-content">
<text>{{ order.create_time }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">买家留言</view>
<view class="item-content">
<text>{{ order.buyer_remark ? order.buyer_remark : '--' }}</text>
</view>
</view>
</view>
<!-- 结算信息 -->
<view class="trade-info i-card">
<view class="info-item">
<view class="item-lable">订单金额</view>
<view class="item-content">
<text>¥{{ order.total_price }}</text>
</view>
</view>
<view v-if="order.coupon_money > 0" class="info-item">
<view class="item-lable">优惠券抵扣</view>
<view class="item-content">
<text>-¥{{ order.coupon_money }}</text>
</view>
</view>
<view v-if="order.points_money > 0" class="info-item">
<view class="item-lable">{{ setting.points_name }}抵扣</view>
<view class="item-content">
<text>-¥{{ order.points_money }}</text>
</view>
</view>
<view class="info-item">
<view class="item-lable">运费</view>
<view class="item-content">
<text>+¥{{ order.express_price }}</text>
</view>
</view>
<view v-if="order.update_price.value != '0.00'" class="info-item">
<view class="item-lable">后台改价</view>
<view class="item-content">
<text>{{ order.update_price.symbol }}</text>
<text>¥{{ order.update_price.value }}</text>
</view>
</view>
<view class="divider"></view>
<view class="trade-total">
<text class="lable">实付款</text>
<view class="goods-price">
<text class="unit">¥</text>
<text class="value">{{ order.pay_price }}</text>
</view>
</view>
</view>
<!-- 底部操作按钮 -->
<view v-if="order.order_status != OrderStatusEnum.CANCELLED.value" class="footer-fixed">
<view class="btn-wrapper">
<!-- 未支付取消订单 -->
<block v-if="order.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item" @click="onCancel(order.order_id)">取消</view>
</block>
<!-- 已支付进行中的订单 -->
<block v-if="order.order_status != OrderStatusEnum.APPLY_CANCEL.value">
<block
v-if="order.pay_status == PayStatusEnum.SUCCESS.value && order.delivery_status == DeliveryStatusEnum.NOT_DELIVERED.value">
<view class="btn-item" @click="onCancel(order.order_id)">申请取消</view>
</block>
</block>
<!-- 已申请取消 -->
<view v-else class="f-28 col-8">取消申请中</view>
<!-- 未支付的订单 -->
<block v-if="order.pay_status == PayStatusEnum.PENDING.value">
<view class="btn-item active" @click="onPay()">去支付</view>
</block>
<!-- 确认收货 -->
<block
v-if="order.delivery_status == DeliveryStatusEnum.DELIVERED.value && order.receipt_status == ReceiptStatusEnum.NOT_RECEIVED.value">
<view class="btn-item active" @click="onReceipt(order.order_id)">确认收货</view>
</block>
<!-- 订单评价 -->
<block v-if="order.order_status == OrderStatusEnum.COMPLETED.value && order.is_comment == 0">
<view class="btn-item" @click="handleTargetComment(order.order_id)">评价</view>
</block>
</view>
</view>
<!-- 支付方式弹窗 -->
<u-popup v-model="showPayPopup" mode="bottom" border-radius="26" :closeable="true">
<view class="pay-popup">
<view class="title">请选择支付方式</view>
<view class="pop-content">
<!-- 微信支付 -->
<!-- #ifdef MP-WEIXIN -->
<view class="pay-item dis-flex flex-x-between" @click="onSelectPayType(PayTypeEnum.WECHAT.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon wechat">
<text class="iconfont icon-wxpay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.WECHAT.name }}</text>
</view>
</view>
</view>
<!-- #endif -->
<!-- 余额支付 -->
<view class="pay-item dis-flex flex-x-between" @click="onSelectPayType(PayTypeEnum.BALANCE.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon balance">
<text class="iconfont icon-qiandai"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.BALANCE.name }}</text>
</view>
</view>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import {
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum
} from '@/common/enum/order'
// import * as OrderApi from '@/api/order'
// import { wxPayment } from '@/core/app'
export default {
data() {
return {
// 枚举类
DeliveryStatusEnum,
DeliveryTypeEnum,
OrderStatusEnum,
PayStatusEnum,
PayTypeEnum,
ReceiptStatusEnum,
// 当前订单ID
orderId: null,
// 加载中
isLoading: true,
// 当前订单详情
order: {},
// 当前设置
setting: {},
// 支付方式弹窗
showPayPopup: false
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad({ orderId }) {
// 当前订单ID
this.orderId = orderId
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 获取当前订单信息
this.getOrderDetail()
},
methods: {
// 获取当前订单信息
getOrderDetail() {
const app = this
app.isLoading = true
OrderApi.detail(app.orderId)
.then(result => {
app.order = result.data.order
app.setting = result.data.setting
app.isLoading = false
})
},
// 复制指定内容
handleCopy(value) {
const app = this
uni.setClipboardData({
data: value,
success() {
app.$toast('复制成功')
}
})
},
// 跳转到物流跟踪页面
handleTargetExpress() {
this.$navTo('pages/order/express/index', { orderId: this.orderId })
},
// 跳转到商品详情页面
handleTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
// 跳转到申请售后页面
handleApplyRefund(orderGoodsId) {
this.$navTo('pages/refund/apply', { orderGoodsId })
},
// 取消订单
onCancel(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认要取消该订单吗?',
success(o) {
if (o.confirm) {
OrderApi.cancel(orderId)
.then(result => {
// 显示成功信息
app.$toast(result.message)
// 刷新当前订单数据
app.getOrderDetail()
})
}
}
});
},
// 确认收货
onReceipt(orderId) {
const app = this
uni.showModal({
title: '友情提示',
content: '确认收到商品了吗?',
success(o) {
if (o.confirm) {
OrderApi.receipt(orderId)
.then(result => {
// 显示成功信息
app.$success(result.message)
// 刷新当前订单数据
app.getOrderDetail()
})
}
}
});
},
// 点击去支付
onPay() {
// 显示支付方式弹窗
this.showPayPopup = true
},
// 选择支付方式
onSelectPayType(payType) {
const app = this
// 隐藏支付方式弹窗
this.showPayPopup = false
// 发起支付请求
OrderApi.pay(app.orderId, payType)
.then(result => app.onSubmitCallback(result))
.catch(err => err)
},
// 订单提交成功后回调
onSubmitCallback(result) {
const app = this
// 发起微信支付
if (result.data.pay_type == PayTypeEnum.WECHAT.value) {
wxPayment(result.data.payment)
.then(() => {
app.$success('支付成功')
setTimeout(() => {
app.getOrderDetail()
}, 1500)
})
.catch(err => {
app.$error('订单未支付')
})
.finally(() => {
app.disabled = false
})
}
// 余额支付
if (result.data.pay_type == PayTypeEnum.BALANCE.value) {
app.$success('支付成功')
app.disabled = false
setTimeout(() => {
// 刷新当前订单数据
app.getOrderDetail()
}, 1500)
}
},
// 跳转到订单评价页
handleTargetComment(orderId) {
this.$navTo('pages/order/comment/index', { orderId })
},
},
}
</script>
<style>
page {
background: #f4f4f4;
}
</style>
<style lang="scss" scoped>
.container {
padding-bottom: constant(env(safe-area-inset-bottom) + 106rpx + 6rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 106rpx + 6rpx);
}
// 页面顶部
.header {
display: flex;
justify-content: space-between;
background-color: #e8c269;
height: 280rpx;
padding: 56rpx 30rpx 0 30rpx;
.order-status {
display: flex;
align-items: center;
height: 128rpx;
.status-icon {
width: 128rpx;
height: 128rpx;
.image {
display: block;
width: 100%;
height: 100%;
}
}
.status-text {
padding-left: 20rpx;
color: #fff;
font-size: 38rpx;
font-weight: bold;
}
}
.next-action {
display: flex;
align-items: center;
height: 128rpx;
.action-btn {
min-width: 152rpx;
height: 56rpx;
padding: 0 30rpx;
line-height: 56rpx;
background-color: #fff;
text-align: center;
border-radius: 28rpx;
border-color: rgb(102, 102, 102);
cursor: pointer;
user-select: none;
color: #c7a157;
}
}
}
// 通栏卡片
.i-card {
background: #fff;
padding: 24rpx 24rpx;
width: 94%;
box-shadow: 0 1rpx 5rpx 0px rgba(0, 0, 0, 0.05);
margin: 0 auto 20rpx auto;
border-radius: 20rpx;
}
// 收货地址
.delivery-address {
margin-top: -50rpx;
.link-man {
line-height: 46rpx;
color: #333;
.name {
margin-right: 10rpx;
}
}
.address {
margin-top: 12rpx;
color: #999;
font-size: 24rpx;
.detail {
margin-left: 6rpx;
}
}
}
// 物流公司
.express {
display: flex;
align-items: center;
.main {
flex: 1;
}
.info-item {
display: flex;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.item-lable {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
margin-right: 30rpx;
}
.item-content {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.act-copy {
margin-left: 20rpx;
padding: 2rpx 20rpx;
font-size: 22rpx;
color: #666;
border: 1rpx solid #c1c1c1;
border-radius: 16rpx;
}
}
}
// 右侧箭头
.right-arrow {
margin-left: 16rpx;
// color: #777;
font-size: 26rpx;
}
}
// 商品列表
.goods-list {
// 商品项
.goods-item {
margin-bottom: 40rpx;
&:last-child {
margin-bottom: 0;
}
// 商品信息
.goods-main {
display: flex;
}
// 商品图片
.goods-image {
width: 180rpx;
height: 180rpx;
.image {
display: block;
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
// 商品内容
.goods-content {
flex: 1;
padding-left: 16rpx;
padding-top: 16rpx;
.goods-title {
font-size: 26rpx;
max-height: 76rpx;
}
.goods-props {
margin-top: 14rpx;
height: 40rpx;
color: #ababab;
font-size: 24rpx;
overflow: hidden;
.goods-props-item {
display: inline-block;
margin-right: 14rpx;
padding: 4rpx 16rpx;
border-radius: 12rpx;
background-color: #F5F5F5;
width: auto;
}
}
}
// 交易信息
.goods-trade {
padding-top: 16rpx;
width: 150rpx;
text-align: right;
color: $uni-text-color-grey;
font-size: 26rpx;
.goods-price {
vertical-align: bottom;
margin-bottom: 16rpx;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
// 商品售后
.goods-refund {
display: flex;
justify-content: flex-end;
.stata-text {
font-size: 24rpx;
color: #999;
}
.action-btn {
border-radius: 28rpx;
padding: 8rpx 26rpx;
font-size: 24rpx;
color: #383838;
border: 1rpx solid #a8a8a8;
}
}
}
}
// 订单信息
.order-info {
.info-item {
display: flex;
margin-bottom: 24rpx;
&:last-child {
margin-bottom: 0;
}
.item-lable {
display: flex;
align-items: center;
font-size: 24rpx;
color: #999;
margin-right: 30rpx;
}
.item-content {
flex: 1;
display: flex;
align-items: center;
font-size: 26rpx;
color: #333;
.act-copy {
margin-left: 20rpx;
padding: 2rpx 20rpx;
font-size: 22rpx;
color: #666;
border: 1rpx solid #c1c1c1;
border-radius: 16rpx;
}
}
}
}
// 交易信息
.trade-info {
.info-item {
display: flex;
margin-bottom: 24rpx;
.item-lable {
font-size: 24rpx;
color: #999;
margin-right: 24rpx;
}
.item-content {
flex: 1;
font-size: 26rpx;
color: #333;
text-align: right;
}
}
.divider {
height: 1rpx;
background: #f1f1f1;
margin-bottom: 24rpx;
}
.trade-total {
display: flex;
justify-content: flex-end;
.goods-price {
margin-left: 12rpx;
vertical-align: bottom;
color: $uni-text-color-active;
.unit {
margin-right: -2rpx;
font-size: 24rpx;
}
}
}
}
/* 底部操作栏 */
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// 设置ios刘海屏底部横线安全区域
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
.btn-wrapper {
height: 106rpx;
display: flex;
align-items: center;
justify-content: flex-end;
padding: 0 30rpx;
}
.btn-item {
min-width: 164rpx;
border-radius: 28rpx;
padding: 10rpx 24rpx;
font-size: 28rpx;
color: #383838;
text-align: center;
border: 1rpx solid #a8a8a8;
margin-left: 24rpx;
&.active {
color: #fff;
border: none;
background: linear-gradient(to right, #f9211c, #ff6335);
}
}
}
// 弹出层-支付方式
.pay-popup {
padding: 24rpx;
.title {
font-size: 30rpx;
margin-bottom: 50rpx;
font-weight: bold;
text-align: center;
}
.pop-content {
min-height: 260rpx;
padding: 0 10rpx;
.pay-item {
padding: 20rpx 35rpx;
font-size: 28rpx;
border-bottom: 1rpx solid #f1f1f1;
&:last-child {
border-bottom: none;
}
.item-left_icon {
margin-right: 20rpx;
font-size: 32rpx;
&.wechat {
color: #00c800;
}
&.balance {
color: #ff9700;
}
}
}
}
}
//
</style>

@ -0,0 +1,18 @@
<!--
* @Author: ch
* @Date: 2022-03-22 10:58:24
* @LastEditors: ch
* @LastEditTime: 2022-03-22 11:15:01
* @Description: file content
-->
<template>
<view></view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,446 @@
.dis-flex{
display: flex;
}
// 配送信息
.flow-delivery {
padding: 34rpx 30rpx;
background: #fff url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANYAAAANCAYAAADVGpDCAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDIxIDc5LjE1NTc3MiwgMjAxNC8wMS8xMy0xOTo0NDowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo3Yjk4M2ExYy1jMDhkLTQ1OTktYTI0Ny1kZjNjYzdiYTQ5ZTgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDQwNkY3RkU5N0NGMTFFNUI3N0M4NTU4MzM2RjlFODIiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDQwNkY3RkQ5N0NGMTFFNUI3N0M4NTU4MzM2RjlFODIiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTQgKE1hY2ludG9zaCkiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowNzgwZWI1NS03OGFhLTQzOTUtODQ4OC1lOWI5YmVlYTY1ZDciIHN0UmVmOmRvY3VtZW50SUQ9ImFkb2JlOmRvY2lkOnBob3Rvc2hvcDo1OTRiYzUyMy1jMzc3LTExNzgtYTdkZS04NGY3YmM1ZGIxMDMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz556PLxAAACBElEQVR42tyaSyhEYRTHP48imlKibDQeSSlkSlEWLCRFsZNH5FE2FqQ8ErIRC9lIkTwXSpMkWWChhEJCSnlkoUZGSsr78f98n43CMFPu/Z/6NZuZ2zn33/+cb869XkmLx8IDEQaGQJbgiytQDSY3MyL+LYnL/HxPXSoHDIJQQq2WQQk4Dbbb/yUB29LJ+6e3B66VB3ZITbUIEqSpCGoJBP1ghtBUD6ARpEtTGSEhXzd+awE9oJzQUPegWdf3QlBPMhgDMYRa7YNisGWkpP5qrBQtVBShUHugUE9hs4fUtwG0utlEjRivoA/Ug1sj3vjffr8FNJEK1auPFHcE9UTq5pdK2PwcoAzMG7mjuRrRYEIfK9jiDJSCBZJ6ynSTsBBqNQ0qgdPISbq6vJCFbJOaagrEk5gqWNczRGiqG1Ah1LLMafRkf5pYIUKtZnMJDXUNasAIST2ZYFioRx9ssQaKwJFZEv5uYmWDXVJTrYBEElP562PfPKGpnkAbSDOTqb6aWAGgW6iHol5kQj2CdtAJngnqkc1hHMQRNr9DPaXWzZj8Z2PZtFCxhEIdaKE2CGqRJ4060AH8CLUaALX6f5VpBZLhI9SaeZXQVHKNLt84SCIxVbhQi5YuQlNd6OVElZlN9TGxrGBUn2PZ4lyoTdIsST0FQj0UDSLUak6ot3gcBLVY3wQYAJoVXxmNERajAAAAAElFTkSuQmCC') bottom left repeat-x;
background-size: 120rpx auto;
.detail-location {
font-size: 36rpx;
}
.detail-content {
padding: 0 20rpx;
.detail-content__title-phone {
margin-left: 10rpx;
}
.detail-content__describe {
font-size: 28rpx;
color: #777;
}
}
.detail-content__title {
margin-bottom: 6rpx;
}
}
// 买家留言
.flow-all-money {
.ipt-wrapper {
input {
font-size: 28rpx;
width: 100%;
height: 75rpx;
}
}
}
// 商品列表
.checkout_list {
padding: 20rpx 30rpx 4rpx 30rpx;
background: #fff;
border-bottom: 1rpx solid rgb(248, 248, 248);
.flow-shopList {
padding: 5rpx 0 10rpx;
border-bottom: 1rpx solid rgb(248, 248, 248);
&:last-child {
border-bottom: 0;
}
}
}
.flow-header-left {
padding-left: 90rpx;
}
/* 会员价 */
.flow-shopList {
.flow-list-right {
.flow-cont {
&.price-delete {
font-size: 26rpx;
color: #777;
text-decoration: line-through;
}
}
}
.grade-price {
padding-top: 8rpx;
font-size: 28rpx;
color: #ff495e;
text-align: right;
}
.goods-name{
font-size: 28rpx;
color: #333;
}
}
/* 优惠券选择 */
.popup__coupon {
width: 750rpx;
background: #fff;
box-sizing: border-box;
padding: 30rpx;
.coupon__do_not {
.control {
width: 90%;
height: 72rpx;
margin-bottom: 24rpx;
color: #888;
border: 1rpx solid #e3e3e3;
border-radius: 10rpx;
/* #ifdef H5 */
max-width: 1120rpx;
/* #endif */
}
}
.coupon__title {
text-align: center;
margin-bottom: 30rpx;
}
.coupon-list {
/* #ifdef H5 */
max-width: 1120rpx;
margin: 0 auto;
/* #endif */
}
.coupon-item {
overflow: hidden;
margin-bottom: 22rpx;
}
.item-wrapper {
width: 100%;
display: flex;
background: #fff;
border-radius: 8rpx;
color: #fff;
height: 180rpx;
.coupon-type {
position: absolute;
top: 0;
right: 0;
z-index: 10;
width: 128rpx;
padding: 6rpx 0;
background: #a771ff;
font-size: 20rpx;
text-align: center;
color: #ffffff;
transform: rotate(45deg);
transform-origin: 64rpx 64rpx;
}
&.color-blue {
background: linear-gradient(-125deg, #57bdbf, #2f9de2);
}
&.color-red {
background: linear-gradient(-128deg, #ff6d6d, #ff3636);
}
&.color-violet {
background: linear-gradient(-113deg, #ef86ff, #b66ff5);
.coupon-type {
background: #55b5ff;
}
}
&.color-yellow {
background: linear-gradient(-141deg, #f7d059, #fdb054);
}
&.color-gray {
background: linear-gradient(-113deg, #bdbdbd, #a2a1a2);
.coupon-type {
background: #9e9e9e;
}
}
.content {
flex: 1;
padding: 30rpx 20rpx;
border-radius: 16rpx 0 0 16rpx;
.title {
font-size: 32rpx;
}
.bottom {
.time {
font-size: 24rpx;
}
.receive {
height: 46rpx;
width: 122rpx;
line-height: 46rpx;
text-align: center;
border: 1rpx solid #fff;
border-radius: 30rpx;
color: #fff;
font-size: 24rpx;
&.state {
border: none;
}
}
}
}
.tip {
position: relative;
flex: 0 0 32%;
text-align: center;
border-radius: 0 16rpx 16rpx 0;
.money {
font-weight: bold;
font-size: 52rpx;
}
.pay-line {
font-size: 22rpx;
}
}
.split-line {
position: relative;
flex: 0 0 0;
border-left: 4rpx solid #fff;
margin: 0 10rpx 0 6rpx;
background: #fff;
&:before,
{
border-radius: 0 0 16rpx 16rpx;
top: 0;
}
&:after {
border-radius: 16rpx 16rpx 0 0;
bottom: 0;
}
&:before,
&:after {
content: '';
position: absolute;
width: 24rpx;
height: 12rpx;
background: #f7f7f7;
left: -14rpx;
z-index: 1;
}
}
}
}
/* 积分抵扣 */
.points {
.title {
margin-right: 5rpx;
}
.icon-help {
font-size: 28rpx;
}
.points-money {
margin-right: 20rpx;
}
}
/* 支付方式 */
.pay-method {
.pay-item {
padding: 20rpx 0;
font-size: 28rpx;
border-bottom: 1rpx solid rgb(248, 248, 248);
.item-left_icon {
margin-right: 20rpx;
font-size: 32rpx;
&.wechat {
color: #00c800;
}
&.balance {
color: #ff9700;
}
}
}
.user-balance {
margin-left: 20rpx;
font-size: 26rpx;
// color: #464646;
}
}
// 商品规格
.goods-props {
padding-top: 10rpx;
font-size: 24rpx;
color: #999;
.goods-props-item {
float: left;
.group-name {
margin-right: 6rpx;
}
}
}
// 右侧箭头
.right-arrow {
margin-left: 16rpx;
// color: #777;
font-size: 26rpx;
}
// 底部操作栏
.flow-fixed-footer {
position: fixed;
bottom: var(--window-bottom);
left: var(--window-left);
right: var(--window-right);
// width: 100%;
background: #fff;
border-top: 1px solid #eee;
z-index: 11;
.chackout-left {
font-size: 28rpx;
line-height: 92rpx;
color: #777;
flex: 4;
padding-left: 12px;
}
.chackout-right {
font-size: 34rpx;
flex: 2;
}
// 提交按钮
.flow-btn {
background: linear-gradient(to right, #f9211c, #ff6335);
color: #fff;
text-align: center;
line-height: 92rpx;
display: block;
font-size: 28rpx;
// 禁用按钮
&.disabled {
background: #ff9779;
}
}
}
// 积分说明
.points-content {
padding: 30rpx 48rpx;
font-size: 28rpx;
line-height: 50rpx;
text-align: left;
color: #606266;
height: 620rpx;
box-sizing: border-box;
}
/* 共几件商品 */
.flow-num-box {
font-size: 28rpx;
color: #777;
padding: 16rpx 24rpx;
text-align: right;
}
/* app.scss */
.flow-shopList {
padding: 18rpx 0;
.flow-list-left {
margin-right: 20rpx;
image {
width: 180rpx;
height: 180rpx;
border: 1rpx solid #eee;
background: #fff;
}
}
.flow-list-right {
.flow-cont {
font-size: 28rpx;
color: #fa2209;
}
.small {
font-size: 26rpx;
color: #777;
}
.flow-list-cont {
padding-top: 10rpx;
}
}
}
.flow-all-money {
padding: 0 24rpx;
color: #444;
.flow-all-list {
font-size: 28rpx;
padding: 20rpx 0;
border-bottom: 1rpx solid rgb(248, 248, 248);
}
.flow-all-list:last-child {
border-bottom: none;
}
.flow-all-list-cont {
font-size: 28rpx;
padding: 10rpx 0;
}
.flow-arrow {
justify-content: flex-end;
align-items: center;
}
}

@ -0,0 +1,532 @@
<!--
* @Author: ch
* @Date: 2022-03-20 14:14:53
* @LastEditors: ch
* @LastEditTime: 2022-03-20 14:59:53
* @Description: file content
-->
<template>
<view class="container p-bottom" v-if="order.goodsList.length">
<!-- 快递配送:配送地址 -->
<view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" @click="onSelectAddress" class="flow-delivery">
<view class="flow-delivery__detail dis-flex flex-y-center">
<view class="detail-location dis-flex">
<text class="iconfont icon-dingwei"></text>
</view>
<view class="detail-content flex-box">
<block v-if="order.address">
<view class="detail-content__title dis-flex">
<text class="f-30">{{ order.address.name }}</text>
<text class="detail-content__title-phone f-28">{{ order.address.phone }}</text>
</view>
<view class="address detail-content__describe">
<text class="region" v-for="(region, idx) in order.address.region" :key="idx">{{ region }}</text>
<text class="detail">{{ order.address.detail }}</text>
</view>
</block>
<block v-else>
<view class="detail-content__describe dis-flex">
<text class="col-6">请选择配送地址</text>
</view>
</block>
</view>
<view class="detail-arrow dis-flex">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 商品列表 -->
<view class="m-top20">
<view class="checkout_list" v-for="(item, index) in order.goodsList" :key="index">
<view class="flow-shopList dis-flex" data-index="index" @click="onTargetGoods(item.goods_id)">
<!-- 商品图片 -->
<view class="flow-list-left">
<image mode="scaleToFill" :src="item.goods_image"></image>
</view>
<view class="flow-list-right flex-box">
<!-- 商品名称 -->
<text class="goods-name twoline-hide">{{ item.goods_name }}</text>
<!-- 商品规格 -->
<view class="goods-props clearfix">
<view class="goods-props-item" v-for="(props, idx) in item.skuInfo.goods_props" :key="idx">
<text class="group-name">{{ props.group.name }}: </text>
<text>{{ props.value.name }};</text>
</view>
</view>
<!-- 商品数量和单价 -->
<view class="flow-list-cont dis-flex flex-x-between flex-y-center">
<text class="small">×{{ item.total_num }}</text>
<text class="flow-cont" :class="[item.is_user_grade ? 'price-delete' : '']">¥{{ item.goods_price }}</text>
</view>
<!-- 会员折扣价 -->
<view v-if="item.is_user_grade" class="grade-price">
<text>会员折扣价:¥{{ item.grade_goods_price }}</text>
</view>
</view>
</view>
</view>
<view class="flow-num-box b-f">
<text>共{{ order.orderTotalNum }}件商品,合计:</text>
<text class="flow-money col-m">¥{{ order.orderTotalPrice }}</text>
</view>
</view>
<!-- 商品金额 -->
<view class="flow-all-money b-f m-top20">
<view class="flow-all-list dis-flex">
<text class="flex-five">订单总金额:</text>
<view class="flex-five t-r">
<text class="col-m">¥{{ order.orderTotalPrice }}</text>
</view>
</view>
<!-- 优惠券 -->
<view class="flow-all-list dis-flex">
<text class="flex-five">优惠券:</text>
<view class="flex-five t-r">
<view v-if="order.couponList.length > 0" @click="handleShowPopup()">
<text class="col-m" v-if="order.couponId > 0">-¥{{ order.couponMoney }}</text>
<text class="col-m" v-else>有{{ order.couponList.length }}张优惠券</text>
<text class="right-arrow iconfont icon-arrow-right"></text>
</view>
<text v-else class="">无优惠券可用</text>
</view>
</view>
<!-- 积分抵扣 -->
<view v-if="order.isAllowPoints" class="points flow-all-list dis-flex flex-y-center">
<view class="block-left flex-five" @click="handleShowPoints()">
<text class="title">可用{{ setting.points_name }}抵扣:</text>
<text class="iconfont icon-help"></text>
</view>
<view class="flex-five dis-flex flex-x-end flex-y-center">
<text class="points-money col-m">-¥{{ order.pointsMoney }}</text>
<u-switch v-model="isUsePoints" size="48" active-color="#07c160" @change="getOrderData()"></u-switch>
</view>
</view>
<!-- 配送费用 -->
<view v-if="curDelivery == DeliveryTypeEnum.EXPRESS.value" class="dis-flex flow-all-list">
<text class="flex-five">配送费用:</text>
<view class="flex-five t-r">
<view v-if="order.address">
<text class="col-m" v-if="order.isIntraRegion">+¥{{ order.expressPrice }}</text>
<text v-else>不在配送范围</text>
</view>
<view v-else>
<text class="col-7">请先选择配送地址</text>
</view>
</view>
</view>
</view>
<!-- 支付方式 -->
<view class="pay-method flow-all-money b-f m-top20">
<view class="flow-all-list dis-flex">
<text class="flex-five">支付方式</text>
</view>
<!-- 微信支付 -->
<!-- #ifdef MP-WEIXIN -->
<view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.WECHAT.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon wechat">
<text class="iconfont icon-wxpay"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.WECHAT.name }}</text>
</view>
</view>
<view class="item-right col-m" v-if="curPayType == PayTypeEnum.WECHAT.value">
<text class="iconfont icon-check"></text>
</view>
</view>
<!-- #endif -->
<!-- 余额支付 -->
<view class="pay-item dis-flex flex-x-between" @click="handleSelectPayType(PayTypeEnum.BALANCE.value)">
<view class="item-left dis-flex flex-y-center">
<view class="item-left_icon balance">
<text class="iconfont icon-qiandai"></text>
</view>
<view class="item-left_text">
<text>{{ PayTypeEnum.BALANCE.name }}</text>
</view>
<view class="user-balance">
<text>(可用¥{{ personal.balance }}元)</text>
</view>
</view>
<view class="item-right col-m" v-if="curPayType == PayTypeEnum.BALANCE.value">
<text class="iconfont icon-check"></text>
</view>
</view>
</view>
<!-- 买家留言 -->
<view class="flow-all-money b-f m-top20">
<view class="ipt-wrapper dis-flex flow-all-list">
<input v-model="remark" placeholder="选填:买家留言(50字以内)"></input>
</view>
</view>
<!-- 提交订单 -->
<view class="flow-fixed-footer b-f m-top10">
<view class="dis-flex chackout-box">
<view class="chackout-left pl-12">实付款:
<text class="col-m">¥{{ order.orderPayPrice }}</text>
</view>
<view class="chackout-right" @click="onSubmitOrder()">
<view class="flow-btn f-32" :class="{ disabled }">提交订单</view>
</view>
</view>
</view>
<!-- 积分说明弹窗 -->
<u-modal v-model="showPoints" :title="`${setting.points_name}说明`">
<scroll-view class="points-content" :scroll-y="true">
<text>{{ setting.points_describe }}</text>
</scroll-view>
</u-modal>
<!-- 优惠券弹出框 -->
<u-popup v-model="showPopup" mode="bottom">
<view class="popup__coupon">
<view class="coupon__title f-30">选择优惠券</view>
<!-- 优惠券列表 -->
<view class="coupon-list">
<scroll-view :scroll-y="true" style="height: 565rpx;">
<view class="coupon-item" v-for="(item, index) in order.couponList" :key="index">
<view class="item-wrapper"
:class="[item.is_apply ? 'color-' + CouponColors[index % CouponColors.length] : 'color-gray']"
@click="handleSelectCoupon(index)">
<view class="coupon-type">{{ CouponTypeEnum[item.coupon_type].name }}</view>
<view class="tip dis-flex flex-dir-column flex-x-center">
<view v-if="item.coupon_type == CouponTypeEnum.FULL_DISCOUNT.value">
<text class="f-30">¥</text>
<text class="money">{{ item.reduce_price }}</text>
</view>
<text class="money"
v-if="item.coupon_type == CouponTypeEnum.DISCOUNT.value">{{ item.discount }}折</text>
<text class="pay-line">满{{ item.min_price }}元可用</text>
</view>
<view class="split-line"></view>
<view class="content dis-flex flex-dir-column flex-x-between">
<view class="title">{{ item.name }}</view>
<view class="bottom dis-flex flex-y-center">
<view class="time flex-box">
<block v-if="item.start_time === item.end_time">{{ item.start_time }}</block>
<block v-else>{{ item.start_time }}~{{ item.end_time }}</block>
</view>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
<!-- 不使用优惠券 -->
<view class="coupon__do_not dis-flex flex-y-center flex-x-center">
<view class="control dis-flex flex-y-center flex-x-center" @click="handleNotUseCoupon()">
<text class="f-26">不使用优惠券</text>
</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
// import * as Verify from '@/utils/verify'
// import * as CheckoutApi from '@/api/checkout'
import { CouponTypeEnum } from '@/common/enum/coupon'
import DeliveryTypeEnum from '@/common/enum/order/DeliveryType'
import PayTypeEnum from '@/common/enum/order/PayType'
// import { wxPayment } from '@/core/app'
import submitOrderData from '@/mock/submitOrder.json'
const CouponColors = ['red', 'blue', 'violet', 'yellow']
export default {
data() {
return {
// 枚举类
DeliveryTypeEnum,
PayTypeEnum,
CouponTypeEnum,
// 当前页面参数
options: {},
// 优惠券颜色组
CouponColors,
// 当前选中的配送方式
curDelivery: null,
// 当前选中的支付方式
curPayType: PayTypeEnum.BALANCE.value,
// 选择的优惠券
selectCouponId: 0,
// 是否使用积分抵扣
isUsePoints: false,
// 买家留言
remark: '',
// 禁用submit按钮
disabled: false,
// 是否显示积分说明
showPoints: false,
// 是否显示优惠券弹窗
showPopup: false,
// 按钮禁用
disabled: false,
// 订单信息 (从后端api中获取)
order: {
// 商品列表
goodsList: [],
// 优惠券列表
couponList: [],
// 是否存在收货地址
existAddress: false,
// 默认收货地址
address: null,
// 是否存在收货地址
existAddress: false,
// 当前用户收货城市是否存在配送规则中
isIntraRegion: true,
// 是否存在错误
hasError: false,
// 错误信息
errorMsg: ''
},
// 个人信息
personal: {},
// 商城设置
setting: {}
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.options = options
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
// 获取当前订单信息
this.getOrderData()
},
methods: {
// 获取订单数据
getOrderData() {
const app = this
// 请求的参数
const params = {
delivery: app.curDelivery || 0,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
}
// 请求api
// CheckoutApi.order(app.options.mode, app.getRequestParam())
// .then(result => {
// app.initData(result.data)
// })
// .catch(err => err);
app.initData(submitOrderData.data)
},
// 初始化数据
initData({ order, personal }) {
const app = this
app.order = order
app.personal = personal || {}
app.setting = order.setting
// 显示错误信息
if (order.hasError) {
app.$toast(order.errorMsg)
}
// 当前选择的配送方式
app.curDelivery = order.delivery
// 如果只有一种配送方式则不显示选项卡
app.isShowTab = app.setting.deliveryType.length > 1
// 当前选择支付方式 (如果是微信小程序默认使用微信支付)
if (app.$platform === 'MP-WEIXIN') {
app.curPayType = PayTypeEnum.WECHAT.value
}
},
// 获取api请求的参数
getRequestParam() {
const app = this
const { options } = app
// 结算模式的固定参数
const modeParam = {}
// 结算模式: 立即购买
if (options.mode === 'buyNow') {
modeParam.goodsId = options.goodsId
modeParam.goodsNum = options.goodsNum
modeParam.goodsSkuId = options.goodsSkuId
}
// 结算模式: 购物车
if (options.mode === 'cart') {
modeParam.cartIds = options.cartIds
}
// 订单结算参数(用户选择)
const orderParam = {
delivery: app.curDelivery || 0,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
}
return { ...orderParam, ...modeParam }
},
// 显示积分说明
handleShowPoints() {
this.showPoints = true
},
// 显示优惠券弹窗
handleShowPopup() {
this.showPopup = true
},
// 选择优惠券
handleSelectCoupon(index) {
const app = this
const { couponList } = app.order
// 当前选择的优惠券
const couponItem = couponList[index]
// 判断是否在适用范围
if (!couponItem.is_apply) {
app.$toast(couponItem.not_apply_info)
return
}
// 记录选中的优惠券id
app.selectCouponId = couponItem.user_coupon_id
// 重新获取订单信息
app.getOrderData()
// 隐藏优惠券弹层
app.showPopup = false
},
// 不使用优惠券
handleNotUseCoupon() {
const app = this
app.selectCouponId = 0
// 重新获取订单信息
app.getOrderData()
// 隐藏优惠券弹层
app.showPopup = false
},
// 选择支付方式
handleSelectPayType(value) {
this.curPayType = value
},
// 快递配送:选择收货地址
onSelectAddress() {
this.$navTo('pages/address/index', { from: 'checkout' })
},
// 跳转到商品详情页
onTargetGoods(goodsId) {
this.$navTo('pages/goods/detail', { goodsId })
},
// 订单提交
onSubmitOrder() {
const app = this
if (app.disabled) {
return false
}
// 表单验证
if (!app.onVerifyFrom()) {
return false
}
// 按钮禁用
app.disabled = true
// 请求api
// CheckoutApi.submit(app.options.mode, app.getFormData())
// .then(result => app.onSubmitCallback(result))
// .catch(err => {
// if (err.result) {
// const errData = err.result.data
// if (errData.is_created) {
// app.navToMyOrder()
// return false
// }
// }
// app.disabled = false
// })
},
// 订单提交成功后回调
onSubmitCallback(result) {
const app = this
// 发起微信支付
if (result.data.payType == PayTypeEnum.WECHAT.value) {
// wxPayment(result.data.payment)
// .then(() => app.$success('支付成功'))
// .catch(err => app.$error('订单未支付'))
// .finally(() => {
// app.disabled = false
// app.navToMyOrder()
// })
}
// 余额支付
if (result.data.payType == PayTypeEnum.BALANCE.value) {
app.$success('支付成功')
app.disabled = false
app.navToMyOrder()
}
},
// 跳转到我的订单(等待1秒)
navToMyOrder() {
setTimeout(() => {
this.$navTo('pages/order/index')
}, 1000)
},
// 表单提交的数据
getFormData() {
const app = this
const { options } = app
// 表单数据
const form = {
delivery: app.curDelivery,
payType: app.curPayType,
couponId: app.selectCouponId || 0,
isUsePoints: app.isUsePoints ? 1 : 0,
remark: app.remark || '',
}
// 创建订单-立即购买
if (options.mode === 'buyNow') {
form.goodsId = options.goodsId
form.goodsNum = options.goodsNum
form.goodsSkuId = options.goodsSkuId
}
// 创建订单-购物车结算
if (options.mode === 'cart') {
form.cartIds = options.cartIds || null
}
return form
},
// 表单验证
onVerifyFrom() {
const app = this
if (app.hasError) {
app.$toast(app.errorMsg)
return false
}
return true
},
}
}
</script>
<style lang="scss" scoped>
@import "./style.scss";
</style>

@ -0,0 +1,19 @@
<!--
* @Author: ch
* @Date: 2022-03-22 15:09:06
* @LastEditors: ch
* @LastEditTime: 2022-03-22 16:19:06
* @Description: file content
-->
<template>
<view>
<u-cell :isLink="true" arrow-direction="right" label="昵称">
<u-image slot="icon" shape="circle" width="100rpx" height="100rpx" ></u-image>
</u-cell>
<u-cell label="我的收货地址" :isLink="true" rightIcon="arrow-right"></u-cell>
<u-cell label="版本号" value="1.0.0"></u-cell>
<u-button type="primary" text="退出登录"></u-button>
</view>
</template>
<style lang="scss" scoped>
</style>

@ -0,0 +1,167 @@
<template>
<view class="container">
<!-- 搜索框 -->
<!-- <search tips="搜索商品" @event="$navTo('pages/search/index')" /> -->
<view class="cate-content" v-if="list.length > 0">
<!-- 左侧 一级分类 -->
<scroll-view class="cate-left" :scroll-y="true">
<text class="type-nav" :class="{ selected: curIndex == index }"
v-for="(item, index) in list" @click="handleSelectNav(index)"
:key="index" >{{ item.name }}</text>
</scroll-view>
<!-- 右侧 二级分类 -->
<scroll-view class="cate-right" :scroll-top="scrollTop" :scroll-y="true">
<image class="cate-tow-img" :src="list[curIndex].image ? list[curIndex].image.preview_url : ''"></image>
<view v-for="(item, idx) in list[curIndex].children"
:key="idx" @click="onTargetGoodsList(item.category_id)">
<view class="cate-tow-group">
<text class="cate-tow-group--title">{{item.name}}</text>
<view class="cate-tow-group--item" v-for="i in item.children" :key="i.id">
<image class="cate-tow-group--img" mode="scaleToFill"
:src="i.image.preview_url"></image>
<text class="cate-tow-group--name">{{i.name}}</text>
</view>
</view>
</view>
</scroll-view>
</view>
<BsEmpty v-if="!list.length" :isLoading="isLoading" />
</view>
</template>
<script>
import categoryData from "@/mock/category.json";
import BsEmpty from "@/components/BsEmpty";
export default {
components: { BsEmpty },
data() {
return {
// 一级分类:指针
curIndex: 0,
// 内容区竖向滚动条位置
scrollTop: 0,
// 分类列表
list: [],
// 正在加载中
isLoading: true,
};
},
onLoad() {
// 加载页面数据
this.getPageData();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
this.getPageData();
},
methods: {
// 一级分类:选中分类
handleSelectNav(index) {
this.curIndex = index;
this.scrollTop = 0;
},
/**
* 获取页面数据
*/
getPageData() {
this.isLoading = true;
// 初始化分类列表数据
this.initCategory(categoryData.data.list);
this.isLoading = false;
},
/**
* 初始化分类列表数据
* @param {Object} result
*/
initCategory(list) {
this.list = list;
},
},
};
</script>
<style>
page {
background: #fff;
}
</style>
<style lang="scss" scoped>
.cate-content {
background: #fff;
height: calc(100vh - 120rpx);
display: flex;
}
.cate-left {
width: 190rpx;
color: #444;
height: 100%;
background: #f8f8f8;
}
.cate-right {
height: 100%;
padding: 40rpx 0;
overflow: hidden;
}
.type-nav {
display: block;
width: 190rpx;
height: 100rpx;
line-height: 100rpx;
font-size: 28rpx;
text-align: center;
color: #999;
}
.type-nav.selected {
background: #fff;
color: #FF875B;
font-size: 28rpx;
}
.cate-tow-img{
width:480rpx;
margin: 0 40rpx 40rpx;
height: 164rpx;
background: #ccc;
border-radius: 16rpx;
}
.cate-tow-group{
display: flex;
flex-wrap: wrap;
margin-bottom: 40rpx;
&--title{
padding: 20rpx 40rpx;
display: block;
width: 100%;
font-size: 30rpx;
color: #333;
}
&--item{
margin-left: 40rpx;
}
&--img{
display: block;
width: 130rpx;
height: 130rpx;
}
&--name{
display: block;
text-align: center;
font-size: 24rpx;
margin: 10rpx 0 40rpx;
color: #333;
}
}
</style>

@ -0,0 +1,162 @@
<template>
<!-- 商品评价 -->
<view v-if="!isLoading && list.length" class="goods-comment m-top20">
<view class="item-title dis-flex">
<view class="block-left flex-box">
商品评价 (<text class="total">{{ total }}条</text>)
</view>
<view class="block-right">
<text @click="onTargetToComment" class="show-more col-9">查看更多</text>
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 评论列表 -->
<view class="comment-list">
<view class="comment-item" v-for="(item, index) in list" :key="index">
<view class="comment-item_row dis-flex flex-y-center">
<view class="user-info dis-flex flex-y-center">
<avatar-image class="user-avatar" :url="item.user.avatar_url" :width="50" />
<text class="user-name">{{ item.user.nick_name }}</text>
</view>
<!-- 评星 -->
<view class="star-rating">
<u-rate active-color="#f4a213" :current="rates[item.score]" :disabled="true" />
</view>
</view>
<view class="item-content m-top20">
<text class="f-26 twoline-hide">{{ item.content }}</text>
</view>
<view class="comment-time">{{ item.create_time }}</view>
</view>
</view>
</view>
</template>
<script>
import AvatarImage from '@/components/avatar-image'
import * as CommentApi from '@/api/comment'
export default {
components: {
AvatarImage
},
props: {
// 商品ID
goodsId: {
type: Number,
default: null
},
// 加载多少条记录 默认2条
limit: {
type: Number,
default: 2
}
},
data() {
return {
// 正在加载
isLoading: true,
// 评星数据转换
rates: { 10: 5, 20: 3, 30: 1 },
// 评价列表数据
list: [],
// 评价总数量
total: 0
}
},
created() {
// 加载评价列表数据
this.getCommentList()
},
methods: {
// 加载评价列表数据
getCommentList() {
const app = this
app.isLoading = true
CommentApi.listRows(app.goodsId, app.limit)
.then(result => {
app.list = result.data.list
app.total = result.data.total
})
.catch(err => err)
.finally(() => app.isLoading = false)
},
// 跳转到评论列表页
onTargetToComment() {
const app = this
app.$navTo('pages/comment/index', { goodsId: app.goodsId })
}
}
}
</script>
<style lang="scss" scoped>
.goods-comment {
padding: 20rpx 30rpx;
background-color: #fff;
}
.item-title {
font-size: 28rpx;
margin-bottom: 25rpx;
.total {
margin: 0 4rpx;
}
.show-more {
margin-right: 8rpx;
font-size: 24rpx;
}
}
.comment-item {
padding: 15rpx 5rpx;
margin-bottom: 10rpx;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
margin-bottom: 0;
border-bottom: none;
}
.comment-item_row {
margin-bottom: 10rpx;
}
}
.user-info {
margin-right: 15rpx;
.user-avatar {
width: 50rpx;
height: 50rpx;
border-radius: 50%;
margin-right: 10rpx;
}
.user-name {
font-size: 24rpx;
}
}
.item-content {
color: #333;
margin: 16rpx 0;
max-height: 76rpx;
line-height: 38rpx;
}
.comment-time {
font-size: 24rpx;
color: #999;
margin-top: 10rpx;
}
</style>

@ -0,0 +1,157 @@
<template>
<view v-if="list.length" class="service-wrapper">
<!-- 服务简述 -->
<view class="service-simple" @click="handlePopup">
<view class="s-list">
<view class="s-item" v-for="(item, index) in list" :key="index">
<text class="item-icon iconfont icon-fuwu"></text>
<text class="item-val">{{ item.name }}</text>
</view>
</view>
<!-- 扩展箭头 -->
<view class="s-arrow f-26 col-9 t-r">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
<!-- 详情内容弹窗 -->
<u-popup v-model="showPopup" mode="bottom" :closeable="true" :border-radius="26">
<view class="service-content">
<view class="title">服务</view>
<scroll-view class="content-scroll" :scroll-y="true">
<view class="s-list clearfix">
<view class="s-item" v-for="(item, index) in list" :key="index">
<text class="item-icon iconfont icon-fuwu"></text>
<view class="item-val">{{ item.name }}</view>
<view class="item-summary">{{ item.summary }}</view>
</view>
</view>
</scroll-view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
props: {
// 商品ID
goodsId: {
type: Number,
default: null
}
},
data() {
return {
// 正在加载
isLoading: true,
// 显示详情内容弹窗
showPopup: false,
// 服务列表数据
list: []
}
},
created() {
// 获取商品服务列表
this.getServiceList()
},
methods: {
// 获取商品服务列表
getServiceList() {
const app = this
app.isLoading = true
// ServiceApi.list(app.goodsId)
// .then(result => app.list = result.data.list)
// .finally(() => app.isLoading = false)
},
// 显示弹窗
handlePopup() {
this.showPopup = !this.showPopup
}
}
}
</script>
<style lang="scss" scoped>
.service-wrapper {
min-height: 24rpx;
margin-bottom: -24rpx;
}
// 服务简述
.service-simple {
padding: 24rpx 30rpx;
display: flex;
align-items: center;
.s-list {
flex: 1;
margin-left: -15rpx;
}
.s-item {
float: left;
font-size: 26rpx;
margin: 8rpx 15rpx;
.item-icon {
color: #FA2209;
}
.item-val {
margin-left: 12rpx;
}
}
}
// 服务详细内容
.service-content {
padding: 24rpx;
.title {
font-size: 30rpx;
margin-bottom: 50rpx;
font-weight: bold;
text-align: center;
}
.content-scroll {
min-height: 400rpx;
max-height: 760rpx;
}
.s-list {
padding: 0 30rpx 0 80rpx;
}
.s-item {
position: relative;
margin-bottom: 60rpx;
.item-icon {
position: absolute;
top: 6rpx;
left: -50rpx;
color: #FA2209;
}
.item-val {
font-size: 28rpx;
}
.item-summary {
font-size: 26rpx;
margin-top: 20rpx;
color: #6d6d6d;
}
}
}
</style>

@ -0,0 +1,173 @@
<template>
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode"
:maskCloseAble="true" @open="openSkuPopup" @close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow"
buyNowText="立即购买" />
</template>
<script>
// import { setCartTotalNum } from '@/core/app'
// import * as CartApi from '@/api/cart'
import GoodsSkuPopup from '@/components/goods-sku-popup'
export default {
components: {
GoodsSkuPopup
},
model: {
prop: 'value',
event: 'input'
},
props: {
// true 组件显示 false 组件隐藏
value: {
Type: Boolean,
default: false
},
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: {
type: Number,
default: 1
},
// 商品详情信息
goods: {
type: Object,
default: {}
}
},
data() {
return {
goodsInfo: {}
}
},
created() {
const app = this
const { goods } = app
app.goodsInfo = {
_id: goods.goods_id,
name: goods.goods_name,
goods_thumb: goods.goods_image,
sku_list: app.getSkuList(),
spec_list: app.getSpecList()
}
},
methods: {
// 监听组件显示隐藏
onChangeValue(val) {
this.$emit('input', val)
},
// 整理商品SKU列表
getSkuList() {
const app = this
const { goods: { goods_name, goods_image, skuList } } = app
const skuData = []
skuList.forEach(item => {
skuData.push({
_id: item.id,
goods_sku_id: item.goods_sku_id,
goods_id: item.goods_id,
goods_name: goods_name,
image: item.image_url ? item.image_url : goods_image,
price: item.goods_price * 100,
stock: item.stock_num,
spec_value_ids: item.spec_value_ids,
sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
})
})
return skuData
},
// 获取sku记录的规格值列表
getSkuNameArr(specValueIds) {
const app = this
const defaultData = ['默认']
const skuNameArr = []
if (specValueIds) {
specValueIds.forEach((valueId, groupIndex) => {
const specValueName = app.getSpecValueName(valueId, groupIndex)
skuNameArr.push(specValueName)
})
}
return skuNameArr.length ? skuNameArr : defaultData
},
// 获取指定的规格值名称
getSpecValueName(valueId, groupIndex) {
const app = this
const { goods: { specList } } = app
const res = specList[groupIndex].valueList.find(specValue => {
return specValue.spec_value_id == valueId
})
return res.spec_value
},
// 整理规格数据
getSpecList() {
const { goods: { specList } } = this
const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
const specData = []
specList.forEach(group => {
const children = []
group.valueList.forEach(specValue => {
children.push({ name: specValue.spec_value })
})
specData.push({
name: group.spec_name,
list: children
})
})
return specData.length ? specData : defaultData
},
// sku组件 开始-----------------------------------------------------------
openSkuPopup() {
// console.log("监听 - 打开sku组件")
},
closeSkuPopup() {
// console.log("监听 - 关闭sku组件")
},
// 加入购物车按钮
addCart(selectShop) {
const app = this
const { goods_id, goods_sku_id, buy_num } = selectShop
// CartApi.add(goods_id, goods_sku_id, buy_num)
// .then(result => {
// // 显示成功
// app.$toast(result.message)
// // 隐藏当前弹窗
// app.onChangeValue(false)
// // 购物车商品总数量
// const cartTotal = result.data.cartTotal
// // 缓存购物车数量
// setCartTotalNum(cartTotal)
// // 传递给父级
// app.$emit('addCart', cartTotal)
// })
},
// 立即购买
buyNow(selectShop) {
// 跳转到订单结算页
this.$navTo('pages/checkout/index', {
mode: 'buyNow',
goodsId: selectShop.goods_id,
goodsSkuId: selectShop.goods_sku_id,
goodsNum: selectShop.buy_num
})
// 隐藏当前弹窗
this.onChangeValue(false)
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,141 @@
<template>
<!-- 商品图片 -->
<view class="images-swiper">
<swiper class="swiper-box" :autoplay="autoplay" :duration="duration" :indicator-dots="indicatorDots"
:interval="interval" :circular="true" @change="setCurrent">
<!-- 主图视频 -->
<swiper-item v-if="video">
<view class="slide-video">
<video id="myVideo" class="video" :poster="videoCover.preview_url" :src="video.external_url" controls
x5-playsinline playsinline webkit-playsinline webkit-playsinline x5-video-player-type="h5"
x5-video-player-fullscreen x5-video-orientation="portrait" :enable-progress-gesture="false"
@play="onVideoPlay"></video>
</view>
</swiper-item>
<!-- 轮播图片 -->
<swiper-item v-for="(item, index) in images" :key="index" @click="onPreviewImages(index)">
<view class="slide-image">
<image class="image" :draggable="false" :src="item.preview_url"></image>
</view>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
props: {
// 主图视频
video: {
type: Object,
default () {
return null
}
},
// 主图视频封面
videoCover: {
type: Object,
default () {
return null
}
},
// 图片轮播
images: {
type: Array,
default: []
}
},
data() {
return {
indicatorDots: true, // 是否显示面板指示点
autoplay: true, // 是否自动切换
interval: 4000, // 自动切换时间间隔
duration: 800, // 滑动动画时长
currentIndex: 1, // 轮播图指针
}
},
methods: {
// 事件:视频开始播放
onVideoPlay(e) {
this.autoplay = false
},
// 设置轮播图当前指针 数字
setCurrent({ detail }) {
const app = this
app.currentIndex = detail.current + 1
},
// 浏览商品图片
onPreviewImages(index) {
const app = this
const imageUrls = []
app.images.forEach(item => {
imageUrls.push(item.preview_url);
});
uni.previewImage({
current: imageUrls[index],
urls: imageUrls
})
}
}
}
</script>
<style lang="scss" scoped>
// swiper组件
.images-swiper {
position: relative;
}
.swiper-box {
width: 100%;
height: 100vw;
/* #ifdef H5 */
max-width: 480px;
max-height: 480px;
margin: 0 auto;
/* #endif */
// 主图视频
.slide-video {
width: 100%;
height: 100%;
.video {
display: block;
width: 100%;
height: 100%;
}
}
// 图片轮播
.slide-image {
position: relative;
width: 100%;
height: 100%;
.image {
display: block;
width: 100%;
height: 100%;
}
}
}
// swiper计数
.swiper-count {
position: absolute;
right: 36rpx;
bottom: 72rpx;
padding: 2rpx 18rpx;
background: rgba(0, 0, 0, 0.363);
border-radius: 50rpx;
color: #fff;
font-size: 26rpx;
}
</style>

@ -0,0 +1,230 @@
.container {
// 设置ios刘海屏底部横线安全区域
// 110 - 18 + 4
padding-bottom: calc(constant(safe-area-inset-bottom) + 106rpx + 6rpx);
padding-bottom: calc(env(safe-area-inset-bottom) + 106rpx + 6rpx);
}
// 商品信息
.goods-info {
background: #fff;
padding: 25rpx 30rpx;
}
.info-item__top {
min-height: 40rpx;
margin-bottom: 20rpx;
line-height: 1;
}
.floor-price__samll {
font-size: 26rpx;
line-height: 1;
color: #FA2209;
margin-bottom: -10rpx;
}
// 商品价
.floor-price {
color: #FA2209;
margin-right: 15rpx;
font-size: 42rpx;
}
.original-price {
font-size: 26rpx;
text-decoration: line-through;
color: #959595;
margin-right: 15rpx;
margin-bottom: -6rpx;
}
// 会员价标签
.user-grade {
background: #3c3c3c;
border-radius: 6rpx;
padding: 8rpx 14rpx;
margin-right: 15rpx;
font-size: 24rpx;
color: #EEE0C3;
}
.goods-sales {
font-size: 24rpx;
color: #959595;
}
.info-item__name .goods-name {
font-size: 30rpx;
}
/* 商品分享 */
.goods-share__line {
border-left: 1rpx solid #f4f4f4;
height: 60rpx;
margin: 0 30rpx;
}
.goods-share .share-btn {
line-height: normal;
padding: 0;
background: none;
border-radius: 0;
box-shadow: none;
font-size: 8pt;
border: none;
color: #191919;
}
.goods-share .share-btn::after {
border: none;
}
.goods-share .share__icon {
font-size: 40rpx;
margin-bottom: 5rpx;
}
// 商品卖点
.info-item_selling-point {
margin-top: 8rpx;
font-size: 24rpx;
color: #808080;
}
// 选择商品规格
.goods-choice {
padding: 26rpx 30rpx;
font-size: 28rpx;
.spec-list {
display: flex;
align-items: center;
.spec-name {
margin-right: 10rpx;
}
}
}
// 商品详情
.goods-content .item-title {
padding: 26rpx 30rpx;
font-size: 28rpx;
}
// 底部操作栏
.footer-fixed {
position: fixed;
bottom: var(--window-bottom);
left: 0;
right: 0;
display: flex;
z-index: 11;
box-shadow: 0 -4rpx 40rpx 0 rgba(151, 151, 151, 0.24);
background: #fff;
// 设置ios刘海屏底部横线安全区域
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.footer-container {
width: 100%;
display: flex;
height: 106rpx;
}
// 快捷菜单
.foo-item-fast {
box-sizing: border-box;
width: 256rpx;
line-height: 1;
display: flex;
justify-content: center;
align-items: center;
.fast-item {
position: relative;
padding: 4rpx 10rpx;
line-height: 1;
// text-align: center;
.fast-icon {
margin-bottom: 6rpx;
}
&--home {
margin-right: 30rpx;
}
&--cart {
.fast-icon { padding-left: 3px; }
}
// 角标
.fast-badge {
display: inline-block;
box-sizing: border-box;
min-width: 16px;
padding: 0 3px;
color: #fff;
font-weight: 500;
font-size: 12px;
font-family: -apple-system-font, Helvetica Neue, Arial, sans-serif;
line-height: 1.2;
text-align: center;
background-color: #ee0a24;
border: 1px solid #fff;
border-radius: 999px;
}
.fast-badge--fixed {
position: absolute;
top: 0;
right: 0;
transform-origin: 100%
}
.fast-icon {
font-size: 46rpx;
}
.fast-text {
font-size: 24rpx;
}
}
}
// 操作按钮
.foo-item-btn {
flex: 1;
.btn-wrapper {
height: 100%;
display: flex;
align-items: center;
}
.btn-item {
flex: 1;
font-size: 28rpx;
height: 72rpx;
margin-right: 16rpx;
color: #fff;
border-radius: 50rpx;
display: flex;
justify-content: center;
align-items: center;
}
// 立即购买按钮
.btn-item-main {
background: linear-gradient(to right, #f9211c, #ff6335);
}
// 购物车按钮
.btn-item-deputy {
background: linear-gradient(to right, #ffa600, #ffbb00);
}
}

@ -0,0 +1,304 @@
<!--
* @Author: ch
* @Date: 2022-03-21 10:43:45
* @LastEditors: ch
* @LastEditTime: 2022-03-21 11:25:56
* @Description: file content
-->
<template>
<view v-show="!isLoading" class="container">
<!-- 商品图片轮播 -->
<SlideImage v-if="!isLoading" :video="goods.video" :videoCover="goods.videoCover" :images="goods.goods_images" />
<!-- 商品信息 -->
<view v-if="!isLoading" class="goods-info m-top20">
<!-- 价格、销量 -->
<view class="info-item info-item__top dis-flex flex-x-between flex-y-end">
<view class="block-left dis-flex flex-y-center">
<!-- 商品售价 -->
<text class="floor-price__samll">¥</text>
<text class="floor-price">{{ goods.goods_price_min }}</text>
<!-- 会员价标签 -->
<view v-if="goods.is_user_grade" class="user-grade">
<text>会员价</text>
</view>
<!-- 划线价 -->
<text v-if="goods.line_price_min > 0" class="original-price">¥{{ goods.line_price_min }}</text>
</view>
<view class="block-right dis-flex">
<!-- 销量 -->
<view class="goods-sales">
<text>已售{{ goods.goods_sales }}件</text>
</view>
</view>
</view>
<!-- 标题、分享 -->
<view class="info-item info-item__name dis-flex flex-y-center">
<view class="goods-name flex-box">
<text class="twoline-hide">{{ goods.goods_name }}</text>
</view>
<!-- #ifdef MP-WEIXIN -->
<view class="goods-share__line"></view>
<view class="goods-share">
<button class="share-btn dis-flex flex-dir-column" open-type="share">
<text class="share__icon iconfont icon-fenxiang"></text>
<text class="f-24">分享</text>
</button>
</view>
<!-- #endif -->
</view>
<!-- 商品卖点 -->
<view v-if="goods.selling_point" class="info-item info-item_selling-point">
<text>{{ goods.selling_point }}</text>
</view>
</view>
<!-- 选择商品规格 -->
<view v-if="goods.spec_type == 20" class="goods-choice m-top20 b-f" @click="onShowSkuPopup(1)">
<view class="spec-list">
<view class="flex-box">
<text class="col-8">选择:</text>
<text class="spec-name" v-for="(item, index) in goods.specList" :key="index">{{ item.spec_name }}</text>
</view>
<view class="f-26 col-9 t-r">
<text class="iconfont icon-arrow-right"></text>
</view>
</view>
</view>
<!-- 商品服务 -->
<Service v-if="!isLoading" :goods-id="goodsId" />
<!-- 商品SKU弹窗 -->
<SkuPopup v-if="!isLoading" v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" @addCart="onAddCart" />
<!-- 商品评价 -->
<!-- <Comment v-if="!isLoading" :goods-id="goodsId" :limit="2" /> -->
<!-- 商品描述 -->
<view v-if="!isLoading" class="goods-content m-top20">
<view class="item-title b-f">
<text>商品描述</text>
</view>
<block v-if="goods.content != ''">
<view class="goods-content__detail b-f">
<mp-html :content="goods.content" />
</view>
</block>
<empty v-else tips="亲,暂无商品描述" />
</view>
<!-- 底部选项卡 -->
<view class="footer-fixed">
<view class="footer-container">
<!-- 导航图标 -->
<view class="foo-item-fast">
<!-- 首页 -->
<view class="fast-item fast-item--home" @click="onTargetHome">
<view class="fast-icon">
<text class="iconfont icon-shouye"></text>
</view>
<view class="fast-text">
<text>首页</text>
</view>
</view>
<!-- 客服 (仅微信小程序端显示) -->
<!-- #ifdef MP-WEIXIN -->
<button class="btn-normal" open-type="contact">
<view class="fast-item">
<view class="fast-icon">
<text class="iconfont icon-kefu1"></text>
</view>
<view class="fast-text">
<text>客服</text>
</view>
</view>
</button>
<!-- #endif -->
<!-- 购物车 (非微信小程序端显示) -->
<!-- #ifndef MP-WEIXIN -->
<view class="fast-item fast-item--cart" @click="onTargetCart">
<view v-if="cartTotal > 0" class="fast-badge fast-badge--fixed">{{ cartTotal > 99 ? '99+' : cartTotal }}
</view>
<view class="fast-icon">
<text class="iconfont icon-gouwuche"></text>
</view>
<view class="fast-text">
<text>购物车</text>
</view>
</view>
<!-- #endif -->
</view>
<!-- 操作按钮 -->
<view class="foo-item-btn">
<view class="btn-wrapper">
<view class="btn-item btn-item-deputy" @click="onShowSkuPopup(2)">
<text>加入购物车</text>
</view>
<view class="btn-item btn-item-main" @click="onShowSkuPopup(3)">
<text>立即购买</text>
</view>
</view>
</view>
</view>
</view>
<!-- 快捷导航 -->
<!-- <shortcut bottom="120rpx" /> -->
</view>
</template>
<script>
import goodsDetailData from '@/mock/goodsDetail.json';
// import Shortcut from '@/components/shortcut'
import SlideImage from './components/SlideImage';
import SkuPopup from './components/SkuPopup';
import Service from './components/Service';
import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html';
export default {
components: {
// Shortcut,
mpHtml,
SlideImage,
SkuPopup,
Comment,
Service
},
data() {
return {
// 正在加载
isLoading: true,
// 当前商品ID
goodsId: null,
// 商品详情
goods: {},
// 购物车总数量
cartTotal: 0,
// 显示/隐藏SKU弹窗
showSkuPopup: false,
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: 1
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 记录商品ID
this.goodsId = parseInt(options.goodsId)
// 加载页面数据
this.onRefreshPage()
},
methods: {
// 刷新页面数据
onRefreshPage() {
const app = this
app.isLoading = true
Promise.all([app.getGoodsDetail(), app.getCartTotal()])
.finally(() => app.isLoading = false)
},
// 获取商品信息
getGoodsDetail() {
const app = this
return new Promise((resolve, reject) => {
app.goods = goodsDetailData.data.detail
resolve(goodsDetailData.data.detail)
// console.log(goodsDetailData.data.detail)
// GoodsApi.detail(app.goodsId)
// .then(result => {
// app.goods = result.data.detail
// resolve(result)
// })
// .catch(reject)
})
},
// 获取购物车总数量
getCartTotal() {
const app = this
return new Promise((resolve, reject) => {
app.cartTotal = 2
resolve({"status":200,"message":"success","data":{"cartTotal":2}})
// CartApi.total()
// .then(result => {
// app.cartTotal = result.data.cartTotal
// resolve(result)
// })
// .catch(reject)
})
},
// 更新购物车数量
onAddCart(total) {
this.cartTotal = total
},
/**
* 显示/隐藏SKU弹窗
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
*/
onShowSkuPopup(skuMode = 1) {
this.skuMode = skuMode
this.showSkuPopup = !this.showSkuPopup
},
// 跳转到首页
onTargetHome(e) {
this.$navTo('pages/index/index')
},
// 跳转到购物车页
onTargetCart() {
this.$navTo('pages/cart/index')
},
},
/**
* 分享当前页面
*/
onShareAppMessage() {
const app = this
// 构建页面参数
const params = app.$getShareUrlParams({
goodsId: app.goodsId,
})
return {
title: app.goods.goods_name,
path: `/pages/goods/detail?${params}`
}
},
/**
* 分享到朋友圈
* 本接口为 Beta 版本,暂只在 Android 平台支持,详见分享到朋友圈 (Beta)
* https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/share-timeline.html
*/
onShareTimeline() {
const app = this
// 构建页面参数
const params = app.$getShareUrlParams({
goodsId: app.goodsId,
})
return {
title: app.goods.goods_name,
path: `/pages/goods/detail?${params}`
}
}
}
</script>
<style>
page {
background: #fafafa;
}
</style>
<style lang="scss" scoped>
@import "./detail.scss";
</style>

@ -0,0 +1,74 @@
<!--
* @Author: ch
* @Date: 2022-03-24 10:32:58
* @LastEditors: ch
* @LastEditTime: 2022-03-24 11:13:48
* @Description: file content
-->
<template>
<view>
<u-cell label="服务" value="假一赔四 · 全国包邮" :border="false"
isLink @click="showPopup = true"></u-cell>
<!-- 详情内容弹窗 -->
<u-popup mode="bottom" :show="showPopup" :round="12">
<view class="content">
<text class="title">假一赔四</text>
<text class="desc">正品保障,假一赔四</text>
<text class="title">全国包邮</text>
<text class="desc">偏远地区(青海、西藏、新疆)除外</text>
<text class="title">不支持7天退换</text>
<text class="desc">此商品不支持七天无理由退换</text>
</view>
<button class="btn" @click="showPopup = false">确认</button>
</u-popup>
</view>
</template>
<script>
export default {
data() {
return {
showPopup: false,
};
},
created() {
// 获取商品服务列表
},
methods: {},
};
</script>
<style lang="scss" scoped>
.content{
padding: 50rpx;
color: #333;
}
.title{
margin: 50rpx 0 20rpx 0;
display: block;
font-size: 32rpx;
::before{
display: inline-block;
content: '';
width: 10rpx;
height: 10rpx;
border-radius: 8rpx;
background: #FF7F39;
margin-right: 20rpx;
}
}
.desc{
font-size: 28rpx;
}
.btn{
height: 80rpx;
width: 690rpx;
font-size: 30rpx;
color: #fff;
margin: 100rpx auto 30rpx auto;
border-radius: 100rpx;
background: linear-gradient(270deg, #FF7F39 0%, #FFA35B 100%);
}
</style>

@ -0,0 +1,173 @@
<template>
<goods-sku-popup :value="value" @input="onChangeValue" border-radius="20" :localdata="goodsInfo" :mode="skuMode"
:maskCloseAble="true" @open="openSkuPopup" @close="closeSkuPopup" @add-cart="addCart" @buy-now="buyNow"
buyNowText="立即购买" />
</template>
<script>
// import { setCartTotalNum } from '@/core/app'
// import * as CartApi from '@/api/cart'
import GoodsSkuPopup from '@/components/goods-sku-popup'
export default {
components: {
GoodsSkuPopup
},
model: {
prop: 'value',
event: 'input'
},
props: {
// true 组件显示 false 组件隐藏
value: {
Type: Boolean,
default: false
},
// 模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode: {
type: Number,
default: 1
},
// 商品详情信息
goods: {
type: Object,
default: {}
}
},
data() {
return {
goodsInfo: {}
}
},
created() {
const app = this
const { goods } = app
app.goodsInfo = {
_id: goods.goods_id,
name: goods.goods_name,
goods_thumb: goods.goods_image,
sku_list: app.getSkuList(),
spec_list: app.getSpecList()
}
},
methods: {
// 监听组件显示隐藏
onChangeValue(val) {
this.$emit('input', val)
},
// 整理商品SKU列表
getSkuList() {
const app = this
const { goods: { goods_name, goods_image, skuList } } = app
const skuData = []
skuList.forEach(item => {
skuData.push({
_id: item.id,
goods_sku_id: item.goods_sku_id,
goods_id: item.goods_id,
goods_name: goods_name,
image: item.image_url ? item.image_url : goods_image,
price: item.goods_price * 100,
stock: item.stock_num,
spec_value_ids: item.spec_value_ids,
sku_name_arr: app.getSkuNameArr(item.spec_value_ids)
})
})
return skuData
},
// 获取sku记录的规格值列表
getSkuNameArr(specValueIds) {
const app = this
const defaultData = ['默认']
const skuNameArr = []
if (specValueIds) {
specValueIds.forEach((valueId, groupIndex) => {
const specValueName = app.getSpecValueName(valueId, groupIndex)
skuNameArr.push(specValueName)
})
}
return skuNameArr.length ? skuNameArr : defaultData
},
// 获取指定的规格值名称
getSpecValueName(valueId, groupIndex) {
const app = this
const { goods: { specList } } = app
const res = specList[groupIndex].valueList.find(specValue => {
return specValue.spec_value_id == valueId
})
return res.spec_value
},
// 整理规格数据
getSpecList() {
const { goods: { specList } } = this
const defaultData = [{ name: '默认', list: [{ name: '默认' }] }]
const specData = []
specList.forEach(group => {
const children = []
group.valueList.forEach(specValue => {
children.push({ name: specValue.spec_value })
})
specData.push({
name: group.spec_name,
list: children
})
})
return specData.length ? specData : defaultData
},
// sku组件 开始-----------------------------------------------------------
openSkuPopup() {
// console.log("监听 - 打开sku组件")
},
closeSkuPopup() {
// console.log("监听 - 关闭sku组件")
},
// 加入购物车按钮
addCart(selectShop) {
const app = this
const { goods_id, goods_sku_id, buy_num } = selectShop
// CartApi.add(goods_id, goods_sku_id, buy_num)
// .then(result => {
// // 显示成功
// app.$toast(result.message)
// // 隐藏当前弹窗
// app.onChangeValue(false)
// // 购物车商品总数量
// const cartTotal = result.data.cartTotal
// // 缓存购物车数量
// setCartTotalNum(cartTotal)
// // 传递给父级
// app.$emit('addCart', cartTotal)
// })
},
// 立即购买
buyNow(selectShop) {
// 跳转到订单结算页
this.$navTo('pages/checkout/index', {
mode: 'buyNow',
goodsId: selectShop.goods_id,
goodsSkuId: selectShop.goods_sku_id,
goodsNum: selectShop.buy_num
})
// 隐藏当前弹窗
this.onChangeValue(false)
}
}
}
</script>
<style lang="scss" scoped>
</style>

@ -0,0 +1,141 @@
<template>
<!-- 商品图片 -->
<view class="images-swiper">
<swiper class="swiper-box" :autoplay="autoplay" :duration="duration" :indicator-dots="indicatorDots"
:interval="interval" :circular="true" @change="setCurrent">
<!-- 主图视频 -->
<swiper-item v-if="video">
<view class="slide-video">
<video id="myVideo" class="video" :poster="videoCover.preview_url" :src="video.external_url" controls
x5-playsinline playsinline webkit-playsinline webkit-playsinline x5-video-player-type="h5"
x5-video-player-fullscreen x5-video-orientation="portrait" :enable-progress-gesture="false"
@play="onVideoPlay"></video>
</view>
</swiper-item>
<!-- 轮播图片 -->
<swiper-item v-for="(item, index) in images" :key="index" @click="onPreviewImages(index)">
<view class="slide-image">
<image class="image" :draggable="false" :src="item.preview_url"></image>
</view>
</swiper-item>
</swiper>
</view>
</template>
<script>
export default {
props: {
// 主图视频
video: {
type: Object,
default () {
return null
}
},
// 主图视频封面
videoCover: {
type: Object,
default () {
return null
}
},
// 图片轮播
images: {
type: Array,
default: []
}
},
data() {
return {
indicatorDots: true, // 是否显示面板指示点
autoplay: true, // 是否自动切换
interval: 4000, // 自动切换时间间隔
duration: 800, // 滑动动画时长
currentIndex: 1, // 轮播图指针
}
},
methods: {
// 事件:视频开始播放
onVideoPlay(e) {
this.autoplay = false
},
// 设置轮播图当前指针 数字
setCurrent({ detail }) {
const app = this
app.currentIndex = detail.current + 1
},
// 浏览商品图片
onPreviewImages(index) {
const app = this
const imageUrls = []
app.images.forEach(item => {
imageUrls.push(item.preview_url);
});
uni.previewImage({
current: imageUrls[index],
urls: imageUrls
})
}
}
}
</script>
<style lang="scss" scoped>
// swiper组件
.images-swiper {
position: relative;
}
.swiper-box {
width: 100%;
height: 100vw;
/* #ifdef H5 */
max-width: 480px;
max-height: 480px;
margin: 0 auto;
/* #endif */
// 主图视频
.slide-video {
width: 100%;
height: 100%;
.video {
display: block;
width: 100%;
height: 100%;
}
}
// 图片轮播
.slide-image {
position: relative;
width: 100%;
height: 100%;
.image {
display: block;
width: 100%;
height: 100%;
}
}
}
// swiper计数
.swiper-count {
position: absolute;
right: 36rpx;
bottom: 72rpx;
padding: 2rpx 18rpx;
background: rgba(0, 0, 0, 0.363);
border-radius: 50rpx;
color: #fff;
font-size: 26rpx;
}
</style>

@ -0,0 +1,139 @@
<!--
* @Author: ch
* @Date: 2022-03-23 17:27:21
* @LastEditors: ch
* @LastEditTime: 2022-03-24 14:10:20
* @Description: file content
-->
<template>
<view>
<SlideImage :images="goods.goods_images"></SlideImage>
<view class="goods-info">
<view class="pirce">
<text>¥</text>
<text>199.9</text>
<text>起</text>
</view>
<view class="goods-info--title">{{goods.goods_name}}</view>
</view>
<view class="select">
<Service></Service>
<u-cell label="选择" :border="false" isLink @click="onShowSkuPopup()"></u-cell>
<SkuPopup v-model="showSkuPopup" :skuMode="skuMode" :goods="goods" @addCart="onAddCart" ></SkuPopup>
</view>
<view class="goods-desc">
<mp-html :content="goods.content"/>
</view>
<view class="footer">
<view class="footer--left">
<view class="icon service">客服</view>
<view class="icon cart">购物车</view>
</view>
<view class="footer--btns">
<button class="btn" plain @click="onShowSkuPopup(2)">加入购物车</button>
<button class="btn" plain @click="onShowSkuPopup(3)">立即购买</button>
</view>
</view>
</view>
</template>
<script>
import goodsDetailData from '@/mock/goodsDetail.json';
import SlideImage from '@/pages/goods/detail/SlideImage.vue';
import Service from './Service.vue';
import mpHtml from 'mp-html/dist/uni-app/components/mp-html/mp-html';
import SkuPopup from './SkuPopup.vue';
export default {
components: { SlideImage, mpHtml, Service, SkuPopup},
data(){
return {
goods : goodsDetailData.data.detail,
showSkuPopup : false,
// sku弹窗模式 1:都显示 2:只显示购物车 3:只显示立即购买
skuMode : 1
}
},
methods:{
/**
* 显示/隐藏SKU弹窗
* @param {skuMode} 模式 1:都显示 2:只显示购物车 3:只显示立即购买
*/
onShowSkuPopup(skuMode = 1) {
this.skuMode = skuMode
this.showSkuPopup = true
},
}
}
</script>
<style lang="scss" scoped>
page{
background: #f8f8f8;
padding-bottom: 138rpx;
}
.goods-info{
margin: 20rpx 30rpx 0;
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
&--title{
font-size: 32rpx;
line-height: 48rpx;
}
}
.select{
margin: 20rpx 30rpx;
background: #fff;
border-radius: 16rpx;
}
.goods-desc{
margin: 20rpx 30rpx;
background: #fff;
border-radius: 16rpx;
padding: 20rpx;
}
.footer{
height: 138rpx;
padding: 0 30rpx;
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
bottom: 0;
left: 0;
right: 0;
border-top-left-radius: 24rpx;
border-top-right-radius: 24rpx;
background: #fff;
&--left{
display: flex;
}
&--btns{
background: #FFF3EE;
border-radius: 40rpx;
overflow: hidden;
}
.icon{
font-size: 22rpx;
padding-top: 60rpx;
background: url('@/static/tabbar/home.png') no-repeat center top;
background-size: 48rpx;
color: #333;
&.cart{
margin-left: 40rpx;
}
}
.btn{
width: 250rpx;
height: 80rpx;
line-height: 80rpx;
display: inline-block;
background: none;
border: none;
font-size: 28rpx;
color: #FF875B;
}
}
</style>

@ -0,0 +1,105 @@
<!--
* @Author: ch
* @Date: 2022-03-23 16:37:30
* @LastEditors: ch
* @LastEditTime: 2022-03-23 17:16:03
* @Description: file content
-->
<template>
<view class="sort">
<view class="sort--item" :class="item.value == 'all' && 'sort--item__active'" v-for="item in sortData" :key="item.value">
<text class="sort--label">{{item.label}}</text>
<view>
<view class="sort--icon sort--icon__up sort--icon__active"></view>
<view class="sort--icon sort--icon__down"></view>
</view>
</view>
<!-- <view class="sort-item" :class="{ active: sortType === 'all' }" @click="handleSortType('all')">
<text>综合</text>
</view>
<view class="sort-item" :class="{ active: sortType === 'sales' }" @click="handleSortType('sales')">
<text>销量</text>
</view>
<view class="sort-item sort-item-price" :class="{ active: sortType === 'price' }" @click="handleSortType('price')">
<text>价格</text>
<view class="price-arrow">
<view class="icon up" :class="{ active: sortType === 'price' && !sortPrice }">
<text class="iconfont icon-arrow-up"></text>
</view>
<view class="icon down" :class="{ active: sortType === 'price' && sortPrice }">
<text class="iconfont icon-arrow-down"></text> </view>
</view>
</view> -->
</view>
</template>
<script>
export default {
data(){
return {
sortData : [
{
value : 'all',
label : '综合',
sort : 'ASC'
},
{
value : 'pirce',
label : '价格',
sort : 'ASC'
},
{
value : 'num',
label : '销量',
sort : 'ASC'
},
{
value : 'new',
label : '上新',
sort : 'ASC'
}
]
}
}
}
</script>
<style lang="scss" scoped>
.sort{
height: 88rpx;
padding: 0 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
background: #fff;
&--item{
display: flex;
align-items: center;
color: #666;
&__active{
color: #333;
}
}
&--label{
font-size: 28rpx;
margin-right: 16rpx;
}
&--icon{
border: 8rpx solid #999;
border-left-color: transparent;
border-right-color: transparent;
&__up{
border-top-color: transparent;
margin-bottom: 6rpx;
&.sort--icon__active{
border-bottom-color: #FF7F39;
}
}
&__down{
border-bottom-color: transparent;
&.sort--icon__active{
border-top-color: #FF7F39;
}
}
}
}
</style>

@ -0,0 +1,44 @@
<!--
* @Author: ch
* @Date: 2022-03-21 10:31:54
* @LastEditors: ch
* @LastEditTime: 2022-03-23 17:03:25
* @Description: file content
-->
<template>
<view>
<view class="header">
<BsSearchTop :value="$Route.query.search"></BsSearchTop>
<!-- 排序标签 -->
<Sort></Sort>
</view>
<BsGoodsGroup class="goods-group"></BsGoodsGroup>
</view>
</template>
<script>
import BsGoodsGroup from "@/components/BsGoodsGroup.vue";
import BsSearchTop from "@/components/BsSearchTop.vue";
import Sort from "./Sort.vue";
export default {
components: { BsGoodsGroup, BsSearchTop, Sort },
data() {
return {
sortType: "all", // 排序类型
sortPrice: false, // 价格排序 (true高到低 false低到高)
};
},
};
</script>
<style lang="scss" scoped>
page {
background: #f8f8f8;
}
.header {
position: sticky;
top: var(--window-top);
z-index: 99;
}
.goods-group {
padding-top: 30rpx;
}
</style>

@ -0,0 +1,173 @@
<!--
* @Author: ch
* @Date: 2022-03-21 11:29:38
* @LastEditors: ch
* @LastEditTime: 2022-03-23 17:18:48
* @Description: file content
-->
<template>
<view class="container">
<view class="search">
<image class="search--icon" src="@/static/search/arrow.png" @click="$Router.back()"></image>
<u--input class="search--input" placeholder="请输入您想搜索的商品名称"
prefixIcon="search" clearable prefixIconStyle="font-size:48rpx;color:#ccc"
v-model="searchValue" placeholderClass="search--input__placeholder"></u--input>
<text class="search--btn" @click="onSearch">搜索</text>
</view>
<view class="history" v-if="historyList.length">
<view class="history--head">
<text>最近搜索</text>
<image class="history--del" src="@/static/search/del.png" @click="clearHistory" />
</view>
<view class="history--list">
<view class="history--item" v-for="(val, index) in historyList"
:key="index" @click="handleQuick(val)">{{ val }}
</view>
</view>
</view>
</view>
</template>
<script>
const HISTORY_SEARCH_KEY = "historySearch";
export default {
data() {
return {
historyList : [],
searchValue: '',
};
},
onShow(){
this.searchValue = '';
this.historyList = uni.getStorageSync(HISTORY_SEARCH_KEY) || [];
},
methods: {
/**
* 搜索提交
*/
onSearch() {
const { searchValue } = this;
if (searchValue) {
this.setHistory(searchValue);
this.$Router.push({path:'/pages/goods/list/index', query :{
search: searchValue
}
});
}
},
/**
* 清空最近搜索记录
*/
clearHistory() {
this.updateHistoryStorage([]);
},
/**
* 记录历史搜索
*/
setHistory(searchValue) {
const data = this.historyList;
const index = data.indexOf(searchValue);
index > -1 && data.splice(index, 1);
data.unshift(searchValue);
if(data.length === 11){
data.pop();
}
this.updateHistoryStorage(data);
},
/**
* 更新历史搜索缓存
* @param {Object} data
*/
updateHistoryStorage(data) {
this.historyList = data;
uni.setStorageSync(HISTORY_SEARCH_KEY, data);
},
/**
* 跳转到最近搜索
*/
handleQuick(searchValue) {
this.setHistory(searchValue);
this.$Router.push({path:'/pages/goods/list/index', query :{
search: searchValue
}
});
},
},
};
</script>
<style lang="scss" scoped>
page{
background: #fff;
}
.container{
padding: 0 40rpx;
}
.search{
height: 88rpx;
display: flex;
justify-content: space-between;
align-items: center;
&--icon{
width: 14rpx;
height: 28rpx;
}
&--input{
display: block;
margin: 0 40rpx;
height: 70rpx;
border:none;
box-sizing: border-box;
background: #F8F8F8;
&__placeholder{
font-size: 26rpx;
color: #666;
}
}
&--btn{
font-size: 26rpx;
color: #666;
}
}
// 最近搜索
.history {
&--head {
font-size: 26rpx;
color: #666;
display: flex;
justify-content: space-between;
align-items: center;
margin: 40rpx 0;
}
&--del{
width: 27rpx;
height: 28rpx;
}
&--list{
display: flex;
flex-wrap: wrap;
}
&--item{
max-width: 276rpx;
overflow: hidden;
text-overflow: ellipsis;
height: 70rpx;
line-height: 70rpx;
padding: 0 20rpx;
font-size: 26rpx;
color: #666;
background: #F8F8F8;
margin: 10rpx 18rpx 10rpx 0;
}
}
</style>

@ -0,0 +1,93 @@
<!--
* @Author: ch
* @Date: 2022-03-23 10:31:12
* @LastEditors: ch
* @LastEditTime: 2022-03-23 10:31:16
* @Description: file content
-->
<template>
<view class="banner">
<u-swiper :list="bannerList" height="240rpx" indicator indicatorMode="dot"></u-swiper>
<view class="desc">
<view class="desc--item">
<image class="desc--icon" src='@/static/index/bz.png'></image>
<text class="desc--txt">马士兵严选</text>
</view>
<view class="desc--item">
<image class="desc--icon" src='@/static/index/bz.png'></image>
<text class="desc--txt">100%正品</text>
</view>
<view class="desc--item">
<image class="desc--icon" src='@/static/index/bz.png'></image>
<text class="desc--txt">365天质保</text>
</view>
<view class="desc--item">
<image class="desc--icon" src='@/static/index/bz.png'></image>
<text class="desc--txt">售后无忧</text>
</view>
</view>
</view>
</template>
<script>
export default {
data(){
return {
bannerList : []
}
}
}
</script>
<style lang="scss" scoped>
.banner{
height: 298rpx;
position: relative;
padding: 40rpx 30rpx 0;
margin-bottom: 50rpx;
&::before{
display: block;
content: '';
position: absolute;
left: -5vw;
top:0;
height: 231rpx;
width: 110vw;
background: #69ADE5;
border-bottom-left-radius: 100%;
border-bottom-right-radius: 100%;
}
}
.desc{
display: flex;
justify-content: space-between;
margin-top: 24rpx;
&--item{
display: flex;
}
&--icon{
width: 22rpx;
height: 24rpx;
position: relative;
margin-right: 11rpx;
overflow: initial;
// box-shadow: 0 0 5rpx #f00;
&::before{
display: block;
content: '';
position: absolute;
width: 10rpx;
height: 10rpx;
border-radius: 50%;
top: 10rpx;
left: 7rpx;
box-shadow: 0 0 20rpx #FA7204;
background: #FA7204;
z-index: -1;
}
}
&--txt{
font-size: 22rpx;
color: #666;
}
}
</style>

@ -0,0 +1,39 @@
<!--
* @Author: ch
* @Date: 2022-03-23 10:29:07
* @LastEditors: ch
* @LastEditTime: 2022-03-23 10:29:10
* @Description: file content
-->
<template>
<view class="pick">
<view class="pick-max">
</view>
<view class="pick-min"></view>
<view class="pick-min"></view>
</view>
</template>
<style lang="scss" scoped>
.pick{
margin: 0 30rpx;
height: 450rpx;
display: flex;
flex-flow: column wrap;
justify-content: space-between;
&-max{
width: 335rpx;
height: 450rpx;
background: #fff;
border-radius: 8rpx;
}
&-min{
width: 335rpx;
height: 215rpx;
background: #fff;
border-radius: 8rpx;
}
}
</style>

@ -0,0 +1,182 @@
<!--
* @Author: ch
* @Date: 2022-03-23 10:07:48
* @LastEditors: ch
* @LastEditTime: 2022-03-23 10:08:58
* @Description: file content
-->
<template>
<view class="sekill">
<view class="sekill-title">
<view class="sekill-title--left">
<text class="sekill-title--text">限时秒杀</text>
<view class="sekill-title--time">
<text class="sekill-title--time-name">14点场</text>
<image class="sekill-title--time-icon" src="@/static/index/time.png"></image>
<text class="sekill-title--time-time">14:20:30</text>
</view>
</view>
<view class="sekill-title--more" @click="$Router.push('/pages/sekill/index')">
<text>查看更多</text>
<image class="sekill-title--more-icon" src="@/static/common/arrow.png"></image>
</view>
</view>
<view class="sekill--group">
<view>
<image class="sekill--item-img"></image>
<view class="sekill--item-pirce-box">
<text>秒杀价</text>
<text class="sekill--item-pirce">¥29</text>
</view>
<text class="sekill--item-original-pirce">¥50</text>
</view>
<view>
<image class="sekill--item-img"></image>
<view class="sekill--item-pirce-box">
<text>秒杀价</text>
<text class="sekill--item-pirce">¥29</text>
</view>
<text class="sekill--item-original-pirce">¥50</text>
</view>
<view>
<image class="sekill--item-img"></image>
<view class="sekill--item-pirce-box">
<text>秒杀价</text>
<text class="sekill--item-pirce">¥29</text>
</view>
<text class="sekill--item-original-pirce">¥50</text>
</view>
</view>
</view>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
.sekill{
height: 384rpx;
width: 630rpx;
margin: 0 auto;
padding: 30rpx;
background: #fff;
border-radius: 20rpx;
&-title{
height: 38rpx;
display: flex;
justify-content: space-between;
align-items: center;
&--text{
font-size: 36rpx;
}
&--left{
display: flex;
}
&--time{
display: flex;
height: 38rpx;
line-height: 36rpx;
border-radius: 18rpx;
margin-left: 36rpx;
background: #FFEDE9;
overflow: hidden;
align-items: center;
}
&--time-name{
height: 100%;
padding: 0 22rpx 0 15rpx;
background: #FF512B;
font-size: 22rpx;
color: #fff;
position: relative;
&::after{
display: block;
content: '';
width: 16rpx;
height: 36rpx;
background: #FF512B;
transform: rotate(40deg);
position: absolute;
right: -10rpx;
top: -12rpx;
}
}
&--time-time{
margin-right: 15rpx;
font-size: 22rpx;
color: #FF512B;
position: relative;
&::before{
display: block;
content: '';
width: 14rpx;
height: 36rpx;
background: #FFEDE9;
transform: rotate(40deg);
position: absolute;
left: -52rpx;
top: 8rpx;
z-index: 0;
}
}
&--time-icon{
width: 22rpx;
height: 22rpx;
padding-left: 20rpx;
margin-right: 8rpx;
}
&--more{
display: inline-block;
font-size: 24rpx;
color: #999;
}
&--more-icon{
width: 10rpx;
height: 20rpx;
margin-left: 10rpx;
}
}
&--group{
display: flex;
justify-content: space-between;
margin-top: 30rpx;
}
&--item{
&-img{
width: 200rpx;
height: 200rpx;
border-radius: 8rpx;
background: #F8F8F8;
}
&-pirce-box{
width: 140rpx;
height: 44rpx;
line-height: 44rpx;
background: linear-gradient(90deg, #FFE7DE 0%, #FFFFFF 100%);
border-radius: 20rpx;
font-size: 22rpx;
color: #666;
padding: 0 20rpx;
margin: 20rpx 0;
}
&-pirce{
color: #FF512B;
font-size: 32rpx;
}
&-original-pirce{
display: block;
font-size: 22rpx;
color: #999;
text-decoration: line-through;
text-align: center;
}
}
}
</style>

@ -0,0 +1,180 @@
<!--
* @Author: ch
* @Date: 2019-08-22 19:41:20
* @LastEditors: ch
* @LastEditTime: 2022-03-23 20:11:26
* @Description: file content
-->
<script>
import {ApiGetBannerData} from '@/common/api/index.js';
export default {
data() {
return {
bannerList: [],
categoryList : [
{ url : 'bz.png', title : '数码周边'},
{ url : 'bz.png', title : '数码周边'},
{ url : 'bz.png', title : '数码周边'},
{ url : 'bz.png', title : '数码周边'},
{ url : 'bz.png', title : '数码周边'},
{ url : 'bz.png', title : '数码周边'},
]
}
},
async onLoad() {
const {error, result} = await ApiGetBannerData({pageIndex:1, length:999});
this.bannerList = result.concat(result).map(i => i.imgUrl);
},
methods: {
},
}
</script>
<template>
<view>
<view class="header">
<view class="logo" :class="{'logo__min' : scrollTop > 35 }"></view>
<view class="search search__min" v-if="scrollTop > 35" @click="$Router.push('/pages/goods/search')">
<text class="search--input">请输入您想搜索的商品名称</text>
</view>
<view class="msg">
<image class="msg--icon" src="@/static/index/msg.png"></image>
</view>
</view>
<view class="search" @click="$Router.push('/pages/goods/search')">
<text class="search--input">请输入您想搜索的商品名称</text>
</view>
<Banner></Banner>
<view class="category">
<view class="category--item" v-for="item in categoryList" :key="item.url">
<view class="category--image-box">
<image class="category--image" src="@/static/tabbar/home-active.png"></image>
</view>
<text class="category--title">{{item.title}}</text>
</view>
</view>
<Sekill></Sekill>
<view class="title">甄选推荐</view>
<Pick></Pick>
<view class="title">为您推荐</view>
<BsGoodsGroup></BsGoodsGroup>
</view>
</template>
<script>
import BsGoodsGroup from '@/components/BsGoodsGroup';
import Sekill from './Sekill';
import Pick from './Pick';
import Banner from './Banner';
export default {
components : {BsGoodsGroup, Pick, Banner},
data(){
return {
scrollTop : 0
}
},
onLoad(){},
onPageScroll({scrollTop}){
this.scrollTop = scrollTop;
}
}
</script>
<style lang="scss">
page{
background: #f8f8f8;
padding-bottom: 120rpx;
}
.header{
position: sticky;
top: var(--window-top);
z-index:999;
height: 88rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #69ADE5;
padding: 0 30rpx;
}
.logo{
width: 265rpx;
height: 50rpx;
background: url("@/static/index/logo.png");
background-size: 265rpx;
&__min{
width: 50rpx;
}
}
.msg{
&--icon{
width: 32rpx;
height: 38rpx;
}
}
.search{
padding: 0 30rpx;
background: #69ADE5;
&__min{
width: 548rpx;
}
&--input{
display: flex;
height: 70rpx;
align-items: center;
justify-content: space-between;
background: rgba(255, 255, 255, .3);
color: #fff;
border-radius: 8rpx;
padding: 0 30rpx;
font-size: 24rpx;
&::after{
display: block;
content: "";
width: 34rpx;
height: 36rpx;
background: url("@/static/index/search.png");
background-size: 100%;
}
}
&--icon{
width: 34rpx;
height: 36rpx;
}
}
.category{
display: flex;
padding: 0 20rpx;
flex-wrap: wrap;
&--item{
padding: 0 10rpx;
margin-bottom: 40rpx;
}
&--image-box{
width: 120rpx;
height: 120rpx;
}
&--image{
width: 120rpx;
height: 120rpx;
}
&--title{
font-size: 24rpx;
color: #666;
text-align: center;
display: block;
}
}
.title{
font-size: 36rpx;
margin: 60rpx 30rpx 30rpx;
}
</style>

@ -0,0 +1,68 @@
<!--
* @Author: ch
* @Date: 2022-03-21 18:08:07
* @LastEditors: ch
* @LastEditTime: 2022-03-21 18:28:55
* @Description: file content
-->
<template>
<view>
<view class="banner"></view>
<view class="tab">
<view class="tab--item">
<text class="tab--time">16:00</text>
<text>已开抢</text>
</view>
<view class="tab--item tab--item__active">
<text class="tab--time">18:00</text>
<text>已结束</text>
</view>
<view class="tab--item">
<text class="tab--time">20:00</text>
<text>即将开抢</text>
</view>
<view class="tab--item">
<text class="tab--time">20:00</text>
<text>即将开抢</text>
</view>
<view class="tab--item">
<text class="tab--time">20:00</text>
<text>即将开抢</text>
</view>
</view>
<view class="list"></view>
</view>
</template>
<style lang="scss" scoped>
page{
background: #F53739;
}
.banner{
height: 678rpx;
}
.tab{
height:120rpx;
margin: 0 18rpx;
background: linear-gradient(180deg, #FDDBAE 0%, #FEC793 25%, #FFB378 97%, #FFB378 100%);
display: flex;
justify-content: space-around;
align-items: center;
text-align: center;
&--item{
font-size: 24rpx;
line-height: 45rpx;
color: #EF6817;
&__active{
color: #F53739;
}
}
&--time{
display: block;
font-size: 32rpx;
}
}
.list{
margin: 0 20rpx;
padding: 30rpx;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 946 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 525 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 663 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 484 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save