仓库迁移

msb_beta
ch 3 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 <!--
* @Author: ch
马士兵严选APP / h5 * @Date: 2022-03-17 11:30:06
* @LastEditors: ch
## Getting started * @LastEditTime: 2022-03-22 18:07:54
* @Description: file content
To make it easy for you to get started with GitLab, here's a list of recommended next steps. -->
# shopping-app
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: ## 公共方法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 - 请务必使用easycom模式引入第三方组件
git branch -M main - 根目录的components 只放置真正的组件某个页面的业务模块应该在pages的相应目录下新建components目录放置
git push -uf origin main - 所有的自定义组件文件名以大驼峰命名且在templet中使用也用大驼峰形式使用
```
## Integrate with your tools ## 请求
- 所有请求方法命名以Api+请求类型+具体方) 法命名
- [ ] [Set up project integrations](http://internel-git.mashibing.cn/yanxuan-frontend/shop-app/-/settings/integrations) - 所有请求使用ToAsyncAwait 包裹
- 不允许使用try catch 和 then 处理返回结果
## Collaborate with your team ``` js
// 使用示例
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) // xxapi.js
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) import {ToAsyncAwait, ReqestTk} from '@/common/utils'
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) const ApiGetUserInfo = (parapms) => ToAsyncAwait(ReqestTk.get('xxxxUrl',{params}));
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) exprot {
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) // 获取用户信息
ApiGetUserInfo
## Test and Deploy }
// user.vue
Use the built-in continuous integration in GitLab. improt {ApiGetUserInfo} from '@/common/api/xxapi.js';
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) const getUserInfo = async () =>{
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) const {error, result} = await ApiGetUserInfo();
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) if(error){
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) alert(error);
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) return false;
}
*** app.userInfo = result;
}
# Editing this README
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 - 按pages目录结果放置图片
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. - 有公共使用的图片请放到static/common文件夹内
## 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.

@ -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'
// valuekeys
//
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
},
// inputrpx
size: {
type: [Number, String],
default: 26
},
//
color: {
type: String,
default: '#323233'
},
// inputrpx
inputWidth: {
type: [Number, String],
default: 80
},
// inputrpx
inputHeight: {
type: [Number, String],
default: 50
},
// index使numberbox使forindex
index: {
type: [Number, String],
default: ''
},
// disabledOR
// disabledfalsedisabledInputtrue
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) {
// valueinputVal
if(!this.changeFromInner) {
this.inputVal = v1;
// inputValthis.handleChange()changeFromInnertrue
// this.$nextTick
// changeFromInnerfalse
this.$nextTick(function(){
this.changeFromInner = false;
})
}
},
modelValue(v1, v2) {
// valueinputVal
if(!this.changeFromInner) {
this.inputVal = v1;
// inputValthis.handleChange()changeFromInnertrue
// this.$nextTick
// changeFromInnerfalse
this.$nextTick(function(){
this.changeFromInner = false;
})
}
},
inputVal(v1, v2) {
//
if (v1 == '') return;
let value = 0;
// minmax使
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, // 使propsvalueprops
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-90min
// props min0
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;
}
// inputv-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) => {
// truefalse
return isPhone(value)
},
message: '手机号码不正确',
// blurchange
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) {},
// onReadyonLoad
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
// // checkedIdsID
// app.onClearInvalidId()
// })
// .finally(() => app.isLoading = false)
},
// checkedIdsID
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