前端代码第二次修改

master
wbb 2 years ago
parent 0c42142c5a
commit 40cd019818

@ -0,0 +1,160 @@
module.exports = {
env: {
browser: true,
commonjs: true,
es6: true,
},
parserOptions: {
ecmaVersion: 2020,
// ECMAScript modules 模式
sourceType: 'module',
},
extends: ['plugin:prettier/recommended', 'prettier'],
globals: {
wx: true,
App: true,
Page: true,
Component: true,
getApp: true,
getCurrentPages: true,
Behavior: true,
global: true,
__wxConfig: true,
},
ignorePatterns: ['*.wxs'],
rules: {
'prettier/prettier': 'warn',
'no-undef': 'off',
camelcase: ['error', { ignoreDestructuring: true }],
'class-name-casing': 'off',
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
'no-unused-expressions': [
'error',
{ allowShortCircuit: true, allowTernary: true },
],
'no-empty-interface': 'off',
'no-use-before-define': ['error', { functions: false }],
'no-useless-constructor': 'error',
'prefer-const': 'error',
'prefer-destructuring': [
'error',
{
AssignmentExpression: {
array: false,
object: false,
},
VariableDeclarator: {
array: false,
object: true,
},
},
{
enforceForRenamedProperties: false,
},
],
'no-const-assign': 'error',
'no-new-object': 'error',
'no-prototype-builtins': 'error',
'no-array-constructor': 'error',
'array-callback-return': 'warn',
'prefer-template': 'error',
'no-useless-escape': 'error',
'wrap-iife': ['error', 'outside'],
'space-before-function-paren': [
'warn',
{
anonymous: 'always',
named: 'never',
asyncArrow: 'always',
},
],
'no-param-reassign': [
'warn',
{
props: true,
ignorePropertyModificationsFor: [
'acc', // for reduce accumulators
'accumulator', // for reduce accumulators
'e', // for e.returnvalue
'ctx', // for Koa routing
'req', // for Express requests
'request', // for Express requests
'res', // for Express responses
'response', // for Express responses
'$scope', // for Angular 1 scopes
'staticContext', // for ReactRouter context
'state', // for Vuex
],
},
],
'no-confusing-arrow': 'warn',
'no-dupe-class-members': 'error',
'no-iterator': 'warn',
'dot-notation': 'warn',
'one-var': ['warn', 'never'],
'no-multi-assign': 'error',
'no-unused-vars': [
'error',
{
args: 'after-used',
ignoreRestSiblings: true,
argsIgnorePattern: '^_.+',
varsIgnorePattern: '^_.+',
},
],
eqeqeq: ['warn', 'always'],
'no-case-declarations': 'error',
'no-nested-ternary': 'warn',
'no-unneeded-ternary': 'warn',
'no-mixed-operators': [
'error',
{
groups: [
['%', '**'],
['%', '+'],
['%', '-'],
['%', '*'],
['%', '/'],
['&', '|', '<<', '>>', '>>>'],
['==', '!=', '===', '!=='],
['&&', '||'],
],
allowSamePrecedence: false,
},
],
'no-else-return': [
'warn',
{
allowElseIf: false,
},
],
'no-new-wrappers': 'warn',
indent: [
'warn',
2,
{
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
FunctionDeclaration: {
parameters: 1,
body: 1,
},
FunctionExpression: {
parameters: 1,
body: 1,
},
CallExpression: {
arguments: 1,
},
ArrayExpression: 1,
ObjectExpression: 1,
ImportDeclaration: 1,
flatTernaryExpressions: false,
ignoreComments: false,
},
],
'linebreak-style': ['warn', 'unix'],
},
};

@ -0,0 +1,15 @@
node_modules/
yarn-error.log
miniprogram/
miniprogram_npm/
miniprogram_dist/
.DS_Store
$node_modules/
.history/
**/dist
components/**/*.lock
components/**/package-lock.json
package-lock.json
yarn.lock
project.private.config.json
.eslintcache

@ -0,0 +1,9 @@
# 去除注释可以使用代理进行安装
# proxy=http://127.0.0.1:1080
# https_proxy=http://127.0.0.1:1080
# 去除注释可以使用淘宝源
# registry=https://registry.npm.taobao.org
# 去除注释可以使用腾讯源
#registry=http://mirrors.tencent.com/npm/

@ -0,0 +1,3 @@
miniprogram_npm
package.json
project.config.json

@ -0,0 +1,11 @@
{
"useTabs": false,
"printWidth": 80,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"jsxBracketSameLine": false,
"noSemi": true,
"rcVerbose": true,
"endOfLine": "auto"
}

@ -0,0 +1,40 @@
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.enable": true,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll.tslint": true,
"source.fixAll.eslint": true
},
"[javascript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "HookyQR.beautify"
},
"[json]": {
"editor.defaultFormatter": "vscode.json-language-features"
},
"[wxss]": {
"editor.defaultFormatter": "HookyQR.beautify"
},
"wxmlConfig.onSaveFormat": true,
"wxmlConfig.format": {
"brace_style": "collapse",
"indent_inner_html": true,
"indent_scripts": "keep",
"indent_size": 2,
"indent_char": " ",
"unformatted": "['wxs']",
"disable_automatic_closing_labels": false,
"preserve_newlines": true,
"wrap_attributes": "force-expand-multiline",
"wrap_attributes_count": 4,
"wrap_attributes_indent_size": 2
},
"editor.tabSize": 2,
"[wxml]": {
"editor.defaultFormatter": "wechat.miniprogram.wxml-language-features"
},
"[css]": {
"editor.defaultFormatter": "HookyQR.beautify"
}
}

@ -0,0 +1,9 @@
MIT License
Copyright (c) 2021-present TDesign
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

@ -0,0 +1,133 @@
<p align="center">
<a href="https://tdesign.tencent.com/" target="_blank">
<img alt="TDesign Logo" width="200" src="https://tdesign.gtimg.com/site/TDesign.png">
</a>
</p>
<p align="center">
<a href="https://img.shields.io/github/stars/Tencent/tdesign-miniprogram-starter-retail">
<img src="https://img.shields.io/github/stars/Tencent/tdesign-miniprogram-starter-retail" alt="License">
</a>
<a href="https://github.com/Tencent/tdesign-miniprogram-starter-retail/issues">
<img src="https://img.shields.io/github/issues/Tencent/tdesign-miniprogram-starter-retail" alt="License">
</a>
<a href="https://github.com/Tencent/tdesign-miniprogram-starter-retail/LICENSE">
<img src="https://img.shields.io/github/license/Tencent/tdesign-miniprogram-starter-retail" alt="License">
</a>
<a href="https://www.npmjs.com/package/tdesign-miniprogram">
<img src="https://img.shields.io/npm/v/tdesign-miniprogram.svg?sanitize=true" alt="Version">
</a>
<a href="https://www.npmjs.com/package/tdesign-miniprogram">
<img src="https://img.shields.io/npm/dw/tdesign-miniprogram" alt="Downloads">
</a>
</p>
# TDesign 零售行业模版示例小程序
TDesign 零售模版示例小程序采用 [TDesign 企业级设计体系小程序解决方案](https://tdesign.tencent.com/miniprogram/overview) 进行搭建,依赖 [TDesign 微信小程序组件库](https://github.com/Tencent/tdesign-miniprogram),涵盖完整的基本零售场景需求。
## :high_brightness: 预览
<p>请使用微信扫描以下二维码:</p>
<img src="https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-mp/common/qrcode.jpeg" width = "200" height = "200" alt="模版小程序二维码" align=center />
## :pushpin: 项目介绍
### 1. 业务介绍
零售行业模版小程序是个经典的单店版电商小程序,涵盖了电商的黄金链路流程,从商品->购物车->结算->订单等。小程序总共包含 28 个完整的页面,涵盖首页,商品详情页,个人中心,售后流程等基础页面。采用 mock 数据进行展示,提供了完整的零售商品展示、交易与售后流程。页面详情:
<img src="https://cdn-we-retail.ym.tencent.com/tsr/tdesign-starter-readmeV1.png" width = "650" height = "900" alt="模版小程序页面详情" align=center />
主要页面截图如下:
<p align="center">
<img alt="example-home" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v1/home.png" />
<img alt="example-sort" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v2/sort.png" />
<img alt="example-cart" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v1/cart.png" />
<img alt="example-user-center" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v1/user-center.png" />
<img alt="example-goods-detail" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v1/goods-detail.png" />
<img alt="example-pay" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v1/pay.png" />
<img alt="example-order" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v1/order.png" />
<img alt="example-order-detail" width="200" src="https://cdn-we-retail.ym.tencent.com/tsr/example/v2/order.png" />
</p>
### 2. 项目构成
零售行业模版小程序采用基础的 JavaScript + WXSS + ESLint 进行构建,降低了使用门槛。
项目目录结构如下:
```
|-- tdesign-miniprogram-starter
|-- README.md
|-- app.js
|-- app.json
|-- app.wxss
|-- components // 公共组件库
|-- config // 基础配置
|-- custom-tab-bar // 自定义 tabbar
|-- model // mock 数据
|-- pages
| |-- cart // 购物车相关页面
| |-- coupon // 优惠券相关页面
| |-- goods // 商品相关页面
| |-- home // 首页
| |-- order // 订单售后相关页面
| |-- promotion-detail // 营销活动页面
| |-- usercenter // 个人中心及收货地址相关页面
|-- services // 请求接口
|-- style // 公共样式与iconfont
|-- utils // 工具库
```
### 3. 数据模拟
零售小程序采用真实的接口数据,模拟后端返回逻辑,在小程序展示完整的购物场景与购物体验逻辑。
### 4. 添加新页面
1. 在 `pages `目录下创建对应的页面文件夹
2. 在 `app.json` 文件中的 ` "pages"` 数组中加上页面路径
3. [可选] 在 `project.config.json` 文件的 `"miniprogram-list"` 下添加页面配置
## :hammer: 构建运行
1. `npm install`
2. 小程序开发工具中引入工程
3. 构建 npm
## :art: 代码风格控制
`eslint` `prettier`
## :iphone: 基础库版本
最低基础库版本`^2.6.5`
## :dart: 反馈&合作
本开源项目是由[腾讯云Mall团队](https://ym.qq.com/)核心贡献。项目也在[github](https://github.com/Tencent/tdesign-miniprogram-starter-retail)上做了开源有任何问题或者建议都欢迎在issue上留言反馈, 或者加入TD小程序开发者群进行反馈:star2::star2::star2:
<img src="https://cdn.qa.ym.qq.com/officical-site/assets/logo.png?auto=format&fit=max&w=384" width = "100" height = "30" alt="模版小程序页面详情" align=center />
[云Mall](https://ym.qq.com/)是基于微信小程序的电商SaaS产品致力于提供全面、可靠的小程序商城经营服务助力商家成功。支持标准化和定开类型商家入驻。合作洽谈可微信咨询联系`lixingdecai`。
<img src="https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-mp/common/wechat-group.jpg" width = "230" height = "290" alt="模版小程序页面详情" align=center />
## :link: TDesign 其他技术栈实现
- 移动端 小程序 实现:[mobile-miniprogram](https://github.com/Tencent/tdesign-miniprogram)
- 桌面端 Vue 2 实现:[web-vue](https://github.com/Tencent/tdesign-vue)
- 桌面端 Vue 3 实现:[web-vue-next](https://github.com/Tencent/tdesign-vue-next)
- 桌面端 React 实现:[web-react](https://github.com/Tencent/tdesign-react)
## :page_with_curl: 开源协议
TDesign 遵循 [MIT 协议](https://github.com/Tencent/tdesign-miniprogram-starter-retail/LICENSE)。

@ -0,0 +1,8 @@
import updateManager from './common/updateManager';
App({
onLaunch: function () {},
onShow: function () {
updateManager();
},
});

@ -0,0 +1,77 @@
{
"pages": [
"pages/home/home",
"pages/usercenter/index",
"pages/usercenter/person-info/index",
"pages/usercenter/address/list/index",
"pages/usercenter/address/edit/index",
"pages/goods/list/index",
"pages/goods/details/index",
"pages/goods/category/index",
"pages/goods/search/index",
"pages/goods/result/index",
"pages/cart/index",
"pages/order/order-confirm/index",
"pages/order/receipt/index",
"pages/order/pay-result/index",
"pages/order/order-list/index",
"pages/order/order-detail/index",
"pages/goods/comments/index",
"pages/order/apply-service/index",
"pages/order/after-service-list/index",
"pages/order/after-service-detail/index",
"pages/goods/comments/create/index",
"pages/coupon/coupon-list/index",
"pages/coupon/coupon-detail/index",
"pages/coupon/coupon-activity-goods/index",
"pages/promotion-detail/index",
"pages/order/fill-tracking-no/index",
"pages/order/delivery-detail/index",
"pages/order/invoice/index",
"pages/usercenter/name-edit/index",
"pages/preferchoice/preferchoice",
"pages/forum/forum",
"pages/turntable/turntable"
],
"tabBar": {
"custom": true,
"color": "#666666",
"selectedColor": "#FF5F15",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页"
},
{
"pagePath": "pages/forum/forum",
"text": "论坛",
"iconPath": "/images/forum.png",
"selectedIconPath": "/images/forum.png"
},
{
"pagePath": "pages/cart/index",
"text": "购物车"
},
{
"pagePath": "pages/usercenter/index",
"text": "我的"
}
]
},
"lazyCodeLoading": "requiredComponents",
"usingComponents": {},
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#2B4B6B",
"navigationBarTitleText": "节时不节食",
"navigationBarTextStyle": "white"
},
"sitemapLocation": "sitemap.json",
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
}
}

@ -0,0 +1,3 @@
@import 'style/iconfont.wxss';
@import 'style/theme.wxss';

@ -0,0 +1,29 @@
export default () => {
if (!wx.canIUse('getUpdateManager')) {
return;
}
const updateManager = wx.getUpdateManager();
updateManager.onCheckForUpdate(function (res) {
// 请求完新版本信息的回调
console.log('版本信息', res);
});
updateManager.onUpdateReady(function () {
wx.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success(res) {
if (res.confirm) {
// 新的版本已经下载好,调用 applyUpdate 应用新版本并重启
updateManager.applyUpdate();
}
},
});
});
updateManager.onUpdateFailed(function () {
// 新版本下载失败
});
};

@ -0,0 +1,36 @@
Component({
externalClasses: ['wr-class'],
options: {
multipleSlots: true,
},
properties: {
show: {
type: Boolean,
observer(show) {
this.setData({ visible: show });
},
},
closeBtn: {
type: Boolean,
value: false,
},
},
data: { visible: false },
methods: {
reset() {
this.triggerEvent('reset');
},
confirm() {
this.triggerEvent('confirm');
},
close() {
this.triggerEvent('showFilterPopupClose');
this.setData({ visible: false });
},
},
});

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-popup": "tdesign-miniprogram/popup/popup"
}
}

@ -0,0 +1,18 @@
<t-popup
visible="{{visible}}"
placement="right"
bind:visible-change="close"
data-index="5"
close-btn="{{closeBtn}}"
>
<view class="content">
<slot name="filterSlot" />
<view class="filter-btns-wrap">
<view class="filter-btn btn-reset" bind:tap="reset">重置</view>
<view class="filter-btn btn-confirm" bind:tap="confirm" data-index="5">
确定
</view>
</view>
</view>
</t-popup>

@ -0,0 +1,39 @@
.content .filter-btns-wrap {
width: 100%;
position: absolute;
bottom: calc(20rpx + env(safe-area-inset-bottom));
display: flex;
flex-direction: row;
border-radius: 10rpx 0 0 10rpx;
padding: 16rpx 32rpx;
border-top: 1rpx solid #e5e5e5;
box-sizing: border-box;
}
.filter-btn {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 28rpx;
font-weight: 500;
height: 80rpx;
}
.btn-reset {
color: #fa4126;
background: rgba(255, 255, 255, 1);
position: relative;
border: 1rpx solid #fa4126;
border-radius: 84rpx 0 0 84rpx;
}
.btn-confirm {
border-radius: 0 84rpx 84rpx 0;
border: 1rpx solid #fa4126;
}
.btn-confirm {
color: #fff;
background: #fa4126;
}

@ -0,0 +1,84 @@
Component({
externalClasses: ['wr-class'],
options: {
multipleSlots: true,
},
properties: {
overall: {
type: Number,
value: 1,
observer(overall) {
this.setData({
overall,
});
},
},
layout: {
type: Number,
value: 1,
observer(layout) {
this.setData({
layout,
});
},
},
sorts: {
type: String,
value: '',
observer(sorts) {
this.setData({
sorts,
});
},
},
color: {
type: String,
value: '#FA550F',
},
},
data: {
layout: 1,
overall: 1,
sorts: '',
},
methods: {
onChangeShowAction() {
const { layout } = this.data;
const nextLayout = layout === 1 ? 0 : 1;
this.triggerEvent('change', { ...this.properties, layout: nextLayout });
},
handlePriseSort() {
const { sorts } = this.data;
this.triggerEvent('change', {
...this.properties,
overall: 0,
sorts: sorts === 'desc' ? 'asc' : 'desc',
});
},
open() {
this.triggerEvent('showFilterPopup', {
show: true,
});
},
onOverallAction() {
const { overall } = this.data;
const nextOverall = overall === 1 ? 0 : 1;
const nextData = {
sorts: '',
prices: [],
};
this.triggerEvent('change', {
...this.properties,
...nextData,
overall: nextOverall,
});
},
},
});

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-icon": "tdesign-miniprogram/icon/icon"
}
}

@ -0,0 +1,37 @@
<!-- 过滤组件 -->
<view class="wr-class filter-wrap">
<view class="filter-left-content">
<view class="filter-item {{overall === 1 ? 'filter-active-item' : ''}}" bindtap="onOverallAction">
综合
</view>
<view class="filter-item" bind:tap="handlePriseSort">
<text style="color: {{sorts !== '' ? color : '' }}">价格</text>
<view class="filter-price">
<t-icon
prefix="wr"
name="arrow_drop_up"
size="18rpx"
style="color:{{sorts === 'asc' ? color : '#bbb'}}"
/>
<t-icon
prefix="wr"
name="arrow_drop_down"
size="18rpx"
style="color:{{sorts === 'desc' ? color : '#bbb'}}"
/>
</view>
</view>
<view class="filter-item {{prices.length ? 'filter-active-item' : ''}}" bindtap="open" data-index="5">
筛选
<t-icon
name="filter"
prefix="wr"
color="#333"
size="32rpx"
/>
</view>
</view>
</view>
<!-- 筛选弹框 -->
<slot name="filterPopup" />

@ -0,0 +1,50 @@
.filter-wrap {
width: 100%;
height: 88rpx;
display: flex;
justify-content: space-between;
position: relative;
background: #fff;
}
.filter-right-content {
height: 100%;
flex-basis: 100rpx;
text-align: center;
line-height: 88rpx;
}
.filter-left-content {
height: 100%;
display: flex;
flex-grow: 2;
flex-flow: row nowrap;
justify-content: space-between;
}
.filter-left-content .filter-item {
flex: 1;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
line-height: 36rpx;
font-weight: 400;
color: rgba(51, 51, 51, 1);
}
.filter-left-content .filter-item .filter-price {
display: flex;
flex-direction: column;
margin-left: 6rpx;
justify-content: space-between;
}
.filter-left-content .filter-item .wr-filter {
margin-left: 8rpx;
}
.filter-left-content .filter-active-item {
color: #fa550f;
}

@ -0,0 +1,141 @@
Component({
options: {
addGlobalClass: true,
},
properties: {
id: {
type: String,
value: '',
observer(id) {
this.genIndependentID(id);
if (this.properties.thresholds?.length) {
this.createIntersectionObserverHandle();
}
},
},
data: {
type: Object,
observer(data) {
if (!data) {
return;
}
let isValidityLinePrice = true;
if (data.originPrice && data.price && data.originPrice < data.price) {
isValidityLinePrice = false;
}
this.setData({ goods: data, isValidityLinePrice });
},
},
currency: {
type: String,
value: '¥',
},
thresholds: {
type: Array,
value: [],
observer(thresholds) {
if (thresholds && thresholds.length) {
this.createIntersectionObserverHandle();
} else {
this.clearIntersectionObserverHandle();
}
},
},
},
data: {
independentID: '',
goods: { id: '' },
isValidityLinePrice: false,
},
lifetimes: {
ready() {
this.init();
},
detached() {
this.clear();
},
},
pageLifeTimes: {},
methods: {
clickHandle() {
this.triggerEvent('click', { goods: this.data.goods });
},
clickThumbHandle() {
this.triggerEvent('thumb', { goods: this.data.goods });
},
addCartHandle(e) {
const { id } = e.currentTarget;
const { id: cardID } = e.currentTarget.dataset;
this.triggerEvent('add-cart', {
...e.detail,
id,
cardID,
goods: this.data.goods,
});
},
genIndependentID(id) {
let independentID;
if (id) {
independentID = id;
} else {
independentID = `goods-card-${~~(Math.random() * 10 ** 8)}`;
}
this.setData({ independentID });
},
init() {
const { thresholds, id } = this.properties;
this.genIndependentID(id);
if (thresholds && thresholds.length) {
this.createIntersectionObserverHandle();
}
},
clear() {
this.clearIntersectionObserverHandle();
},
intersectionObserverContext: null,
createIntersectionObserverHandle() {
if (this.intersectionObserverContext || !this.data.independentID) {
return;
}
this.intersectionObserverContext = this.createIntersectionObserver({
thresholds: this.properties.thresholds,
}).relativeToViewport();
this.intersectionObserverContext.observe(
`#${this.data.independentID}`,
(res) => {
this.intersectionObserverCB(res);
},
);
},
intersectionObserverCB() {
this.triggerEvent('ob', {
goods: this.data.goods,
context: this.intersectionObserverContext,
});
},
clearIntersectionObserverHandle() {
if (this.intersectionObserverContext) {
try {
this.intersectionObserverContext.disconnect();
} catch (e) {}
this.intersectionObserverContext = null;
}
},
},
});

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"price": "/components/price/index",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-image": "/components/webp-image/index"
}
}

@ -0,0 +1,63 @@
<view
id="{{independentID}}"
class="goods-card"
bind:tap="clickHandle"
data-goods="{{ goods }}"
>
<view class="goods-card__main">
<view class="goods-card__thumb" bind:tap="clickThumbHandle">
<t-image
wx:if="{{ !!goods.thumb }}"
t-class="goods-card__img"
src="{{ goods.thumb }}"
mode="aspectFill"
lazy-load
/>
</view>
<view class="goods-card__body">
<view class="goods-card__upper">
<view wx:if="{{ goods.title }}" class="goods-card__title">
{{ goods.title }}
</view>
<view wx:if="{{ goods.tags && !!goods.tags.length }}" class="goods-card__tags">
<view
wx:for="{{ goods.tags }}"
wx:key="index"
wx:for-item="tag"
class="goods-card__tag"
data-index="{{index}}"
>
{{tag}}
</view>
</view>
</view>
<view class="goods-card__down">
<price
wx:if="{{ goods.price }}"
wr-class="spec-for-price"
symbol-class="spec-for-symbol"
symbol="{{currency}}"
price="{{goods.price}}"
/>
<price
wx:if="{{ goods.originPrice && isValidityLinePrice }}"
wr-class="goods-card__origin-price"
symbol="{{currency}}"
price="{{goods.originPrice}}"
type="delthrough"
/>
<t-icon
class="goods-card__add-cart"
prefix="wr"
name="cartAdd"
id="{{independentID}}-cart"
data-id="{{independentID}}"
catchtap="addCartHandle"
size="48rpx"
color="#FA550F"
/>
</view>
</view>
</view>
</view>

@ -0,0 +1,133 @@
.goods-card {
box-sizing: border-box;
font-size: 24rpx;
border-radius: 0 0 16rpx 16rpx;
border-bottom: none;
}
.goods-card__main {
position: relative;
display: flex;
line-height: 1;
padding: 0;
background: transparent;
width: 342rpx;
border-radius: 0 0 16rpx 16rpx;
align-items: center;
justify-content: center;
margin-bottom: 16rpx;
flex-direction: column;
}
.goods-card__thumb {
flex-shrink: 0;
position: relative;
width: 340rpx;
height: 340rpx;
}
.goods-card__thumb:empty {
display: none;
margin: 0;
}
.goods-card__img {
display: block;
width: 100%;
height: 100%;
border-radius: 16rpx 16rpx 0 0;
overflow: hidden;
}
.goods-card__body {
display: flex;
flex: 1 1 auto;
background: #fff;
border-radius: 0 0 16rpx 16rpx;
padding: 16rpx 24rpx 18rpx;
flex-direction: column;
}
.goods-card__upper {
display: flex;
flex-direction: column;
overflow: hidden;
flex: 1 1 auto;
}
.goods-card__title {
flex-shrink: 0;
font-size: 28rpx;
color: #333;
font-weight: 400;
display: -webkit-box;
height: 72rpx;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-word;
line-height: 36rpx;
}
.goods-card__tags {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin: 8rpx 0 0 0;
}
.goods-card__tag {
color: #fa4126;
background: transparent;
font-size: 20rpx;
border: 1rpx solid #fa4126;
padding: 0 8rpx;
border-radius: 16rpx;
line-height: 30rpx;
margin: 0 8rpx 8rpx 0;
display: block;
overflow: hidden;
white-space: nowrap;
word-break: keep-all;
text-overflow: ellipsis;
}
.goods-card__down {
display: flex;
position: relative;
flex-direction: row;
justify-content: flex-start;
align-items: baseline;
line-height: 32rpx;
margin: 8rpx 0 0 0;
}
.goods-card__origin-price {
white-space: nowrap;
font-weight: 700;
order: 2;
color: #bbbbbb;
font-size: 24rpx;
margin: 0 0 0 8rpx;
}
.goods-card__add-cart {
order: 3;
margin: auto 0 0 auto;
position: absolute;
bottom: 0;
right: 0;
}
.spec-for-price {
font-size: 36rpx;
white-space: nowrap;
font-weight: 700;
order: 1;
color: #fa4126;
margin: 0;
}
.spec-for-symbol {
font-size: 24rpx;
}

@ -0,0 +1,62 @@
Component({
externalClasses: ['wr-class'],
properties: {
goodsList: {
type: Array,
value: [],
},
id: {
type: String,
value: '',
observer: (id) => {
this.genIndependentID(id);
},
},
thresholds: {
type: Array,
value: [],
},
},
data: {
independentID: '',
},
lifetimes: {
ready() {
this.init();
},
},
methods: {
onClickGoods(e) {
const { index } = e.currentTarget.dataset;
this.triggerEvent('click', { ...e.detail, index });
},
onAddCart(e) {
const { index } = e.currentTarget.dataset;
this.triggerEvent('addcart', { ...e.detail, index });
},
onClickGoodsThumb(e) {
const { index } = e.currentTarget.dataset;
this.triggerEvent('thumb', { ...e.detail, index });
},
init() {
this.genIndependentID(this.id || '');
},
genIndependentID(id) {
if (id) {
this.setData({ independentID: id });
} else {
this.setData({
independentID: `goods-list-${~~(Math.random() * 10 ** 8)}`,
});
}
},
},
});

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"goods-card": "/components/goods-card/index"
}
}

@ -0,0 +1,16 @@
<view class="goods-list-wrap wr-class" id="{{independentID}}">
<block wx:for="{{goodsList}}" wx:for-item="item" wx:key="index">
<goods-card
id="{{independentID}}-gd-{{index}}"
data="{{item}}"
currency="{{item.currency || '¥'}}"
thresholds="{{thresholds}}"
class="goods-card-inside"
data-index="{{index}}"
bind:thumb="onClickGoodsThumb"
bind:click="onClickGoods"
bind:add-cart="onAddCart"
/>
</block>
</view>

@ -0,0 +1,7 @@
.goods-list-wrap {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
padding: 0;
background: #fff;
}

@ -0,0 +1,54 @@
Component({
externalClasses: ['wr-class', 'wr-class--no-more'],
options: { multipleSlots: true },
properties: {
status: {
type: Number,
value: 0,
},
loadingText: {
type: String,
value: '加载中...',
},
noMoreText: {
type: String,
value: '没有更多了',
},
failedText: {
type: String,
value: '加载失败,点击重试',
},
color: {
type: String,
value: '#BBBBBB',
},
failedColor: {
type: String,
value: '#FA550F',
},
size: {
type: null,
value: '40rpx',
},
loadingBackgroundColor: {
type: String,
value: '#F5F5F5',
},
listIsEmpty: {
type: Boolean,
value: false,
},
},
methods: {
/** 点击处理 */
tapHandle() {
// 失败重试
if (this.data.status === 3) {
this.triggerEvent('retry');
}
},
},
});

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"t-loading": "tdesign-miniprogram/loading/loading",
"t-divider": "tdesign-miniprogram/divider/divider"
}
}

@ -0,0 +1,30 @@
<view class="load-more wr-class" style="{{listIsEmpty && (status === 0 || status === 2) ? 'display: none' : '' }}" bindtap="tapHandle">
<!-- 加载中 -->
<t-loading
t-class="t-class-loading"
t-class-text="t-class-loading-text"
t-class-indicator="t-class-indicator"
loading="{{status === 1}}"
text="加载中..."
theme="circular"
size="40rpx"
/>
<!-- 已全部加载 -->
<t-divider wx:if="{{status === 2}}" t-class="t-class-divider" t-class-content="t-class-divider-content">
<text slot="content">{{noMoreText}}</text>
</t-divider>
<!-- 加载失败 -->
<t-loading
t-class="t-class-loading"
theme="error"
loading="{{status===3}}"
bind:reload="tapHandle"
/>
</view>
<!-- 支持通过slot传入页面/列表的空态load-more来控制空态的显示状态 -->
<slot wx:if="{{listIsEmpty && (status === 0 || status === 2)}}" name="empty" />

@ -0,0 +1,24 @@
.load-more {
font-size: 24rpx;
height: 100rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.load-more .t-class-loading {
display: flex;
justify-content: center;
}
.load-more .t-class-loading-text {
color: #bbbbbb;
}
.t-class-divider-content {
margin: 0 10rpx;
color: #bbbbbb;
}
.load-more .t-class-indicator {
color: #b9b9b9 !important;
}

@ -0,0 +1,23 @@
Component({
externalClasses: ['wr-class'],
properties: {
position: {
type: String,
value: 'static',
},
noMask: Boolean,
type: {
type: String,
value: 'circular',
},
vertical: Boolean,
size: {
type: String,
value: '50rpx',
},
backgroundColor: {
type: String,
value: 'rgba(0, 0, 0, .6)',
},
},
});

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-loading": "tdesign-miniprogram/loading/loading"
}
}

@ -0,0 +1,11 @@
<view class="t-class loading-content {{position}}" style="{{(position === 'static' || noMask) ? 'visibility: hidden;' : ''}} background-color: {{backgroundColor}}">
<t-loading
t-class="loading"
theme="{{type}}"
layout="{{vertical}}"
size="{{size}}"
>
<slot/>
</t-loading>
</view>

@ -0,0 +1,23 @@
.loading-content {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.6);
position: relative;
}
.loading-content.absolute {
position: absolute;
z-index: 1;
left: 0;
top: 0;
}
.loading-content.fixed {
position: fixed;
z-index: 1;
left: 0;
top: 0;
}
.loading-content .loading {
width: 100%;
height: 100%;
visibility: visible;
}

@ -0,0 +1,71 @@
Component({
externalClasses: ['wr-class', 'symbol-class', 'decimal-class'],
useStore: [],
properties: {
priceUnit: {
type: String,
value: 'fen',
}, // 价格单位,分 | 元, fenyuan
price: {
type: null,
value: '',
observer(price) {
this.format(price);
},
}, // 价格, 以分为单位
type: {
type: String,
value: '', //
}, // main 粗体, lighter 细体, mini 黑色, del 中划线, delthrough 中划线,包括货币符号
symbol: {
type: String,
value: '¥', // '¥',
}, // 货币符号,默认是人民币符号¥
fill: Boolean, // 是否自动补齐两位小数
decimalSmaller: Boolean, // 小数字号小一点
lineThroughWidth: {
type: null,
value: '0.12em',
}, // 划线价线条高度
},
data: {
pArr: [],
},
methods: {
format(price) {
price = parseFloat(`${price}`);
const pArr = [];
if (!isNaN(price)) {
const isMinus = price < 0;
if (isMinus) {
price = -price;
}
if (this.properties.priceUnit === 'yuan') {
const priceSplit = price.toString().split('.');
pArr[0] = priceSplit[0];
pArr[1] = !priceSplit[1]
? '00'
: priceSplit[1].length === 1
? `${priceSplit[1]}0`
: priceSplit[1];
} else {
price = Math.round(price * 10 ** 8) / 10 ** 8; // 恢复精度丢失
price = Math.ceil(price); // 向上取整
pArr[0] = price >= 100 ? `${price}`.slice(0, -2) : '0';
pArr[1] = `${price + 100}`.slice(-2);
}
if (!this.properties.fill) {
// 如果 fill 为 false 不显示小数末尾的0
if (pArr[1] === '00') pArr[1] = '';
else if (pArr[1][1] === '0') pArr[1] = pArr[1][0];
}
if (isMinus) {
pArr[0] = `-${pArr[0]}`;
}
}
this.setData({ pArr });
},
},
});

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

@ -0,0 +1,21 @@
<wxs module="utils">
var REGEXP = getRegExp('^\d+(\.\d+)?$');
function addUnit(value) {
if (value == null) {
return '';
}
return REGEXP.test('' + value) ? value + 'rpx' : value;
}
module.exports = {
addUnit: addUnit
};
</wxs>
<view class="price {{type}} wr-class">
<view wx:if="{{type === 'delthrough'}}" class="line" style="height:{{utils.addUnit(lineThroughWidth)}};" />
<view class="symbol symbol-class">{{symbol}}</view>
<view class="pprice">
<view class="integer inline">{{pArr[0]}}</view>
<view wx:if="{{pArr[1]}}" class="decimal inline {{decimalSmaller ? 'smaller' : ''}} decimal-class">.{{pArr[1]}}</view>
</view>
</view>

@ -0,0 +1,66 @@
:host {
display: inline-block;
display: inline-block;
font-weight: inherit;
}
.inline {
display: inline;
white-space: nowrap;
}
.price {
display: inline;
color: inherit;
font-size: inherit;
text-decoration: inherit;
}
.lighter {
font-weight: 400;
font-size: 32rpx;
}
.mini {
font-size: 24rpx;
color: #5d5d5d;
font-weight: 400;
}
.del .pprice {
font-size: 32rpx;
color: #9b9b9b;
text-decoration: line-through;
font-weight: 400;
}
.delthrough {
position: relative;
}
.delthrough .line {
position: absolute;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
margin: 0;
background-color: currentColor;
}
.symbol {
display: inline;
color: inherit;
font-size: inherit;
font-size: 0.8em;
}
.pprice {
display: inline;
margin: 0 0 0 4rpx;
}
.integer {
color: inherit;
font-size: inherit;
}
.decimal {
color: inherit;
font-size: inherit;
}
.decimal.smaller {
font-size: 0.8em;
vertical-align: baseline;
}

@ -0,0 +1,79 @@
let ARRAY = [];
Component({
externalClasses: ['wr-class'],
options: {
multipleSlots: true,
},
properties: {
disabled: Boolean,
leftWidth: {
type: Number,
value: 0,
},
rightWidth: {
type: Number,
value: 0,
},
asyncClose: Boolean,
},
attached() {
ARRAY.push(this);
},
detached() {
ARRAY = ARRAY.filter((item) => item !== this);
},
/**
* Component initial data
*/
data: {
wrapperStyle: '',
asyncClose: false,
closed: true,
},
/**
* Component methods
*/
methods: {
open(position) {
this.setData({ closed: false });
this.triggerEvent('close', {
position,
instance: this,
});
},
close() {
this.setData({ closed: true });
},
closeOther() {
ARRAY.filter((item) => item !== this).forEach((item) => item.close());
},
noop() {
return;
},
onClick(event) {
const { key: position = 'outside' } = event.currentTarget.dataset;
this.triggerEvent('click', position);
if (this.data.closed) {
return;
}
if (this.data.asyncClose) {
this.triggerEvent('close', {
position,
instance: this,
});
} else {
this.close();
}
},
},
});

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

@ -0,0 +1,174 @@
<wxs module="swipe">
var THRESHOLD = 0.3;
var MIN_DISTANCE = 10;
var owner;
var state;
var getState = function(ownerInstance) {
owner = ownerInstance;
state = owner.getState();
state.leftWidth = state.leftWidth || 0;
state.rightWidth = state.rightWidth || 0;
state.offset = state.offset || 0;
state.startOffset = state.startOffset || 0;
};
var initRightWidth = function(newVal, oldVal, ownerInstance) {
getState(ownerInstance);
state.rightWidth = newVal;
if (state.offset < 0) {
swipeMove(-state.rightWidth);
}
};
var initLeftWidth = function(newVal, oldVal, ownerInstance) {
getState(ownerInstance);
state.leftWidth = newVal;
if (state.offset > 0) {
swipeMove(state.leftWidth);
}
}
var resetTouchStatus = function() {
state.direction = '';
state.deltaX = 0;
state.deltaY = 0;
state.offsetX = 0;
state.offsetY = 0;
};
var touchMove = function(event) {
var touchPoint = event.touches[0];
state.deltaX = touchPoint.clientX - state.startX;
state.deltaY = touchPoint.clientY - state.startY;
state.offsetX = Math.abs(state.deltaX);
state.offsetY = Math.abs(state.deltaY);
state.direction = state.direction || getDirection(state.offsetX, state.offsetY);
};
var getDirection = function(x, y) {
if (x > y && x > MIN_DISTANCE) {
return 'horizontal';
}
if (y > x && y > MIN_DISTANCE) {
return 'vertical';
}
return '';
};
var range = function(num, min, max) {
return Math.min(Math.max(num, min), max);
};
var swipeMove = function(_offset = 0) {
state.offset = range(
_offset,
-state.rightWidth,
+state.leftWidth,
);
var transform = 'translate3d(' + state.offset + 'px, 0, 0)';
var transition = state.dragging
? 'none'
: 'transform .6s cubic-bezier(0.18, 0.89, 0.32, 1)';
owner.selectComponent('#wrapper').setStyle({
'-webkit-transform': transform,
'-webkit-transition': transition,
'transform': transform,
'transition': transition
});
};
var close = function() {
swipeMove(0);
};
var onCloseChange = function(newVal, oldVal, ownerInstance) {
getState(ownerInstance);
if (newVal === oldVal) return;
if (newVal) {
close();
}
};
var touchStart = function(event) {
resetTouchStatus();
state.startOffset = state.offset;
var touchPoint = event.touches[0];
state.startX = touchPoint.clientX;
state.startY = touchPoint.clientY;
owner.callMethod('closeOther');
};
var startDrag = function(event, ownerInstance) {
getState(ownerInstance);
touchStart(event);
};
var onDrag = function(event, ownerInstance) {
getState(ownerInstance);
touchMove(event);
if (state.direction !== 'horizontal') {
return;
}
state.dragging = true;
swipeMove(state.startOffset + state.deltaX);
};
var open = function(position) {
var _offset = position === 'left' ? +state.leftWidth : -state.rightWidth;
owner.callMethod('open', { position: position });
swipeMove(_offset);
};
var endDrag = function(event, ownerInstance) {
getState(ownerInstance);
state.dragging = false;
// 左/右侧有可滑动区域且当前不是已open状态且滑动幅度超过阈值时open左/右侧(滚动到该侧的最边上)
if (+state.rightWidth > 0 && -state.startOffset < +state.rightWidth && -state.offset > +state.rightWidth * THRESHOLD) {
open('right');
} else if (+state.leftWidth > 0 && state.startOffset < +state.leftWidth && state.offset > +state.leftWidth * THRESHOLD) {
open('left');
} else {
// 仅在有发生侧滑的情况下自动关闭由js控制是否异步关闭
if (state.startOffset !== state.offset) {
close();
}
}
};
module.exports = {
initLeftWidth: initLeftWidth,
initRightWidth: initRightWidth,
startDrag: startDrag,
onDrag: onDrag,
endDrag: endDrag,
onCloseChange: onCloseChange
};
</wxs>
<view
class="wr-class wr-swipeout"
data-key="cell"
capture-bind:tap="onClick"
bindtouchstart="{{disabled || swipe.startDrag}}"
capture-bind:touchmove="{{disabled || swipe.onDrag}}"
bindtouchend="{{disabled || swipe.endDrag}}"
bindtouchcancel="{{disabled || swipe.endDrag}}"
closed="{{closed}}"
change:closed="{{swipe.onCloseChange}}"
leftWidth="{{leftWidth}}"
rightWidth="{{rightWidth}}"
change:leftWidth="{{swipe.initLeftWidth}}"
change:rightWidth="{{swipe.initRightWidth}}"
>
<view id="wrapper">
<view wx:if="{{ leftWidth }}" class="wr-swipeout__left" data-key="left" catch:tap="onClick">
<slot name="left" />
</view>
<slot />
<view wx:if="{{ rightWidth }}" class="wr-swipeout__right" data-key="right" catch:tap="onClick">
<slot name="right" />
</view>
</view>
</view>

@ -0,0 +1,18 @@
.wr-swipeout {
position: relative;
overflow: hidden;
}
.wr-swipeout__left,
.wr-swipeout__right {
position: absolute;
top: 0;
height: 100%;
}
.wr-swipeout__left {
left: 0;
transform: translate3d(-100%, 0, 0);
}
.wr-swipeout__right {
right: 0;
transform: translate3d(100%, 0, 0);
}

@ -0,0 +1,86 @@
/*
* @Author: rileycai
* @Date: 2022-03-14 14:21:26
* @LastEditTime: 2022-03-14 15:23:04
* @LastEditors: rileycai
* @Description: webp-image组件对t-image包裹了一层主要实现图片裁剪webp压缩功能
* @FilePath: /tdesign-miniprogram-starter/components/webp-image/index.js
*/
const systemInfo = wx.getSystemInfoSync();
Component({
externalClasses: ['t-class', 't-class-load'],
properties: {
loadFailed: {
type: String,
value: 'default',
},
loading: {
type: String,
value: 'default',
},
src: {
type: String,
value: '',
},
mode: {
type: String,
value: 'aspectFill',
},
webp: {
type: Boolean,
value: true,
},
lazyLoad: {
type: Boolean,
value: false,
},
showMenuByLongpress: {
type: Boolean,
value: false,
},
},
data: {
thumbHeight: 375,
thumbWidth: 375,
systemInfo,
},
lifetimes: {
ready() {
const { mode } = this.properties;
// 获取容器的真实宽高,设置图片的裁剪宽度
this.getRect('.J-image').then((res) => {
if (res) {
const { width, height } = res;
this.setData(
mode === 'heightFix'
? {
thumbHeight: this.px2rpx(height) || 375,
}
: {
thumbWidth: this.px2rpx(width) || 375,
},
);
}
});
},
},
methods: {
px2rpx(px) {
return (750 / (systemInfo.screenWidth || 375)) * px;
},
getRect(selector) {
return new Promise((resolve) => {
if (!this.selectorQuery) {
this.selectorQuery = this.createSelectorQuery();
}
this.selectorQuery.select(selector).boundingClientRect(resolve).exec();
});
},
onLoad(e) {
this.triggerEvent('load', e.detail);
},
onError(e) {
this.triggerEvent('error', e.detail);
},
},
});

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-image": "tdesign-miniprogram/image/image"
}
}

@ -0,0 +1,14 @@
<wxs src="./utils.wxs" module="Utils" />
<t-image
class="J-image"
src="{{Utils.getSrc({src, thumbWidth: thumbWidth || 0, thumbHeight: thumbHeight || 0, systemInfo, webp, mode})}}"
t-class="t-class"
t-class-load="t-class-load"
mode="{{ mode }}"
lazy="{{ lazyLoad }}"
show-menu-by-longpress="{{showMenuByLongpress}}"
error="{{loadFailed}}"
loading="{{loading}}"
binderror="onError"
bindload="onLoad"
/>

@ -0,0 +1,140 @@
var isString = function (value) {
return typeof value === 'string';
};
var isNumber = function (value) {
return typeof value === 'number';
};
var getFileExt = function (src) {
var fileUrl = src.split('?')[0];
var splitUlr = fileUrl.split('/');
var filepath = splitUlr[splitUlr.length - 1];
return filepath.split('.')[1] || 'jpg';
};
function isUrl(url) {
// NOCC:ToolNameCheck(非敏感词)
var urlReg = getRegExp(
'/[(http(s)?)://(www.)?a-zA-Z0-9@:%._+~#=]{2,256}.[a-z]{2,6}\b([-a-zA-Z0-9@:%_+.~#?&//=]*)/',
'ig',
);
return urlReg.test(url);
}
function rpx2px(rpx, screenWidth) {
// px / systemWidth = rpx / 750
var result = (rpx * (screenWidth || 375)) / 750;
return Math.round(result);
}
function imageMogr(url, options) {
if (!isString(url) || !url) return '';
if (
url.indexOf('qlogo.cn') !== -1 ||
url.indexOf('wxfile://') === 0 ||
url.indexOf('http://tmp/wx') === 0 ||
url.indexOf('imageMogr2') !== -1
) {
//qlogo.cn域名或者本地图片不做转换
return url;
} //强制转https
if (url.indexOf('http://') === 0) {
url = url.replace('http://', 'https://');
} else if (url.indexOf('//') === 0) {
url = 'https:' + url;
}
if (!options) return url;
var width = Math.ceil(options.width),
height = Math.ceil(options.height),
format = options.format,
_optionsQuality = options.quality,
quality = _optionsQuality === undefined ? 70 : _optionsQuality,
_optionsStrip = options.strip,
strip = _optionsStrip === undefined ? true : _optionsStrip,
crop = options.crop;
var isValidWidth = isNumber(width) && width > 0;
var isValidHeight = isNumber(height) && height > 0;
var imageMogrStr = '';
var size = '';
if (isValidWidth && isValidHeight) {
size = ''.concat(width, 'x').concat(height);
} else if (isValidWidth) {
size = ''.concat(width, 'x');
} else if (isValidHeight) {
size = 'x'.concat(height);
}
if (size) {
//缩放或者裁剪
imageMogrStr += '/'.concat(crop ? 'crop' : 'thumbnail', '/').concat(size);
if (crop) {
//裁剪目前需求只有以图片中心为基准
imageMogrStr += '/gravity/center';
}
}
if (isNumber(quality)) {
//质量变换
imageMogrStr += '/quality/'.concat(quality);
}
if (strip) {
//去除元信息
imageMogrStr += '/strip';
}
var ext = getFileExt(url);
// gif 图片不做格式转换,否则会损坏动图
if (ext === 'gif') {
imageMogrStr += '/cgif/1';
} else if (format) {
//格式转换
imageMogrStr += '/format/'.concat(format);
}
if (format === 'jpg' || (!format && (ext === 'jpg' || ext === 'jpeg'))) {
//渐进式 jpg 加载
imageMogrStr += '/interlace/1';
}
if (!imageMogrStr) return url;
return ''
.concat(url)
.concat(url.indexOf('?') !== -1 ? '&' : '?', 'imageMogr2')
.concat(imageMogrStr);
}
function getSrc(options) {
if (!options.src) return '';
if (options.thumbWidth || options.thumbHeight) {
return imageMogr(options.src, {
width:
options.mode !== 'heightFix'
? rpx2px(options.thumbWidth, options.systemInfo.screenWidth) *
options.systemInfo.pixelRatio
: null,
height:
options.mode !== 'widthFix'
? rpx2px(options.thumbHeight, options.systemInfo.screenWidth) *
options.systemInfo.pixelRatio
: null,
format: options.webp ? 'webp' : null,
});
}
return '';
}
module.exports = {
imageMogr: imageMogr,
getSrc: getSrc,
};

@ -0,0 +1,91 @@
/* eslint-disable prefer-template */
/**
* 工程代码pre-commit 检查工具
* @date 2019.9.4
* @author 310227663@qq.com
*/
const { exec } = require('child_process');
const chalk = require('chalk');
const { CLIEngine } = require('eslint');
const cli = new CLIEngine({});
const { log } = console;
function getErrorLevel(number) {
switch (number) {
case 2:
return 'error';
case 1:
return 'warn';
default:
}
return 'undefined';
}
let pass = 0;
exec(
'git diff --cached --name-only --diff-filter=ACM | grep -Ei "\\.ts$|\\.js$"',
(error, stdout) => {
if (stdout.length) {
const array = stdout.split('\n');
array.pop();
const { results } = cli.executeOnFiles(array);
let errorCount = 0;
let warningCount = 0;
results.forEach((result) => {
errorCount += result.errorCount;
warningCount += result.warningCount;
if (result.messages.length > 0) {
log('\n');
log(result.filePath);
result.messages.forEach((obj) => {
const level = getErrorLevel(obj.severity);
if (level === 'warn')
log(
' ' +
obj.line +
':' +
obj.column +
'\t ' +
chalk.yellow(level) +
' \0 ' +
obj.message +
'\t\t' +
chalk.grey(obj.ruleId) +
'',
);
if (level === 'error')
log(
' ' +
obj.line +
':' +
obj.column +
'\t ' +
chalk.red.bold(level) +
' \0 ' +
obj.message +
'\t\t ' +
chalk.grey(obj.ruleId) +
'',
);
if (level === 'error') pass = 1;
});
}
});
if (warningCount > 0 || errorCount > 0) {
log(
'\n' +
chalk.bgRed.bold(errorCount + warningCount + ' problems') +
' (' +
chalk.red.bold(errorCount) +
' errors, ' +
chalk.yellow(warningCount) +
' warnings) \0',
);
}
!pass && log(chalk.green.bold('~~ Done: 代码检验通过,提交成功 ~~'));
process.exit(pass);
}
if (error !== null) {
log(`exec error: ${error}`);
}
},
);

File diff suppressed because it is too large Load Diff

@ -0,0 +1,21 @@
export default [{
icon: 'home',
text: '首页',
url: 'pages/home/home',
},
{
icon: 'sort',
text: '论坛',
url: 'pages/forum/forum',
},
{
icon: 'cart',
text: '购物车',
url: 'pages/cart/index',
},
{
icon: 'person',
text: '个人中心',
url: 'pages/usercenter/index',
},
];

@ -0,0 +1,34 @@
import TabMenu from './data';
Component({
data: {
active: 0,
list: TabMenu,
theme: {
custom: {
colorPrimary: '#333',
},
},
},
methods: {
onChange(event) {
this.setData({ active: event.detail.value });
wx.switchTab({
url: this.data.list[event.detail.value].url.startsWith('/')
? this.data.list[event.detail.value].url
: `/${this.data.list[event.detail.value].url}`,
});
},
init() {
const page = getCurrentPages().pop();
const route = page ? page.route.split('?')[0] : '';
const active = this.data.list.findIndex(
(item) =>
(item.url.startsWith('/') ? item.url.substr(1) : item.url) ===
`${route}`,
);
this.setData({ active });
},
},
});

@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"t-tab-bar": "tdesign-miniprogram/tab-bar/tab-bar",
"t-tab-bar-item": "tdesign-miniprogram/tab-bar/tab-bar-item",
"t-icon": "tdesign-miniprogram/icon/icon"
}
}

@ -0,0 +1,19 @@
<t-tab-bar
value="{{active}}"
bindchange="onChange"
split="{{false}}"
color="{{[theme.custom.colorPrimary, '#bbb' ]}}"
>
<t-tab-bar-item
wx:for="{{list}}"
wx:for-item="item"
wx:for-index="index"
wx:key="index"
>
<view class="custom-tab-bar-wrapper">
<t-icon prefix="wr" name="{{item.icon}}" size="48rpx" />
<view class="text">{{ item.text }}</view>
</view>
</t-tab-bar-item>
</t-tab-bar>

@ -0,0 +1,9 @@
.custom-tab-bar-wrapper {
display: flex;
flex-direction: column;
align-items: center;
}
.custom-tab-bar-wrapper .text {
font-size: 20rpx;
}

@ -0,0 +1,6 @@
const envList = [{"envId":"cloud1-8g5wmepxce8a3b8a","alias":"cloud1"}]
const isMac = false
module.exports = {
envList,
isMac
}

@ -0,0 +1,6 @@
{
"extEnable": true,
"extAppid": "wx5a75208aa13eee03",
"ext": {},
"window": {}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

@ -0,0 +1,5 @@
{
"compilerOptions": {
"baseUrl": "."
}
}

@ -0,0 +1,34 @@
## 模拟与数据
model 用于放置模拟后端数据返回的逻辑;假若接入真实后端接口,则本文件夹可改造为数据层适配。
services 用于请求逻辑,根据 config.useMock 配置可控制返回 mock 数据或是真实接口数据
### 1 模拟策略
1只依靠 ID 规律进行关联
大部分情况下推荐使用本方案ID 为`1`的商品固定会关联 ID 为`1`的优惠券或者[ID 对 10 的模运算结果为 1](https://www.runoob.com/try/try.php?filename=tryjs_oper_mod)的优惠券(看需要 1 个还是多个了)。
> 为保持关系稳定,模运算统一使用`10`为除数,`ID`为被除数;即`1%10`、`2%10`。
2建立额外关联关系查询
在无法使用简单数学关系维持关系的情况下,可以采用单独提供关系数据的方式进行关联(目前也没想到什么场景是数学关系稳定不了的了,先假定有,定下规范做法)。如数据 A 与数据 B 之间需要一个关联 AB则需要提供`A数据mock`、`B数据mock`、以及`A查B与B反查A`共 4 个 mock 源。
### 2 使用数据
使用数据源时应该在 services 文件夹中按照业务新建自己 fetch 函数导出fetch 函数以 Promise 形式返回组合调用 model 逻辑得到的数据。
> 不允许直接在业务中调用、使用 model 数据。
## 接入真实 API 后
接入真实 API 后 model 文件夹逻辑可以反转层级,作为数据适配层继续为项目服务。举例说明:
1. 在没有接入 API 时useMock 为 true
1.1 业务调用 services 进行 fetch
1.2 fetch 逻辑调用 model 文件夹中对应的数据源,构造、返回业务需要的结构
2. 在接入 API 后useMock 为 false
2.1 业务调用 services 进行 fetch
2.2 fetch 逻辑调用接口得到真实后端数据
2.3 比对 model 文件夹中数据 mock 数据结构 export 一个数据结构转换函数,输入真实后端数据,输出与 mock 数据结构一致的新数据,返回给 fetch
2.4 fetch 函数 返回 转换后的 数据结构,业务层无需进行更改

@ -0,0 +1,7 @@
import { getActivity } from './activity';
export function getActivityList(baseID = 0, length = 10) {
return new Array(length).fill(0).map((_, idx) => getActivity(idx + baseID));
}
export const activityList = getActivityList();

@ -0,0 +1,18 @@
/**
* @param {string|number} key 唯一值
*/
export function getActivity(key) {
return {
promotionId: `${key}`,
title: `满减满折回归${key}`,
description: null,
promotionCode: 'MERCHANT',
promotionSubCode: key % 2 === 0 ? 'MYJ' : 'MYG',
tag: '满减',
timeType: 1,
startTime: '1588737710000',
endTime: '1601467070000',
teasingStartTime: null,
activityLadder: [{ label: '满100元减99.9元' }],
};
}

@ -0,0 +1,31 @@
/** 地址 */
export function genAddress(id) {
return {
saasId: '88888888',
uid: `8888888820550${id}`,
authToken: null,
id: `${id}`,
addressId: `${id}`,
phone: '17612345678',
name: `测试用户${id}`,
countryName: '中国',
countryCode: 'chn',
provinceName: '甘肃省',
provinceCode: '620000',
cityName: '甘南藏族自治州',
cityCode: '623000',
districtName: '碌曲县',
districtCode: '623026',
detailAddress: `松日鼎盛大厦${id}${id}`,
isDefault: `${id}` === '0' ? 1 : 0,
addressTag: id === 0 ? '' : '公司',
latitude: '34.59103',
longitude: '102.48699',
storeId: null,
};
}
/** 地址列表 */
export function genAddressList(len = 10) {
return new Array(len).fill(0).map((_, idx) => genAddress(idx));
}

@ -0,0 +1,324 @@
import { mockIp, mockReqId } from '../utils/mock';
export function genCartGroupData() {
const resp = {
data: {
isNotEmpty: true,
storeGoods: [
{
storeId: '1000',
storeName: '云Mall深圳旗舰店',
storeStatus: 1,
totalDiscountSalePrice: '9990',
promotionGoodsList: [
{
title: '满减满折回归',
promotionCode: 'MERCHANT',
promotionSubCode: 'MYJ',
promotionId: '159174555838121985',
tagText: ['满100元减99.9元'],
promotionStatus: 3,
tag: '满减',
description: '满100元减99.9元,已减99.9元',
doorSillRemain: null,
isNeedAddOnShop: 0,
goodsPromotionList: [
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '12',
skuId: '135691622',
isSelected: 1,
thumb:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png',
title:
'腾讯极光盒子4智能网络电视机顶盒6K千兆网络机顶盒4K高分辨率',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png',
quantity: 1,
stockStatus: true,
stockQuantity: 3,
price: '9900',
originPrice: '16900',
tagPrice: null,
titlePrefixTags: [{ text: '新品' }, { text: '火爆' }],
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '经典白',
},
{
specTitle: '类型',
specValue: '经典套装',
},
],
joinCartTime: '2020-06-29T07:55:40.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '18',
skuId: '135681631',
isSelected: 1,
thumb:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png',
title:
'白色短袖连衣裙荷叶边裙摆宽松韩版休闲纯白清爽优雅连衣裙',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png',
quantity: 1,
stockStatus: true,
stockQuantity: 177,
price: '29800',
originPrice: '40000',
tagPrice: null,
titlePrefixTags: null,
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '米色荷叶边',
},
{
specTitle: '尺码',
specValue: 'M',
},
],
joinCartTime: '2020-06-29T07:55:27.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '13',
skuId: '135698362',
isSelected: 1,
thumb:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png',
title:
'带帽午休毯虎年款多功能加厚加大加绒简约多功能午休毯连帽披肩',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png',
quantity: 13,
stockStatus: true,
stockQuantity: 9,
price: '29900',
originPrice: '0',
tagPrice: null,
titlePrefixTags: [{ text: '火爆' }],
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '浅灰色',
},
{
specTitle: '尺码',
specValue: 'M',
},
],
joinCartTime: '2020-06-29T07:54:43.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '7',
skuId: '135681625',
isSelected: 1,
thumb:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/gh-2b.png',
title:
'不锈钢刀叉勺套装家用西餐餐具ins简约耐用不锈钢金色银色可选',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/gh-2b.png',
quantity: 1,
stockStatus: true,
stockQuantity: 0,
price: '29900',
originPrice: '29900',
tagPrice: null,
titlePrefixTags: null,
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '奶黄色',
},
{
specTitle: '数量',
specValue: '六件套',
},
],
joinCartTime: '2020-06-29T07:55:00.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
],
lastJoinTime: '2020-06-29T07:55:40.000+0000',
},
{
title: null,
promotionCode: 'EMPTY_PROMOTION',
promotionSubCode: null,
promotionId: null,
tagText: null,
promotionStatus: null,
tag: null,
description: null,
doorSillRemain: null,
isNeedAddOnShop: 0,
goodsPromotionList: [
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '11',
skuId: '135691629',
isSelected: 0,
thumb:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png',
title: '运动连帽拉链卫衣休闲开衫长袖多色运动细绒面料运动上衣',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png',
quantity: 1,
stockStatus: false,
stockQuantity: 0,
price: '25900',
originPrice: '39900',
tagPrice: null,
tagText: null,
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '军绿色',
},
{
specTitle: '尺码',
specValue: 'S',
},
],
joinCartTime: '2020-04-24T06:26:48.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '5',
skuId: '135691635',
isSelected: 0,
thumb:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png',
title:
'迷你便携高颜值蓝牙无线耳机立体声只能触控式操作简约立体声耳机',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png',
quantity: 1,
stockStatus: true,
stockQuantity: 96,
price: '29000',
originPrice: '29900',
tagPrice: null,
tagText: null,
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '黑色',
},
{
specTitle: '类型',
specValue: '简约款',
},
],
joinCartTime: '2020-06-29T07:55:17.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
],
lastJoinTime: null,
},
],
lastJoinTime: '2020-06-29T07:55:40.000+0000',
postageFreePromotionVo: {
title: null,
promotionCode: null,
promotionSubCode: null,
promotionId: null,
tagText: null,
promotionStatus: null,
tag: null,
description: null,
doorSillRemain: null,
isNeedAddOnShop: 0,
},
},
],
invalidGoodItems: [
{
uid: '88888888205468',
saasId: '88888888',
storeId: '1000',
spuId: '1',
skuId: '135691631',
isSelected: 1,
thumb: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
title: '纯色纯棉休闲圆领短袖T恤纯白亲肤厚柔软细腻面料纯白短袖套头T恤',
primaryImage:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
quantity: 8,
stockStatus: true,
stockQuantity: 177,
price: '26900',
originPrice: '31900',
tagPrice: null,
tagText: null,
roomId: null,
specInfo: [
{
specTitle: '颜色',
specValue: '白色',
},
{
specTitle: '尺码',
specValue: 'S',
},
],
joinCartTime: '2020-04-28T04:03:59.000+0000',
available: 1,
putOnSale: 1,
etitle: null,
},
],
isAllSelected: false,
selectedGoodsCount: 16,
totalAmount: '179997',
totalDiscountAmount: '110000',
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 269,
success: true,
};
return resp;
}

@ -0,0 +1,206 @@
export function getCategoryList() {
return [
{
groupId: '24948',
name: '女装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249481',
name: '女装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249480',
name: '卫衣',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-1.png',
},
{
groupId: '249480',
name: '毛呢外套',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-2.png',
},
{
groupId: '249480',
name: '雪纺衫',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-3.png',
},
{
groupId: '249480',
name: '羽绒服',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-4.png',
},
{
groupId: '249480',
name: '毛衣',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-5.png',
},
{
groupId: '249480',
name: '棉衣',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-6.png',
},
{
groupId: '249480',
name: '西装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-7.png',
},
{
groupId: '249480',
name: '马甲',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png',
},
{
groupId: '249480',
name: '连衣裙',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-9.png',
},
{
groupId: '249480',
name: '半身裙',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-10.png',
},
{
groupId: '249480',
name: '裤子',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png',
},
],
},
],
},
{
groupId: '24948',
name: '男装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249481',
name: '男装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249480',
name: '卫衣',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-1.png',
},
{
groupId: '249480',
name: '裤子',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png',
},
{
groupId: '249480',
name: '西装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-7.png',
},
{
groupId: '249480',
name: '羽绒服',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-4.png',
},
{
groupId: '249480',
name: '马甲',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png',
},
],
},
],
},
{
groupId: '24948',
name: '儿童装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249481',
name: '儿童装',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249480',
name: '马甲',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-8.png',
},
{
groupId: '249480',
name: '裤子',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-11.png',
},
{
groupId: '249480',
name: '连衣裙',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/classify/img-9.png',
},
{
groupId: '249480',
name: '其他',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3b.png',
},
],
},
],
},
{
groupId: '24948',
name: '美妆',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249481',
name: '美妆',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/miniapp/category/category-default.png',
children: [
{
groupId: '249480',
name: '唇釉',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-20a1.png',
},
{
groupId: '249480',
name: '美妆蛋',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-11a1.png',
},
{
groupId: '249480',
name: '眼影',
thumbnail:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/mz-12b.png',
},
],
},
],
},
];
}

@ -0,0 +1,338 @@
/**
* * @param {number} spuId
* @param {number} pageNum
* @param {number} pageSize
* @param {number} commentsLevel
* @param {boolean} hasImage
*/
export function getGoodsAllComments(params) {
const { hasImage } = params.queryParameter;
if (hasImage) {
return {
pageNum: 1,
pageSize: 10,
totalCount: '1',
pageList: [
{
spuId: '1722045',
skuId: '0',
specInfo: '',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentResources: [
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
type: 'image',
},
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4',
type: 'video',
coverSrc:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
},
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4',
type: 'video',
coverSrc:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
},
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4',
type: 'video',
coverSrc:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
},
],
commentScore: 4,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1591953561000',
isAutoComment: false,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
{
spuId: '1722045',
skuId: '0',
specInfo: '',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentResources: [
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
type: 'image',
},
],
commentScore: 4,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1591953561000',
isAutoComment: false,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
{
spuId: '1722045',
skuId: '0',
specInfo: '',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentResources: [
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
type: 'image',
},
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4',
type: 'video',
coverSrc:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
},
],
commentScore: 4,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1591953561000',
isAutoComment: false,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
{
spuId: '1722045',
skuId: '0',
specInfo: '',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentResources: [
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
type: 'image',
},
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4',
type: 'video',
coverSrc:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
},
{
src: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/comment-video.mp4',
type: 'video',
coverSrc:
'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
},
],
commentScore: 4,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1591953561000',
isAutoComment: false,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
],
};
}
return {
pageNum: 1,
pageSize: 10,
totalCount: '47',
pageList: [
{
spuId: '1722045',
skuId: '1697694',
specInfo: '很不错',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 1,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592224320000',
isAutoComment: false,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
{
spuId: '1722045',
skuId: '1697693',
specInfo: '很适合',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 1,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592224320000',
isAutoComment: false,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
{
spuId: '1722045',
skuId: '1697694',
specInfo: 'NICE',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 5,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592218074000',
isAutoComment: true,
sellerReply:
'亲,你好,我们会联系商家和厂商给您一个满意的答复请一定妥善保管好发票',
},
{
spuId: '1722045',
skuId: '0',
specInfo: '',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 5,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592218074000',
isAutoComment: false,
goodsDetailInfo: '颜色:纯净白 尺码:S码',
},
{
spuId: '1722045',
skuId: '1697694',
specInfo: '测试dr超长:dr专用超长;bwtgg01:fff',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 5,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592217607000',
isAutoComment: false,
},
{
spuId: '1722045',
skuId: '1697693',
specInfo: '测试dr超长:超长测试超长测试1;bwtgg01:bbb',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 4,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592217607000',
isAutoComment: false,
},
{
spuId: '1722045',
skuId: '1697694',
specInfo: '测试dr超长:dr专用超长;bwtgg01:fff',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 5,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592205599000',
isAutoComment: false,
},
{
spuId: '1722045',
skuId: '1697694',
specInfo: '测试dr超长:dr专用超长;bwtgg01:fff',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 5,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1592188822000',
isAutoComment: false,
},
{
spuId: '1722045',
skuId: '1697694',
specInfo: '测试dr超长:dr专用超长;bwtgg01:fff',
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentImageUrls: null,
commentScore: 5,
uid: '88881055835',
userName: 'Max',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1593792002000',
isAutoComment: true,
},
{
spuId: '1722045',
skuId: '1697694',
specInfo: '测试dr超长:dr专用超长;bwtgg01:fff',
commentContent: '',
commentImageUrls: null,
commentScore: 5,
uid: '88881055835',
userName: 'Max',
userHeadUrl:
'https://cdn-we-retail.ym.tencent.com/tsr/avatar/avatar1.png',
isAnonymity: false,
commentTime: '1593792001000',
isAutoComment: true,
},
],
};
}
export function getGoodsCommentsCount() {
return {
commentCount: '47',
badCount: '0',
middleCount: '2',
goodCount: '45',
hasImageCount: '1',
goodRate: 95.7,
uidCount: '0',
};
}

@ -0,0 +1,50 @@
const queryDetail = {
commentInfos: [
{
id: '647984992708380600',
uid: '',
userName: 'Dean Cheng',
userHeadUrl:
'https://bizmid-material-qa-1302115263.cos.ap-guangzhou.myqcloud.com/comment/default_head.png',
commentId: '1937712',
commentIdName: '小鹿商品',
commentIdImageUrl:
'https://bizmid-material-qa-1302115263.file.myqcloud.com/persist/4bf2ded7-1759-4821-919c-cc4960e14120/1078823925183295617/100000114727/material/1/cdbeb389be64427b8c165627895ff0bc-1610425563793-%E5%A4%B4%E5%83%8F.png',
commentStage: 1,
commentCheckStatus: 2,
commentIdType: 1,
content: '',
commentInfo: {
score: null,
content: '',
medias: [],
commentTime: '1617872404000',
},
isAgainComment: 0,
commentHasAgainComment: 0,
isAnonymous: 0,
replyList: [],
specification: '颜色:白色 ',
specificationJson: '{"颜色":"白色"}',
commentExtendId: '1937713',
commentTime: '1617872404000',
score: 0,
goodsScore: null,
freightScore: null,
serviceScore: null,
medias: [],
againCommentList: null,
},
],
logisticsScore: null,
serviceScore: null,
};
/**
* @param {string} skuId
* @param {string} spuId
* @param {string} orderNo
*/
export function queryCommentDetail() {
return queryDetail;
}

@ -0,0 +1,39 @@
/**
* 优惠券
*
* @typedef {'default'|'useless'|'disabled'} CouponCardStatus
* @typedef {'discount'|'price'} CouponCardType
*
* @param {number} [id]
* @param {CouponCardStatus} [status]
* @param {CouponCardType} [type]
*/
export function getCoupon(id = 0, status = 'default', type = (id % 2) + 1) {
return {
/** key */
key: `${id}`,
/** 优惠券状态 */
status,
/** 优惠券类型 */
type,
/** 折扣或者满减值 */
value: type === 2 ? 5.5 : 1800,
/** 标签 */
tag: '',
/** 描述 */
desc: parseInt(id) > 0 ? `${parseInt(id) * 100}元可用` : '无门槛使用',
/** 订单底价,满n元 */
base: 10000 * (parseInt(id) || 0),
/** 标题 */
title: type === 2 ? `生鲜折扣券 - ${id}` : `生鲜满减券 - ${id}`,
/** 有效时间限制 */
timeLimit: '2019.11.18-2023.12.18',
/** 货币符号 */
currency: '¥',
};
}
/** 优惠券列表 */
export function getCouponList(status = 'default', length = 10) {
return new Array(length).fill(0).map((_, idx) => getCoupon(idx, status));
}

@ -0,0 +1,30 @@
export function getGoodsDetailsComments() {
return {
homePageComments: [
{
spuId: '1722045',
skuId: null,
specInfo: null,
commentContent:
'收到货了,第一时间试了一下,很漂亮特别喜欢,大爱大爱,颜色也很好看。棒棒!',
commentScore: 4,
uid: '88881048075',
userName: 'Dean',
userHeadUrl:
'https://wx.qlogo.cn/mmopen/vi_32/5mKrvn3ibyDNaDZSZics3aoKlz1cv0icqn4EruVm6gKjsK0xvZZhC2hkUkRWGxlIzOEc4600JkzKn9icOLE6zjgsxw/132',
},
],
};
}
export function getGoodsDetailsCommentsCount() {
return {
commentCount: '47',
badCount: '0',
middleCount: '2',
goodCount: '45',
hasImageCount: '1',
goodRate: 95.7,
uidCount: '0',
};
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,7 @@
import { genGood } from './good';
export function getGoodsList(baseID = 0, length = 10) {
return new Array(length).fill(0).map((_, idx) => genGood(idx + baseID));
}
export const goodsList = getGoodsList();

@ -0,0 +1,295 @@
import { mockIp, mockReqId } from '../../utils/mock';
const orderResps = [
{
data: {
saasId: '88888888',
uid: '88888888205468',
storeId: '1000',
skuId: '135691625',
numOfSku: 1,
numOfSkuAvailable: 1,
refundableAmount: '26900',
refundableDiscountAmount: '0',
shippingFeeIncluded: '0',
paidAmountEach: '26900',
boughtQuantity: 1,
orderNo: '132222623132329291',
goodsInfo: {
goodsName:
'迷你便携高颜值蓝牙无线耳机立体声只能触控式操作简约立体声耳机',
skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-2a.png',
specInfo: [
{
specId: '50456',
specTitle: '颜色',
specValue: '黑色',
},
{
specId: '50459',
specTitle: '尺码',
specValue: '简约款',
},
],
},
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 36,
success: true,
},
{
data: {
saasId: '88888888',
uid: '88888888205468',
storeId: '1000',
skuId: '135676631',
numOfSku: 1,
numOfSkuAvailable: 1,
refundableAmount: '26900',
refundableDiscountAmount: '0',
shippingFeeIncluded: '0',
paidAmountEach: '26900',
boughtQuantity: 1,
orderNo: '132222623132329291',
goodsInfo: {
goodsName: '白色短袖连衣裙荷叶边裙摆宽松韩版休闲纯白清爽优雅连衣裙',
skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-09a.png',
specInfo: [
{
specId: '50456',
specTitle: '颜色',
specValue: '米色荷叶边',
},
{
specId: '50459',
specTitle: '尺码',
specValue: 'S',
},
],
},
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 36,
success: true,
},
{
data: {
saasId: '88888888',
uid: '88888888205468',
storeId: '1000',
skuId: '135691622',
numOfSku: 1,
numOfSkuAvailable: 1,
refundableAmount: '26900',
refundableDiscountAmount: '0',
shippingFeeIncluded: '0',
paidAmountEach: '26900',
boughtQuantity: 1,
orderNo: '132222623132329291',
goodsInfo: {
goodsName: '腾讯极光盒子4智能网络电视机顶盒6K千兆网络机顶盒4K高分辨率',
skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/dz-3a.png',
specInfo: [
{
specId: '50456',
specTitle: '颜色',
specValue: '经典白',
},
{
specId: '50459',
specTitle: '类型',
specValue: '经典套装',
},
],
},
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 36,
success: true,
},
{
data: {
saasId: '88888888',
uid: '88888888205468',
storeId: '1000',
skuId: '135676629',
numOfSku: 1,
numOfSkuAvailable: 1,
refundableAmount: '26900',
refundableDiscountAmount: '0',
shippingFeeIncluded: '0',
paidAmountEach: '26900',
boughtQuantity: 1,
orderNo: '132222623132329291',
goodsInfo: {
goodsName: '带帽午休毯虎年款多功能加厚加大加绒简约多功能午休毯连帽披肩',
skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/muy-3a.png',
specInfo: [
{
specId: '50456',
specTitle: '颜色',
specValue: '浅灰色',
},
{
specId: '50459',
specTitle: '尺码',
specValue: 'S',
},
],
},
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 36,
success: true,
},
{
data: {
saasId: '88888888',
uid: '88888888205468',
storeId: '1000',
skuId: '135686631',
numOfSku: 1,
numOfSkuAvailable: 1,
refundableAmount: '26900',
refundableDiscountAmount: '0',
shippingFeeIncluded: '0',
paidAmountEach: '26900',
boughtQuantity: 1,
orderNo: '132222623132329291',
goodsInfo: {
goodsName: '运动连帽拉链卫衣休闲开衫长袖多色运动细绒面料运动上衣',
skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-17a.png',
specInfo: [
{
specId: '50456',
specTitle: '颜色',
specValue: '军绿色',
},
{
specId: '50459',
specTitle: '尺码',
specValue: 'XS',
},
],
},
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 36,
success: true,
},
{
data: {
saasId: '88888888',
uid: '88888888205468',
storeId: '1000',
skuId: '19384938948343',
numOfSku: 1,
numOfSkuAvailable: 1,
refundableAmount: '26900',
refundableDiscountAmount: '0',
shippingFeeIncluded: '0',
paidAmountEach: '26900',
boughtQuantity: 1,
orderNo: '130169571554503755',
goodsInfo: {
goodsName:
'纯色纯棉休闲圆领短袖T恤纯白亲肤厚柔软细腻面料纯白短袖套头T恤',
skuImage: 'https://cdn-we-retail.ym.tencent.com/tsr/goods/nz-08b.png',
specInfo: [
{
specId: '50456',
specTitle: '颜色',
specValue: '军绿色',
},
{
specId: '50459',
specTitle: '尺码',
specValue: 'XS',
},
],
},
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 36,
success: true,
},
];
export function genRightsPreview(params) {
const { orderNo, skuId } = params;
const resp = orderResps.find(
(r) => r.data.orderNo === orderNo && r.data.skuId === skuId,
);
return resp;
}
export function genApplyReasonList(params) {
const resp = {
data: {
saasId: '70000001',
rightsReasonList: [
{ id: '1', desc: '实际商品与描述不符' },
{ id: '2', desc: '质量问题' },
{ id: '3', desc: '少件/漏发' },
{ id: '4', desc: '包装/商品/污迹/裂痕/变形' },
{ id: '5', desc: '发货太慢' },
{ id: '6', desc: '物流配送太慢' },
{ id: '7', desc: '商家发错货' },
{ id: '8', desc: '不喜欢' },
],
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 6,
success: true,
};
// 未收货对应的原因列表
if (params.rightsReasonType === 'REFUND_MONEY') {
resp.data.rightsReasonList = [
{ id: '9', desc: '空包裹' },
{ id: '10', desc: '快递/物流一直未送到' },
{ id: '11', desc: '货物破损已拒签' },
{ id: '12', desc: '不喜欢' },
];
}
return resp;
}
export function applyService() {
const resp = {
data: {
rightsNo: '123123423',
saasId: '70000001',
uid: '700000011070005',
storeId: '542',
result: null,
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 269,
success: true,
};
return resp;
}

@ -0,0 +1,147 @@
import { mockIp, mockReqId } from '../../utils/mock';
export const transformGoodsDataToConfirmData = (goodsDataList) => {
const list = [];
goodsDataList.forEach((goodsData) => {
list.push({
storeId: goodsData.storeId,
spuId: goodsData.spuId,
skuId: goodsData.skuId,
goodsName: goodsData.title,
image: goodsData.primaryImage,
reminderStock: 119,
quantity: goodsData.quantity,
payPrice: goodsData.price,
totalSkuPrice: goodsData.price,
discountSettlePrice: goodsData.price,
realSettlePrice: goodsData.price,
settlePrice: goodsData.price,
oriPrice: goodsData.originPrice,
tagPrice: null,
tagText: null,
skuSpecLst: goodsData.specInfo,
promotionIds: null,
weight: 0.0,
unit: 'KG',
volume: null,
masterGoodsType: 0,
viceGoodsType: 0,
roomId: goodsData.roomId,
egoodsName: null,
});
});
return list;
};
/** 生成结算数据 */
export function genSettleDetail(params) {
const { userAddressReq, couponList, goodsRequestList } = params;
const resp = {
data: {
settleType: 0,
userAddress: null,
totalGoodsCount: 3,
packageCount: 1,
totalAmount: '289997',
totalPayAmount: '',
totalDiscountAmount: '110000',
totalPromotionAmount: '1100',
totalCouponAmount: '0',
totalSalePrice: '289997',
totalGoodsAmount: '289997',
totalDeliveryFee: '0',
invoiceRequest: null,
skuImages: null,
deliveryFeeList: null,
storeGoodsList: [
{
storeId: '1000',
storeName: '云Mall深圳旗舰店',
remark: null,
goodsCount: 1,
deliveryFee: '0',
deliveryWords: null,
storeTotalAmount: '0',
storeTotalPayAmount: '179997',
storeTotalDiscountAmount: '110000',
storeTotalCouponAmount: '0',
skuDetailVos: [],
couponList: [
{
couponId: 11,
storeId: '1000',
},
],
},
],
inValidGoodsList: null,
outOfStockGoodsList: null,
limitGoodsList: null,
abnormalDeliveryGoodsList: null,
invoiceSupport: 1,
},
code: 'Success',
msg: null,
requestId: mockReqId(),
clientIp: mockIp(),
rt: 244,
success: true,
};
const list = transformGoodsDataToConfirmData(goodsRequestList);
// 获取购物车传递的商品数据
resp.data.storeGoodsList[0].skuDetailVos = list;
// 判断是否携带优惠券数据
const discountPrice = [];
if (couponList && couponList.length > 0) {
couponList.forEach((coupon) => {
if (coupon.status === 'default') {
discountPrice.push({
type: coupon.type,
value: coupon.value,
});
}
});
}
// 模拟计算场景
// 计算总价
const totalPrice = list.reduce((pre, cur) => {
return pre + cur.quantity * Number(cur.settlePrice);
}, 0);
// 计算折扣
const totalDiscountPrice =
discountPrice.length > 0
? discountPrice.reduce((pre, cur) => {
if (cur.type === 1) {
return pre + cur.value;
}
if (cur.type === 2) {
return pre + (Number(totalPrice) * cur.value) / 10;
}
return pre + cur;
}, 0)
: 0;
resp.data.totalSalePrice = totalPrice;
resp.data.totalCouponAmount = totalDiscountPrice;
resp.data.totalPayAmount =
totalPrice - totalDiscountPrice - Number(resp.data.totalPromotionAmount);
if (userAddressReq) {
resp.data.settleType = 1;
resp.data.userAddress = userAddressReq;
}
return resp;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,21 @@
import { getGoodsList } from './goods';
export function getPromotion(baseID = 0, length = 10) {
return {
list: getGoodsList(baseID, length).map((item) => {
return {
spuId: item.spuId,
thumb: item.primaryImage,
title: item.title,
price: item.minSalePrice,
originPrice: item.maxLinePrice,
tags: item.spuTagList.map((tag) => ({ title: tag.title })),
};
}),
banner:
'https://cdn-we-retail.ym.tencent.com/tsr/promotion/banner-promotion.png',
time: 1000 * 60 * 60 * 20,
showBannerDesc: true,
statusTag: 'running',
};
}

@ -0,0 +1,60 @@
import { getGoodsList } from './goods';
/**
* @param {number} sort
* @param {number} pageNum
* @param {number} pageSize
* @param {number} minPrice
* @param {number} maxPrice
* @param {string} keyword
*/
export function getSearchHistory() {
return {
historyWords: [
'鸡',
'电脑',
'iPhone12',
'车载手机支架',
'自然堂',
'小米10',
'原浆古井贡酒',
'欧米伽',
'华为',
'针织半身裙',
'氢跑鞋',
'三盒处理器',
],
};
}
export function getSearchPopular() {
return {
popularWords: [
'鸡',
'电脑',
'iPhone12',
'车载手机支架',
'自然堂',
'小米10',
'原浆古井贡酒',
'欧米伽',
'华为',
'针织半身裙',
'氢跑鞋',
'三盒处理器',
],
};
}
export function getSearchResult() {
return {
saasId: null,
storeId: null,
pageNum: 1,
pageSize: 30,
totalCount: 1,
spuList: getGoodsList(7),
algId: 0,
};
}

@ -0,0 +1,58 @@
export function getGoods() {
return {
goods: [
{
squid: '1',
checkItems: [
{
name: '匿名评价',
value: 'anonymous',
checked: false,
},
],
detail: {
image:
'https://wx.qlogo.cn/mmopen/vi_32/51VSMNuy1CyHiaAhAjLJ00kMZVqqnCqXeZduCLXHUBr52zFHRGxwL7kGia3fHj8GSNzFcqFDInQmRGM1eWjtQgqA/132',
title: '',
},
goodComment: {
/** 商品评价 */
rate: 0,
/** 评价内容 */
label: '123',
/** 上传图片 */
images: [],
},
},
{
squid: '2',
checkItems: [
{
name: '匿名评价',
value: 'anonymous',
checked: false,
},
],
detail: {
image:
'https://wx.qlogo.cn/mmopen/vi_32/51VSMNuy1CyHiaAhAjLJ00kMZVqqnCqXeZduCLXHUBr52zFHRGxwL7kGia3fHj8GSNzFcqFDInQmRGM1eWjtQgqA/132',
title: '评价内容 山姆智利进口',
},
goodComment: {
/** 商品评价 */
rate: 0,
/** 评价内容 */
label: '山姆智利进口',
/** 上传图片 */
images: [],
},
},
],
storeComment: {
/** 物流评价 */
logisticsRate: 0,
/** 服务评价 */
servicesRate: 0,
},
};
}

@ -0,0 +1,30 @@
const images = [
{
img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner1.png',
text: '1',
},
{
img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner2.png',
text: '2',
},
{
img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner3.png',
text: '3',
},
{
img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner4.png',
text: '4',
},
{
img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner5.png',
text: '5',
},
{
img: 'https://cdn-we-retail.ym.tencent.com/tsr/home/v2/banner6.png',
text: '6',
},
];
export function genSwiperImageList() {
return images;
}

@ -0,0 +1,52 @@
const userInfo = {
avatarUrl:
'https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-ui/components-exp/avatar/avatar-1.jpg',
nickName: 'TDesign 🌟',
phoneNumber: '13438358888',
gender: 2,
};
const countsData = [
{
num: 2,
name: '积分',
type: 'point',
},
{
num: 10,
name: '优惠券',
type: 'coupon',
},
];
const orderTagInfos = [
{
orderNum: 1,
tabType: 5,
},
{
orderNum: 1,
tabType: 10,
},
{
orderNum: 1,
tabType: 40,
},
{
orderNum: 0,
tabType: 0,
},
];
const customerServiceInfo = {
servicePhone: '4006336868',
serviceTimeDuration: '每周三至周五 9:00-12:00 13:00-15:00',
};
export const genSimpleUserInfo = () => ({ ...userInfo });
export const genUsercenter = () => ({
userInfo,
countsData,
orderTagInfos,
customerServiceInfo,
});

@ -0,0 +1,36 @@
{
"name": "supermarket-pages",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint --cache --fix --ext .js",
"check": "node config/eslintCheck.js"
},
"author": "",
"license": "ISC",
"husky": {
"hooks": {
"pre-commit": "lint-staged && npm run check"
}
},
"lint-staged": {
"*.{js, ts}": "eslint --cache --fix",
"*.{js,ts,css,less}": "prettier --write"
},
"dependencies": {
"dayjs": "^1.9.3",
"tdesign-miniprogram": "^0.18.0",
"tslib": "^1.11.1"
},
"devDependencies": {
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-prettier": "^3.1.2",
"husky": "^4.3.0",
"lint-staged": "^10.0.8",
"prettier": "^2.1.2"
}
}

@ -0,0 +1,59 @@
Component({
options: {
addGlobalClass: true,
},
/**
* 组件的属性列表
*/
properties: {
isAllSelected: {
type: Boolean,
value: false,
},
totalAmount: {
type: Number,
value: 1,
},
totalGoodsNum: {
type: Number,
value: 0,
observer(num) {
const isDisabled = num == 0;
setTimeout(() => {
this.setData({
isDisabled,
});
});
},
},
totalDiscountAmount: {
type: Number,
value: 0,
},
bottomHeight: {
type: Number,
value: 100,
},
fixed: Boolean,
},
data: {
isDisabled: false,
},
methods: {
handleSelectAll() {
const { isAllSelected } = this.data;
this.setData({
isAllSelected: !isAllSelected,
});
this.triggerEvent('handleSelectAll', {
isAllSelected: isAllSelected,
});
},
handleToSettle() {
if (this.data.isDisabled) return;
this.triggerEvent('handleToSettle');
},
},
});

@ -0,0 +1,7 @@
{
"component": true,
"usingComponents": {
"price": "/components/price/index",
"t-icon": "tdesign-miniprogram/icon/icon"
}
}

@ -0,0 +1,31 @@
<view class="cart-bar__placeholder" wx:if="{{fixed}}" />
<view class="cart-bar {{fixed ? 'cart-bar--fixed' : ''}} flex flex-v-center" style="bottom: {{fixed ? 'calc(' + bottomHeight + 'rpx + env(safe-area-inset-bottom))' : ''}};">
<t-icon
size="40rpx"
color="{{isAllSelected ? '#FA4126' : '#BBBBBB'}}"
name="{{isAllSelected ? 'check-circle-filled' : 'circle'}}"
class="cart-bar__check"
catchtap="handleSelectAll"
/>
<text>全选</text>
<view class="cart-bar__total flex1">
<view>
<text class="cart-bar__total--bold text-padding-right">总计</text>
<price
price="{{totalAmount || '0'}}"
fill="{{false}}"
decimalSmaller
class="cart-bar__total--bold cart-bar__total--price"
/>
<text class="cart-bar__total--normal">(不含运费)</text>
</view>
<view wx:if="{{totalDiscountAmount}}">
<text class="cart-bar__total--normal text-padding-right">已优惠</text>
<price class="cart-bar__total--normal" price="{{totalDiscountAmount || '0'}}" fill="{{false}}" />
</view>
</view>
<view catchtap="handleToSettle" class="{{!isDisabled ? '' : 'disabled-btn'}} account-btn" hover-class="{{!isDisabled ? '' : 'hover-btn'}}">
去结算({{totalGoodsNum}})
</view>
</view>

@ -0,0 +1,80 @@
.cart-bar__placeholder {
height: 100rpx;
}
.flex {
display: flex;
}
.flex-v-center {
align-items: center;
}
.flex1 {
flex: 1;
}
.algin-bottom {
text-align: end;
}
.cart-bar--fixed {
position: fixed;
left: 0;
right: 0;
z-index: 99;
bottom: calc(100rpx + env(safe-area-inset-bottom));
}
.cart-bar {
height: 112rpx;
background-color: #fff;
border-top: 1rpx solid #e5e5e5;
padding: 16rpx 32rpx;
box-sizing: border-box;
font-size: 24rpx;
line-height: 36rpx;
color: #333;
}
.cart-bar .cart-bar__check {
margin-right: 12rpx;
}
.cart-bar .cart-bar__total {
margin-left: 24rpx;
}
.cart-bar .account-btn {
width: 192rpx;
height: 80rpx;
border-radius: 40rpx;
background-color: #fa4126;
font-size: 28rpx;
font-weight: bold;
line-height: 80rpx;
color: #ffffff;
text-align: center;
}
.cart-bar .disabled-btn {
background-color: #cccccc !important;
}
.cart-bar .hover-btn {
opacity: 0.5;
}
.cart-bar__total .cart-bar__total--bold {
font-size: 28rpx;
line-height: 40rpx;
color: #333;
font-weight: bold;
}
.cart-bar__total .cart-bar__total--normal {
font-size: 24rpx;
line-height: 32rpx;
color: #999;
}
.cart-bar__total .cart-bar__total--price {
color: #fa4126;
font-weight: bold;
}
.text-padding-right {
padding-right: 4rpx;
}

@ -0,0 +1,23 @@
Component({
properties: {
imgUrl: {
type: String,
value:
'https://cdn-we-retail.ym.tencent.com/miniapp/template/empty-cart.png',
},
tip: {
type: String,
value: '购物车是空的',
},
btnText: {
type: String,
value: '去首页',
},
},
data: {},
methods: {
handleClick() {
this.triggerEvent('handleClick');
},
},
});

@ -0,0 +1,6 @@
{
"component": true,
"usingComponents": {
"t-image": "/components/webp-image/index"
}
}

@ -0,0 +1,6 @@
<view class="cart-empty">
<t-image t-class="cart-img" src="{{imgUrl}}" />
<view class="tip">{{tip}}</view>
<view class="btn" bind:tap="handleClick">{{btnText}}</view>
</view>

@ -0,0 +1,33 @@
.cart-empty {
padding: 64rpx 0rpx;
display: flex;
flex-direction: column;
align-items: center;
box-sizing: border-box;
height: calc(100vh - 100rpx);
background-color: #f5f5f5;
}
.cart-empty .cart-img {
width: 160rpx;
height: 160rpx;
margin-bottom: 24rpx;
}
.cart-empty .tip {
font-size: 28rpx;
line-height: 40rpx;
color: #999;
margin-bottom: 24rpx;
}
.cart-empty .btn {
width: 240rpx;
height: 72rpx;
border-radius: 36rpx;
text-align: center;
line-height: 72rpx;
border: 2rpx solid #fa4126;
color: #fa4126;
background-color: transparent;
font-size: 28rpx;
font-weight: bold;
}

@ -0,0 +1,166 @@
import Toast from 'tdesign-miniprogram/toast/index';
const shortageImg =
'https://cdn-we-retail.ym.tencent.com/miniapp/cart/shortage.png';
Component({
isSpecsTap: false, // 标记本次点击事件是否因为点击specs触发由于底层goods-card组件没有catch specs点击事件只能在此处加状态来避免点击specs时触发跳转商品详情
externalClasses: ['wr-class'],
properties: {
storeGoods: {
type: Array,
observer(storeGoods) {
for (const store of storeGoods) {
for (const activity of store.promotionGoodsList) {
for (const goods of activity.goodsPromotionList) {
goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值
}
}
for (const goods of store.shortageGoodsList) {
goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值
}
}
this.setData({ _storeGoods: storeGoods });
},
},
invalidGoodItems: {
type: Array,
observer(invalidGoodItems) {
invalidGoodItems.forEach((goods) => {
goods.specs = goods.specInfo.map((item) => item.specValue); // 目前仅展示商品已选规格的值
});
this.setData({ _invalidGoodItems: invalidGoodItems });
},
},
thumbWidth: { type: null },
thumbHeight: { type: null },
},
data: {
shortageImg,
isShowSpecs: false,
currentGoods: {},
isShowToggle: false,
_storeGoods: [],
_invalidGoodItems: [],
},
methods: {
// 删除商品
deleteGoods(e) {
const { goods } = e.currentTarget.dataset;
this.triggerEvent('delete', { goods });
},
// 清空失效商品
clearInvalidGoods() {
this.triggerEvent('clearinvalidgoods');
},
// 选中商品
selectGoods(e) {
const { goods } = e.currentTarget.dataset;
this.triggerEvent('selectgoods', {
goods,
isSelected: !goods.isSelected,
});
},
changeQuantity(num, goods) {
this.triggerEvent('changequantity', {
goods,
quantity: num,
});
},
changeStepper(e) {
const { value } = e.detail;
const { goods } = e.currentTarget.dataset;
let num = value;
if (value > goods.stack) {
num = goods.stack;
}
this.changeQuantity(num, goods);
},
input(e) {
const { value } = e.detail;
const { goods } = e.currentTarget.dataset;
const num = value;
this.changeQuantity(num, goods);
},
overlimit(e) {
const text =
e.detail.type === 'minus'
? '该商品数量不能减少了哦'
: '同一商品最多购买999件';
Toast({
context: this,
selector: '#t-toast',
message: text,
});
},
// 去凑单/再逛逛
gotoBuyMore(e) {
const { promotion, storeId = '' } = e.currentTarget.dataset;
this.triggerEvent('gocollect', { promotion, storeId });
},
// 选中门店
selectStore(e) {
const { storeIndex } = e.currentTarget.dataset;
const store = this.data.storeGoods[storeIndex];
const isSelected = !store.isSelected;
if (store.storeStockShortage && isSelected) {
Toast({
context: this,
selector: '#t-toast',
message: '部分商品库存不足',
});
return;
}
this.triggerEvent('selectstore', {
store,
isSelected,
});
},
// 展开/收起切换
showToggle() {
this.setData({
isShowToggle: !this.data.isShowToggle,
});
},
// 展示规格popup
specsTap(e) {
this.isSpecsTap = true;
const { goods } = e.currentTarget.dataset;
this.setData({
isShowSpecs: true,
currentGoods: goods,
});
},
hideSpecsPopup() {
this.setData({
isShowSpecs: false,
});
},
goGoodsDetail(e) {
if (this.isSpecsTap) {
this.isSpecsTap = false;
return;
}
const { goods } = e.currentTarget.dataset;
this.triggerEvent('goodsclick', { goods });
},
gotoCoupons() {
wx.navigateTo({ url: '/pages/coupon/coupon-list/index' });
},
},
});

@ -0,0 +1,11 @@
{
"component": true,
"usingComponents": {
"t-toast": "tdesign-miniprogram/toast/toast",
"t-icon": "tdesign-miniprogram/icon/icon",
"t-stepper": "tdesign-miniprogram/stepper/stepper",
"swipeout": "/components/swipeout/index",
"goods-card": "../../components/goods-card/index",
"specs-popup": "../../components/specs-popup/index"
}
}

@ -0,0 +1,185 @@
<wxs module="handlePromotion">
var hasPromotion = function (code) {
return code && code !== 'EMPTY_PROMOTION';
}
module.exports.hasPromotion = hasPromotion;
</wxs>
<wxs src="./utils.wxs" module="utils" />
<view class="cart-group">
<view
class="goods-wrap"
wx:for="{{_storeGoods}}"
wx:for-item="store"
wx:for-index="si"
wx:key="storeId"
>
<view class="cart-store">
<t-icon
size="40rpx"
color="{{store.isSelected ? '#FA4126' : '#BBBBBB'}}"
name="{{store.isSelected ? 'check-circle-filled' : 'circle'}}"
class="cart-store__check"
bindtap="selectStore"
data-store-index="{{si}}"
/>
<view class="cart-store__content">
<view class="store-title">
<t-icon
prefix="wr"
size="40rpx"
color="#333333"
name="store"
/>
<view class="store-name">{{store.storeName}}</view>
</view>
<view class="get-coupon" catch:tap="gotoCoupons">优惠券</view>
</view>
</view>
<block
wx:for="{{store.promotionGoodsList}}"
wx:for-item="promotion"
wx:for-index="promoindex"
wx:key="promoindex"
>
<view
class="promotion-wrap"
wx:if="{{handlePromotion.hasPromotion(promotion.promotionCode)}}"
bindtap="gotoBuyMore"
data-promotion="{{promotion}}"
data-store-id="{{store.storeId}}"
>
<view class="promotion-title">
<view class="promotion-icon">{{promotion.tag}}</view>
<view class="promotion-text">{{promotion.description}}</view>
</view>
<view class="promotion-action action-btn" hover-class="action-btn--active">
<view class="promotion-action-label">
{{promotion.isNeedAddOnShop == 1 ? '去凑单' : '再逛逛'}}
</view>
<t-icon name="chevron-right" size="32rpx" color="#BBBBBB" />
</view>
</view>
<view
class="goods-item"
wx:for="{{promotion.goodsPromotionList}}"
wx:for-item="goods"
wx:for-index="gi"
wx:key="extKey"
>
<swipeout right-width="{{ 72 }}">
<view class="goods-item-info">
<view class="check-wrap" catchtap="selectGoods" data-goods="{{goods}}">
<t-icon
size="40rpx"
color="{{goods.isSelected ? '#FA4126' : '#BBBBBB'}}"
name="{{goods.isSelected ? 'check-circle-filled' : 'circle'}}"
class="check"
/>
</view>
<view class="goods-sku-info">
<goods-card
layout="horizontal-wrap"
thumb-width="{{thumbWidth}}"
thumb-height="{{thumbHeight}}"
centered="{{true}}"
data="{{goods}}"
data-goods="{{goods}}"
catchspecs="specsTap"
catchclick="goGoodsDetail"
>
<view slot="thumb-cover" class="stock-mask" wx:if="{{goods.shortageStock || goods.stockQuantity <= 3}}">
仅剩{{goods.stockQuantity}}件
</view>
<view slot="append-body" class="goods-stepper">
<view class="stepper-tip" wx:if="{{goods.shortageStock}}">库存不足</view>
<t-stepper
classname="stepper-info"
value="{{goods.quantity}}"
min="{{1}}"
max="{{999}}"
data-goods="{{goods}}"
data-gi="{{gi}}"
data-si="{{si}}"
catchchange="changeStepper"
catchblur="input"
catchoverlimit="overlimit"
theme="grey"
/>
</view>
</goods-card>
</view>
</view>
<view
slot="right"
class="swiper-right-del"
bindtap="deleteGoods"
data-goods="{{goods}}"
>
删除
</view>
</swipeout>
</view>
<view class="promotion-line-wrap" wx:if="{{handlePromotion.hasPromotion(promotion.promotionCode) && promoindex != (store.promotionGoodsList.length - 2)}}">
<view class="promotion-line" />
</view>
</block>
<block wx:if="{{store.shortageGoodsList.length>0}}">
<view
class="goods-item"
wx:for="{{store.shortageGoodsList}}"
wx:for-item="goods"
wx:for-index="gi"
wx:key="extKey"
>
<swipeout right-width="{{ 72 }}">
<view class="goods-item-info">
<view class="check-wrap">
<view class="unCheck-icon" />
</view>
<view class="goods-sku-info">
<goods-card
layout="horizontal-wrap"
thumb-width="{{thumbWidth}}"
thumb-height="{{thumbHeight}}"
centered="{{true}}"
data="{{goods}}"
data-goods="{{goods}}"
catchspecs="specsTap"
catchclick="goGoodsDetail"
>
<view slot="thumb-cover" class="no-storage-mask" wx:if="{{goods.stockQuantity <=0}}">
<view class="no-storage-content">无货</view>
</view>
</goods-card>
</view>
</view>
<view
slot="right"
class="swiper-right-del"
bindtap="deleteGoods"
data-goods="{{goods}}"
>
删除
</view>
</swipeout>
</view>
<view class="promotion-line-wrap" wx:if="{{handlePromotion.hasPromotion(promotion.promotionCode) && promoindex != (store.promotionGoodsList.length - 2)}}">
<view class="promotion-line" />
</view>
</block>
</view>
</view>
<specs-popup
show="{{isShowSpecs}}"
title="{{currentGoods.title || ''}}"
price="{{currentGoods.price || ''}}"
thumb="{{utils.imgCut(currentGoods.thumb, 180, 180)}}"
specs="{{currentGoods.specs || []}}"
zIndex="{{999}}"
bindclose='hideSpecsPopup'
/>
<t-toast id="t-toast" />

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

Loading…
Cancel
Save