通过微信开发者工具 商城模板 创建新小程序
This commit is contained in:
		
							
								
								
									
										36
									
								
								mini-program/components/filter-popup/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								mini-program/components/filter-popup/index.js
									
									
									
									
									
										Normal 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 });
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										6
									
								
								mini-program/components/filter-popup/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mini-program/components/filter-popup/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "component": true,
 | 
			
		||||
    "usingComponents": {
 | 
			
		||||
        "t-popup": "tdesign-miniprogram/popup/popup"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								mini-program/components/filter-popup/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								mini-program/components/filter-popup/index.wxml
									
									
									
									
									
										Normal 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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										39
									
								
								mini-program/components/filter-popup/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								mini-program/components/filter-popup/index.wxss
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										84
									
								
								mini-program/components/filter/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								mini-program/components/filter/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -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,
 | 
			
		||||
      });
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										6
									
								
								mini-program/components/filter/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mini-program/components/filter/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "component": true,
 | 
			
		||||
    "usingComponents": {
 | 
			
		||||
        "t-icon": "tdesign-miniprogram/icon/icon"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										37
									
								
								mini-program/components/filter/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								mini-program/components/filter/index.wxml
									
									
									
									
									
										Normal 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" />
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										50
									
								
								mini-program/components/filter/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								mini-program/components/filter/index.wxss
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										141
									
								
								mini-program/components/goods-card/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								mini-program/components/goods-card/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										8
									
								
								mini-program/components/goods-card/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								mini-program/components/goods-card/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
    "component": true,
 | 
			
		||||
    "usingComponents": {
 | 
			
		||||
        "price": "/components/price/index",
 | 
			
		||||
        "t-icon": "tdesign-miniprogram/icon/icon",
 | 
			
		||||
        "t-image": "/components/webp-image/index"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										63
									
								
								mini-program/components/goods-card/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								mini-program/components/goods-card/index.wxml
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										133
									
								
								mini-program/components/goods-card/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								mini-program/components/goods-card/index.wxss
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								mini-program/components/goods-list/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								mini-program/components/goods-list/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -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)}`,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										6
									
								
								mini-program/components/goods-list/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mini-program/components/goods-list/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
    "component": true,
 | 
			
		||||
    "usingComponents": {
 | 
			
		||||
        "goods-card": "/components/goods-card/index"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								mini-program/components/goods-list/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								mini-program/components/goods-list/index.wxml
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								mini-program/components/goods-list/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								mini-program/components/goods-list/index.wxss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
.goods-list-wrap {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-flow: row wrap;
 | 
			
		||||
  justify-content: space-between;
 | 
			
		||||
  padding: 0;
 | 
			
		||||
  background: #fff;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										54
									
								
								mini-program/components/load-more/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								mini-program/components/load-more/index.js
									
									
									
									
									
										Normal 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');
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										7
									
								
								mini-program/components/load-more/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								mini-program/components/load-more/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
			
		||||
{
 | 
			
		||||
    "component": true,
 | 
			
		||||
    "usingComponents": {
 | 
			
		||||
        "t-loading": "tdesign-miniprogram/loading/loading",
 | 
			
		||||
        "t-divider": "tdesign-miniprogram/divider/divider"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								mini-program/components/load-more/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								mini-program/components/load-more/index.wxml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
<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>
 | 
			
		||||
 | 
			
		||||
  <!-- 加载失败 -->
 | 
			
		||||
  <view class="load-more__error" wx:if="{{status===3}}">
 | 
			
		||||
    加载失败
 | 
			
		||||
    <text class="load-more__refresh-btn" bind:tap="tapHandle">刷新</text>
 | 
			
		||||
  </view>
 | 
			
		||||
</view>
 | 
			
		||||
 | 
			
		||||
<!-- 支持通过slot传入页面/列表的空态,load-more来控制空态的显示状态 -->
 | 
			
		||||
<slot wx:if="{{listIsEmpty && (status === 0 || status === 2)}}" name="empty" />
 | 
			
		||||
							
								
								
									
										35
									
								
								mini-program/components/load-more/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								mini-program/components/load-more/index.wxss
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										23
									
								
								mini-program/components/loading-content/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								mini-program/components/loading-content/index.js
									
									
									
									
									
										Normal 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)',
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										6
									
								
								mini-program/components/loading-content/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mini-program/components/loading-content/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "component": true,
 | 
			
		||||
  "usingComponents": {
 | 
			
		||||
    "t-loading": "tdesign-miniprogram/loading/loading"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								mini-program/components/loading-content/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								mini-program/components/loading-content/index.wxml
									
									
									
									
									
										Normal 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>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										23
									
								
								mini-program/components/loading-content/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								mini-program/components/loading-content/index.wxss
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								mini-program/components/price/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								mini-program/components/price/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
Component({
 | 
			
		||||
  externalClasses: ['wr-class', 'symbol-class', 'decimal-class'],
 | 
			
		||||
  useStore: [],
 | 
			
		||||
  properties: {
 | 
			
		||||
    priceUnit: {
 | 
			
		||||
      type: String,
 | 
			
		||||
      value: 'fen',
 | 
			
		||||
    }, // 价格单位,分 | 元, fen,yuan
 | 
			
		||||
    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 });
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										4
									
								
								mini-program/components/price/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								mini-program/components/price/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
  "component": true,
 | 
			
		||||
  "usingComponents": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										21
									
								
								mini-program/components/price/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								mini-program/components/price/index.wxml
									
									
									
									
									
										Normal 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[0]}}</view>
 | 
			
		||||
		<view wx:if="{{pArr[1]}}" class="decimal inline {{decimalSmaller ? 'smaller' : ''}} decimal-class">.{{pArr[1]}}</view>
 | 
			
		||||
	</view>
 | 
			
		||||
</view>
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								mini-program/components/price/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								mini-program/components/price/index.wxss
									
									
									
									
									
										Normal 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								mini-program/components/swipeout/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								mini-program/components/swipeout/index.js
									
									
									
									
									
										Normal 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();
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										4
									
								
								mini-program/components/swipeout/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								mini-program/components/swipeout/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
			
		||||
{
 | 
			
		||||
  "component": true,
 | 
			
		||||
  "usingComponents": {}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										174
									
								
								mini-program/components/swipeout/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								mini-program/components/swipeout/index.wxml
									
									
									
									
									
										Normal file
									
								
							@@ -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>
 | 
			
		||||
							
								
								
									
										18
									
								
								mini-program/components/swipeout/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								mini-program/components/swipeout/index.wxss
									
									
									
									
									
										Normal 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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										86
									
								
								mini-program/components/webp-image/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								mini-program/components/webp-image/index.js
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										6
									
								
								mini-program/components/webp-image/index.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								mini-program/components/webp-image/index.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "component": true,
 | 
			
		||||
  "usingComponents": {
 | 
			
		||||
    "t-image": "tdesign-miniprogram/image/image"
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								mini-program/components/webp-image/index.wxml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								mini-program/components/webp-image/index.wxml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
<wxs src="./utils.wxs" module="Utils" />
 | 
			
		||||
<t-image
 | 
			
		||||
  t-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
									
								
								mini-program/components/webp-image/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								mini-program/components/webp-image/index.wxss
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										140
									
								
								mini-program/components/webp-image/utils.wxs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								mini-program/components/webp-image/utils.wxs
									
									
									
									
									
										Normal file
									
								
							@@ -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,
 | 
			
		||||
};
 | 
			
		||||
		Reference in New Issue
	
	Block a user