Initial commit

This commit is contained in:
sjk
2025-11-17 13:32:54 +08:00
commit e788eab6eb
1659 changed files with 171560 additions and 0 deletions

BIN
miniprogram/components/.DS_Store vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,35 @@
Component({
properties: {
// 图标名称
icon: {
type: String,
value: 'shop'
},
// 标题
title: {
type: String,
value: '暂无商品'
},
// 描述文字
description: {
type: String,
value: ''
},
// 是否显示操作按钮
showAction: {
type: Boolean,
value: false
},
// 操作按钮文字
actionText: {
type: String,
value: '刷新'
}
},
methods: {
onActionTap() {
this.triggerEvent('action');
}
}
});

View File

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

View File

@@ -0,0 +1,16 @@
<view class="empty-state wr-class">
<view class="empty-state__icon">
<t-icon name="{{icon || 'shop'}}" size="120rpx" color="#ddd" />
</view>
<view class="empty-state__title">{{title || '暂无商品'}}</view>
<view class="empty-state__description" wx:if="{{description}}">{{description}}</view>
<view class="empty-state__action" wx:if="{{showAction}}">
<t-button
variant="outline"
size="medium"
bind:tap="onActionTap"
>
{{actionText || '刷新'}}
</t-button>
</view>
</view>

View File

@@ -0,0 +1,32 @@
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 120rpx 40rpx;
text-align: center;
min-height: 400rpx;
}
.empty-state__icon {
margin-bottom: 32rpx;
}
.empty-state__title {
font-size: 32rpx;
color: #999;
margin-bottom: 16rpx;
font-weight: 500;
}
.empty-state__description {
font-size: 28rpx;
color: #bbb;
line-height: 1.5;
margin-bottom: 40rpx;
max-width: 400rpx;
}
.empty-state__action {
margin-top: 20rpx;
}

View File

@@ -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 });
},
},
});

View File

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

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -0,0 +1,133 @@
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,
});
},
},
prices: {
type: Array,
value: [],
observer(prices) {
this.setData({
prices,
});
},
},
color: {
type: String,
value: '#FA550F',
},
},
data: {
layout: 1,
overall: 1,
sorts: '',
},
methods: {
onChangeShowAction() {
const { layout } = this.data;
const nextLayout = layout === 1 ? 0 : 1;
console.log('[Filter] 布局切换:', {
currentLayout: layout === 1 ? '列表' : '网格',
nextLayout: nextLayout === 1 ? '列表' : '网格',
timestamp: new Date().toLocaleString()
});
this.triggerEvent('change', { ...this.properties, layout: nextLayout });
},
handlePriseSort() {
const { sorts } = this.data;
const nextSort = sorts === 'desc' ? 'asc' : 'desc';
console.log('[Filter] 价格排序点击:', {
currentSort: sorts || '默认',
nextSort: nextSort,
sortText: nextSort === 'asc' ? '价格从低到高' : '价格从高到低',
timestamp: new Date().toLocaleString(),
action: '用户点击价格排序按钮'
});
const changeData = {
...this.properties,
overall: 0,
sorts: nextSort,
};
console.log('[Filter] 触发排序变化事件:', {
eventData: changeData,
timestamp: new Date().toLocaleString()
});
this.triggerEvent('change', changeData);
},
open() {
this.triggerEvent('showFilterPopup', {
show: true,
});
},
onOverallAction() {
const { overall } = this.data;
const nextOverall = overall === 1 ? 0 : 1;
const nextData = {
sorts: '',
prices: [],
};
console.log('[Filter] 综合排序点击:', {
currentOverall: overall === 1 ? '综合排序' : '其他排序',
nextOverall: nextOverall === 1 ? '综合排序' : '其他排序',
resetData: nextData,
timestamp: new Date().toLocaleString(),
action: '用户点击综合排序,重置所有筛选条件'
});
const changeData = {
...this.properties,
...nextData,
overall: nextOverall,
};
console.log('[Filter] 触发综合排序变化事件:', {
eventData: changeData,
timestamp: new Date().toLocaleString()
});
this.triggerEvent('change', changeData);
},
},
});

View File

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

View File

@@ -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" />

View File

@@ -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;
}

View File

@@ -0,0 +1,159 @@
Component({
options: {
addGlobalClass: true,
},
externalClasses: ['wr-class'],
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: '¥',
},
showCart: {
type: Boolean,
value: true,
},
thresholds: {
type: Array,
value: [],
observer(thresholds) {
if (thresholds && thresholds.length) {
this.createIntersectionObserverHandle();
} else {
this.clearIntersectionObserverHandle();
}
},
},
},
data: {
independentID: '',
goods: { id: '' },
isValidityLinePrice: false,
},
lifetimes: {
attached() {
// 组件实例被放到页面节点树后执行
},
ready() {
this.init();
},
detached() {
this.clear();
},
},
observers: {
'goods.thumb': function(thumb) {
// 图片URL变化监听
}
},
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,
});
},
handleImageError(e) {
console.warn('商品卡片图片加载失败:', e.detail);
// 可以在这里设置默认图片或其他错误处理逻辑
},
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;
}
},
},
});

View File

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

View File

@@ -0,0 +1,72 @@
<view
id="{{independentID}}"
class="goods-card wr-class"
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
bind:error="handleImageError"
/>
<!-- 售罄遮罩 -->
<view wx:if="{{ !goods.isStock }}" class="goods-card__soldout">
<text class="goods-card__soldout-text">售罄</text>
</view>
</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.specs }}" class="goods-card__specs">
{{ goods.specs }}
</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
wx:if="{{showCart}}"
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>

View File

@@ -0,0 +1,166 @@
.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: 333rpx;
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: 333rpx;
height: 333rpx;
}
.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 16rpx 18rpx;
flex-direction: column;
width: 333rpx;
box-sizing: border-box;
}
.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__specs {
flex-shrink: 0;
font-size: 24rpx;
color: #999;
margin-top: 8rpx;
line-height: 32rpx;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.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;
}
/* 售罄遮罩样式 */
.goods-card__soldout {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
border-radius: 16rpx 16rpx 0 0;
}
.goods-card__soldout-text {
color: #fff;
font-size: 32rpx;
font-weight: bold;
}

View File

@@ -0,0 +1,66 @@
Component({
externalClasses: ['wr-class'],
properties: {
goodsList: {
type: Array,
value: [],
},
id: {
type: String,
value: '',
observer: (id) => {
this.genIndependentID(id);
},
},
showCart: {
type: Boolean,
value: true,
},
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)}`,
});
}
},
},
});

View File

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

View File

@@ -0,0 +1,17 @@
<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}}"
show-cart="{{showCart}}"
wr-class="goods-card-inside"
data-index="{{index}}"
bind:thumb="onClickGoodsThumb"
bind:click="onClickGoods"
bind:add-cart="onAddCart"
/>
</block>
</view>

View File

@@ -0,0 +1,14 @@
.goods-list-wrap {
display: flex;
flex-flow: row wrap;
justify-content: space-between;
padding: 16rpx 12rpx;
background: #fff;
gap: 12rpx;
}
/* 使商品卡按两列栅格排布 */
.goods-card-inside {
width: calc((100% - 12rpx) / 2);
box-sizing: border-box;
}

View File

@@ -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');
}
},
},
});

View File

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

View File

@@ -0,0 +1,34 @@
<view
class="load-more wr-class"
wx:if="{{!(listIsEmpty && (status === 0 || status === 2))}}"
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>
<!-- 加载失败 -->
<view class="load-more__error" wx:if="{{status===3}}">
加载失败
<text class="load-more__refresh-btn" bind:tap="tapHandle">刷新</text>
</view>
</view>
<view wx:if="{{listIsEmpty && (status === 0 || status === 2)}}" class="load-more__wrap">
<slot name="empty" />
</view>

View File

@@ -0,0 +1,35 @@
.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;
--td-loading-color: #fa4126;
}
.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;
}
.load-more__error {
margin: auto;
}
.load-more__refresh-btn {
margin-left: 16rpx;
color: #fa4126;
}

View File

@@ -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)',
},
},
});

View File

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

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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: ['0', ''], // 确保默认值
},
methods: {
format(price) {
price = parseFloat(`${price}`);
const pArr = ['0', '']; // 默认初始化
if (!isNaN(price)) {
const isMinus = price < 0;
if (isMinus) {
price = -price;
}
if (this.properties.priceUnit === 'yuan') {
const priceSplit = price.toString().split('.');
pArr[0] = priceSplit.length > 0 ? priceSplit[0] : '0';
pArr[1] = !priceSplit[1]
? '00'
: priceSplit[1].length === 1
? `${priceSplit[1]}0`
: priceSplit[1].substring(0, 2);
} 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] && pArr[1].length > 1 && pArr[1][1] === '0') pArr[1] = pArr[1][0];
}
if (isMinus) {
pArr[0] = `-${pArr[0]}`;
}
}
this.setData({ pArr });
},
},
});

View File

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

View File

@@ -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 && pArr.length > 0 ? pArr[0] : '0'}}</view>
<view wx:if="{{pArr && pArr.length > 1 && pArr[1]}}" class="decimal inline {{decimalSmaller ? 'smaller' : ''}} decimal-class">.{{pArr[1]}}</view>
</view>
</view>

View File

@@ -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;
}

View File

@@ -0,0 +1,98 @@
Component({
options: {
addGlobalClass: true,
multipleSlots: true,
},
externalClasses: ['coupon-class'],
properties: {
mask: {
type: Boolean,
value: false, // 是否添加遮罩
},
superposable: {
type: Boolean,
value: false, // 是否可叠加
},
type: {
type: Number,
value: 0, // 优惠券类型CouponType
},
value: {
type: String,
value: '', // 优惠金额
},
tag: {
type: String,
value: '', // 优惠标签优惠券名字标签img
},
desc: {
type: String,
value: '', // 优惠金额描述,金额下方
},
title: {
type: String, // 优惠券名称
value: '',
},
timeLimit: {
type: String, // 优惠券时限
value: '',
},
ruleDesc: {
type: String, // 优惠券适用规则描述
value: '',
},
currency: {
type: String,
value: '¥', // 优惠货币
},
status: {
type: String,
value: 'default',
},
image: {
type: String,
value: '',
},
userCouponId: {
type: Number,
value: 0, // 用户优惠券ID
},
couponTemplateId: {
type: Number,
value: 0, // 优惠券模板ID
},
},
data: {
CouponType: {
MJ_COUPON: 1,
ZK_COUPON: 2,
MJF_COUPON: 3,
MYF_COUPON: 3, // 免邮券的另一种类型与MJF_COUPON相同
GIFT_COUPON: 4,
MERCHANT_MJ_COUPON: 1, // 商家满减券与MJ_COUPON相同
MERCHANT_ZK_COUPON: 2, // 商家折扣券与ZK_COUPON相同
},
theme: 'primary',
},
observers: {
status: function (value) {
let theme = 'primary';
// 已过期或已使用的券 颜色置灰
if (value === 'useless' || value === 'disabled') {
theme = 'weak';
}
this.setData({ theme });
},
},
attached() {
this.setData({
color: `color${this.properties.colorStyle}`,
});
},
});

View File

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

View File

@@ -0,0 +1,63 @@
<wxs module="tools">
function isBigValue(value) {
if (!value) return false;
var values = (value + '').split('.');
if (values[1] && values[0].length >= 3) {
return true;
} else {
return false;
}
}
function getBigValues(value) {
if (!value) return ['0', ''];
return (value + '').split('.');
}
module.exports = {
isBigValue:isBigValue,
getBigValues: getBigValues
};
</wxs>
<view class="wr-coupon coupon-class theme-{{theme}}">
<view class="wr-coupon__left">
<view wx:if="{{type == CouponType.ZK_COUPON || type === CouponType.MERCHANT_ZK_COUPON}}">
<text class="wr-coupon__left--value">{{value / 10}}</text>
<text class="wr-coupon__left--unit">折</text>
<view class="wr-coupon__left--desc">{{desc}}</view>
</view>
<view wx:if="{{type == CouponType.MJ_COUPON || type === CouponType.MERCHANT_MJ_COUPON}}">
<text class="wr-coupon__left--value" wx:if="{{tools.isBigValue(value / 100)}}">
<text class="wr-coupon__left--value-int">{{tools.getBigValues(value / 100) && tools.getBigValues(value / 100).length > 0 ? tools.getBigValues(value / 100)[0] : '0'}}</text>
<text class="wr-coupon__left--value-decimal" wx:if="{{tools.getBigValues(value / 100) && tools.getBigValues(value / 100).length > 1}}">.{{tools.getBigValues(value / 100)[1]}}</text>
</text>
<text class="wr-coupon__left--value" wx:else>{{value / 100}}</text>
<text class="wr-coupon__left--unit">元</text>
<view class="wr-coupon__left--desc">{{desc}}</view>
</view>
<view wx:if="{{type === CouponType.MJF_COUPON || type === CouponType.MYF_COUPON}}">
<text class="wr-coupon__left--value" style="font-family: PingFang SC; font-size: 44rpx">免邮</text>
<view class="wr-coupon__left--desc">{{desc}}</view>
</view>
<view wx:if="{{type == CouponType.GIFT_COUPON}}">
<t-image t-class="wr-coupon__left--image" src="{{image}}" mode="aspectFill" />
</view>
</view>
<view class="wr-coupon__right">
<view class="wr-coupon__right--title">
<text class="coupon-title">{{title}}</text>
<view class="coupon-time">{{timeLimit}}</view>
<view class="coupon-desc">
<view wx:if="{{ruleDesc}}">{{ruleDesc}}</view>
</view>
<!-- 显示用户优惠券ID用于调试 -->
<view class="coupon-debug-info" style="font-size: 20rpx; color: #999; margin-top: 8rpx;">
用户券ID: {{userCouponId}} | 模板ID: {{couponTemplateId}}
</view>
</view>
<view class="wr-coupon__right--oper">
<slot name="operator" />
</view>
</view>
<view wx:if="{{status === 'useless' || status === 'disabled'}}" class="wr-coupon__seal seal-{{status}}}" />
<view wx:if="{{mask}}" class="wr-coupon__mask" />
<view wx:if="{{superposable}}" class="wr-coupon__tag">可叠加</view>
</view>

View File

@@ -0,0 +1,147 @@
.wr-coupon {
display: flex;
background-image: url('https://tdesign.gtimg.com/miniprogram/template/retail/coupon/coupon-bg-nocorners.png');
background-size: 100% 100%;
background-repeat: no-repeat;
position: relative;
margin-bottom: 24rpx;
overflow: hidden;
}
.theme-weak.wr-coupon {
background-image: url('https://tdesign.gtimg.com/miniprogram/template/retail/coupon/coupon-bg-grey2.png');
}
.wr-coupon__left {
width: 200rpx;
height: 180rpx;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
color: #fa4126;
overflow: hidden;
position: relative;
}
.theme-weak .wr-coupon__left {
color: #333;
}
.wr-coupon__left--value {
font-size: 64rpx;
line-height: 88rpx;
font-weight: bold;
font-family: 'DIN Alternate', cursive;
}
.wr-coupon__left--value-int {
font-size: 48rpx;
line-height: 88rpx;
}
.wr-coupon__left--value-decimal {
font-size: 36rpx;
line-height: 48rpx;
}
.wr-coupon__left--image {
width: 128rpx;
height: 128rpx;
border-radius: 8px;
margin-top: 30rpx;
}
.wr-coupon__left--unit {
font-size: 24rpx;
line-height: 32rpx;
}
.wr-coupon__left--desc {
font-size: 24rpx;
line-height: 32rpx;
color: #fa4126;
}
.theme-weak .wr-coupon__left--desc {
color: #333;
}
.wr-coupon__right {
flex-grow: 1;
padding: 0 20rpx;
height: 180rpx;
box-sizing: border-box;
overflow: hidden;
display: flex;
align-items: center;
}
.wr-coupon__right--title {
display: flex;
-webkit-display: flex;
flex-direction: column;
align-items: flex-start;
color: #999999;
font-size: 24rpx;
flex: 1;
}
.wr-coupon__right--title .coupon-title {
max-width: 320rpx;
color: #333333;
font-size: 28rpx;
line-height: 40rpx;
font-weight: bold;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
overflow: hidden;
white-space: normal;
}
.wr-coupon__right--title .coupon-time {
margin-top: 16rpx;
/* // letter-spacing: -0.05em; */
}
.wr-coupon__right--title .coupon-desc {
margin-top: 8rpx;
}
.wr-coupon__right--title .coupon-arrow {
font-size: 22rpx;
}
.wr-coupon__right--oper {
display: flex;
justify-content: center;
align-items: center;
}
.wr-coupon__mask {
width: 702rpx;
height: 182rpx;
position: absolute;
top: 0;
left: 0;
background-color: #ffffff;
opacity: 0.5;
}
.wr-coupon__tag {
position: absolute;
top: 8px;
right: -24rpx;
text-align: center;
width: 106rpx;
height: 28rpx;
opacity: 0.9;
font-size: 20rpx;
line-height: 28rpx;
color: #fa4126;
border: 0.5px solid #fa4126;
box-sizing: border-box;
transform: rotate(45deg);
}
.wr-coupon__seal {
width: 128rpx;
height: 128rpx;
position: absolute;
top: 0;
right: 0;
background-size: 100% 100%;
}
.wr-coupon__seal.seal-useless {
background-image: url('https://tdesign.gtimg.com/miniprogram/template/retail/coupon/seal-used.png');
}
.wr-coupon__seal.seal-disabled {
background-image: url('https://tdesign.gtimg.com/miniprogram/template/retail/coupon/coupon-expired.png');
}

View File

@@ -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();
}
},
},
});

View File

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

View File

@@ -0,0 +1,176 @@
<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 && event.touches[0];
if (!touchPoint) return;
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 && event.touches[0];
if (!touchPoint) return;
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>

View File

@@ -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);
}

View File

@@ -0,0 +1,88 @@
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);
},
// 添加错误处理方法,防止组件报错
handleError(e) {
console.warn('[webp-image] 图片加载失败:', e.detail);
this.triggerEvent('error', e.detail);
},
// 添加onError方法的别名确保兼容性
error(e) {
console.warn('[webp-image] 图片错误事件:', e);
this.onError(e);
},
},
});

View File

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

View File

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

View File

@@ -0,0 +1,153 @@
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 ||
url.indexOf('tral.cc') !== -1 ||
url.indexOf('localhost') !== -1 ||
url.indexOf('192.168.') !== -1 ||
url.indexOf('127.0.0.1') !== -1
) {
//qlogo.cn域名、tral.cc域名、本地开发环境或者本地图片不做转换
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 '';
// 检查是否为本地开发环境如果是则直接返回原始URL
if (
options.src.indexOf('localhost') !== -1 ||
options.src.indexOf('192.168.') !== -1 ||
options.src.indexOf('127.0.0.1') !== -1
) {
return options.src;
}
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,
};