This commit is contained in:
sjk
2025-11-17 14:11:46 +08:00
commit ad4a600af9
1659 changed files with 171560 additions and 0 deletions

31
miniprogram/.eslintrc.js Normal file
View File

@@ -0,0 +1,31 @@
/*
* Eslint config file
* Documentation: https://eslint.org/docs/user-guide/configuring/
* Install the Eslint extension before using this feature.
*/
module.exports = {
env: {
es6: true,
browser: true,
node: true,
},
ecmaFeatures: {
modules: true,
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
globals: {
wx: true,
App: true,
Page: true,
getCurrentPages: true,
getApp: true,
Component: true,
requirePlugin: true,
requireMiniProgram: true,
},
// extends: 'eslint:recommended',
rules: {},
}

9
miniprogram/LICENSE Normal file
View File

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

127
miniprogram/README.md Normal file
View File

@@ -0,0 +1,127 @@
<p align="center">
<a href="https://tdesign.tencent.com/" target="_blank">
<img alt="TDesign Logo" width="200" src="https://tdesign.gtimg.com/site/TDesign.png">
</a>
</p>
<p align="center">
<a href="https://img.shields.io/github/stars/Tencent/tdesign-miniprogram-starter-retail">
<img src="https://img.shields.io/github/stars/Tencent/tdesign-miniprogram-starter-retail" alt="License">
</a>
<a href="https://github.com/Tencent/tdesign-miniprogram-starter-retail/issues">
<img src="https://img.shields.io/github/issues/Tencent/tdesign-miniprogram-starter-retail" alt="License">
</a>
<a href="https://github.com/Tencent/tdesign-miniprogram-starter-retail/LICENSE">
<img src="https://img.shields.io/github/license/Tencent/tdesign-miniprogram-starter-retail" alt="License">
</a>
<a href="https://www.npmjs.com/package/tdesign-miniprogram">
<img src="https://img.shields.io/npm/v/tdesign-miniprogram.svg?sanitize=true" alt="Version">
</a>
<a href="https://www.npmjs.com/package/tdesign-miniprogram">
<img src="https://img.shields.io/npm/dw/tdesign-miniprogram" alt="Downloads">
</a>
</p>
# TDesign 零售行业模版示例小程序
TDesign 零售模版示例小程序采用 [TDesign 企业级设计体系小程序解决方案](https://tdesign.tencent.com/miniprogram/overview) 进行搭建,依赖 [TDesign 微信小程序组件库](https://github.com/Tencent/tdesign-miniprogram),涵盖完整的基本零售场景需求。
## :high_brightness: 预览
<p>请使用微信扫描以下二维码:</p>
<img src="https://we-retail-static-1300977798.cos.ap-guangzhou.myqcloud.com/retail-mp/common/qrcode.jpeg" width = "200" height = "200" alt="模版小程序二维码" align=center />
## :pushpin: 项目介绍
### 1. 业务介绍
零售行业模版小程序是个经典的单店版电商小程序,涵盖了电商的黄金链路流程,从商品->购物车->结算->订单等。小程序总共包含 28 个完整的页面,涵盖首页,商品详情页,个人中心,售后流程等基础页面。采用 mock 数据进行展示,提供了完整的零售商品展示、交易与售后流程。页面详情:
<img src="https://tdesign.gtimg.com/miniprogram/template/retail/tdesign-starter-readmeV1.png" width = "650" height = "900" alt="模版小程序页面详情" align=center />
主要页面截图如下:
<p align="center">
<img alt="example-home" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v1/home.png" />
<img alt="example-sort" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v2/sort.png" />
<img alt="example-cart" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v1/cart.png" />
<img alt="example-user-center" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v1/user-center.png" />
<img alt="example-goods-detail" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v1/goods-detail.png" />
<img alt="example-pay" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v1/pay.png" />
<img alt="example-order" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v1/order.png" />
<img alt="example-order-detail" width="200" src="https://tdesign.gtimg.com/miniprogram/template/retail/example/v2/order.png" />
</p>
### 2. 项目构成
零售行业模版小程序采用基础的 JavaScript + WXSS + ESLint 进行构建,降低了使用门槛。
项目目录结构如下:
```
|-- tdesign-miniprogram-starter
|-- README.md
|-- app.js
|-- app.json
|-- app.wxss
|-- components // 公共组件库
|-- config // 基础配置
|-- custom-tab-bar // 自定义 tabbar
|-- model // mock 数据
|-- pages
| |-- cart // 购物车相关页面
| |-- coupon // 优惠券相关页面
| |-- goods // 商品相关页面
| |-- home // 首页
| |-- order // 订单售后相关页面
| |-- promotion-detail // 营销活动页面
| |-- usercenter // 个人中心及收货地址相关页面
|-- services // 请求接口
|-- style // 公共样式与iconfont
|-- utils // 工具库
```
### 3. 数据模拟
零售小程序采用真实的接口数据,模拟后端返回逻辑,在小程序展示完整的购物场景与购物体验逻辑。
### 4. 添加新页面
1.`pages `目录下创建对应的页面文件夹
2.`app.json` 文件中的 ` "pages"` 数组中加上页面路径
3. [可选] 在 `project.config.json` 文件的 `"miniprogram-list"` 下添加页面配置
## :hammer: 构建运行
1. `npm install`
2. 小程序开发工具中引入工程
3. 构建 npm
## :art: 代码风格控制
`eslint` `prettier`
## :iphone: 基础库版本
最低基础库版本`^2.6.5`
## :dart: 反馈
企业微信群
TDesign 团队会及时在企业微信大群中同步发布版本、问题修复信息,也会有一些关于组件库建设的讨论,欢迎微信或企业微信扫码入群交流:
<img src="https://oteam-tdesign-1258344706.cos.ap-guangzhou.myqcloud.com/site/doc/TDesign%20IM.png" width = "200" height = "200" alt="模版小程序页面详情" align=center />
邮件联系tdesign@tencent.com
## :link: TDesign 其他技术栈实现
- 移动端 小程序 实现:[mobile-miniprogram](https://github.com/Tencent/tdesign-miniprogram)
- 桌面端 Vue 2 实现:[web-vue](https://github.com/Tencent/tdesign-vue)
- 桌面端 Vue 3 实现:[web-vue-next](https://github.com/Tencent/tdesign-vue-next)
- 桌面端 React 实现:[web-react](https://github.com/Tencent/tdesign-react)
## :page_with_curl: 开源协议
TDesign 遵循 [MIT 协议](https://github.com/Tencent/tdesign-miniprogram-starter-retail/LICENSE)。

23
miniprogram/app.js Normal file
View File

@@ -0,0 +1,23 @@
import updateManager from './common/updateManager';
const { config } = require('./config/index');
App({
globalData: {
config: {
apiBaseUrl: config.apiBase
}
},
onLaunch: function () {
// 小程序启动时的初始化逻辑
console.log('小程序启动');
console.log('API基础URL:', this.globalData.config.apiBaseUrl);
// 初始化更新管理器
// 微信在小程序每次启动(包括热启动)时自动检查更新
updateManager();
},
onShow: function () {
// 小程序显示时的逻辑
console.log('小程序显示');
},
});

111
miniprogram/app.json Normal file
View File

@@ -0,0 +1,111 @@
{
"pages": ["pages/home/home", "pages/category/index", "pages/cart/index", "pages/usercenter/index", "pages/login/index", "pages/points/index", "pages/network-test/index", "pages/help-center/index", "pages/error-log/index", "pages/user-agreement/index", "pages/privacy-policy/index", "pages/debug/payment-test", "pages/debug/comment-test", "pages/debug/comment-display-test",
"pages/debug/simple-comment-test",
"pages/debug/api-test",
"pages/debug/hardcoded-comment-test",
"pages/test-my-comments/index",
"pages/test-comment-stats/index"],
"subpackages": [
{
"root": "pages/user",
"name": "user",
"pages": ["person-info/index", "address/list/index", "address/edit/index", "name-edit/index", "phone-verify/index"]
},
{
"root": "pages/goods",
"name": "goods",
"pages": [
"list/index",
"details/index",
"search/index",
"result/index",
"comments/index",
"comments/create/index"
]
},
{
"root": "pages/order",
"name": "order",
"pages": [
"order-confirm/index",
"receipt/index",
"pay-result/index",
"order-list/index",
"order-detail/index",
"apply-service/index",
"after-service-list/index",
"after-service-detail/index",
"fill-tracking-no/index",
"delivery-detail/index",
"invoice/index",
"comment/index",
"comment-list/index"
]
},
{
"root": "pages/refund",
"name": "refund",
"pages": [
"refund-list/index",
"refund-detail/index",
"refund-apply/index"
]
},
{
"root": "pages/coupon",
"name": "coupon",
"pages": ["coupon-list/index", "coupon-detail/index", "coupon-activity-goods/index", "coupon-center/index"]
},
{
"root": "pages/promotion",
"name": "promotion",
"pages": ["promotion-detail/index"]
}
],
"tabBar": {
"custom": true,
"color": "#666666",
"selectedColor": "#FF5F15",
"backgroundColor": "#ffffff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页"
},
{
"pagePath": "pages/category/index",
"text": "分类"
},
{
"pagePath": "pages/cart/index",
"text": "购物车"
},
{
"pagePath": "pages/usercenter/index",
"text": "我的"
}
]
},
"requiredPrivateInfos": ["chooseAddress","chooseLocation"],
"lazyCodeLoading": "requiredComponents",
"usingComponents": {},
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "Weixin",
"navigationBarTextStyle": "black"
},
"sitemapLocation": "sitemap.json",
"permission": {
"scope.userLocation": {
"desc": "你的位置信息将用于小程序位置接口的效果展示"
}
},
"networkTimeout": {
"request": 60000,
"downloadFile": 60000
},
"debug": false,
"functionalPages": true
}

7
miniprogram/app.wxss Normal file
View File

@@ -0,0 +1,7 @@
@import 'style/iconfont.wxss';
@import 'style/theme.wxss';
.cell--noborder .t-cell::after {
border-bottom: none !important;
}

View File

@@ -0,0 +1,21 @@
<svg width="160" height="160" viewBox="0 0 160 160" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- 背景圆形 -->
<circle cx="80" cy="80" r="80" fill="url(#gradient)" />
<!-- 购物袋图标 -->
<path d="M50 60 L110 60 L105 120 L55 120 Z" fill="white" opacity="0.9" />
<path d="M65 60 L65 50 C65 42 72 35 80 35 C88 35 95 42 95 50 L95 60" stroke="white" stroke-width="4" fill="none" opacity="0.9" />
<!-- 装饰元素 -->
<circle cx="70" cy="85" r="3" fill="white" opacity="0.7" />
<circle cx="90" cy="85" r="3" fill="white" opacity="0.7" />
<circle cx="80" cy="100" r="2" fill="white" opacity="0.5" />
<!-- 渐变定义 -->
<defs>
<linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#667eea;stop-opacity:1" />
<stop offset="100%" style="stop-color:#764ba2;stop-opacity:1" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 919 B

View File

@@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-conventional'] };

View File

@@ -0,0 +1,133 @@
/**
* 小程序自动更新管理器
* 基于微信官方UpdateManager API实现
* 文档https://developers.weixin.qq.com/miniprogram/dev/api/base/update/UpdateManager.html
*/
let updateRetryCount = 0; // 更新重试次数
const MAX_RETRY_COUNT = 3; // 最大重试次数
export default () => {
// 检查是否支持UpdateManager
if (!wx.canIUse('getUpdateManager')) {
console.warn('当前微信版本过低,无法使用 UpdateManager');
return;
}
const updateManager = wx.getUpdateManager();
// 监听检查更新结果
updateManager.onCheckForUpdate(function (res) {
console.log('检查更新结果:', res);
if (res.hasUpdate) {
console.log('发现新版本,开始下载...');
// 显示下载提示
wx.showToast({
title: '发现新版本,正在下载...',
icon: 'loading',
duration: 2000,
mask: true
});
} else {
console.log('当前已是最新版本');
}
});
// 监听新版本下载完成
updateManager.onUpdateReady(function () {
console.log('新版本下载完成,准备更新');
wx.hideToast(); // 隐藏下载提示
wx.showModal({
title: '更新提示',
content: '新版本已准备就绪,立即重启应用以获得更好的体验?',
confirmText: '立即更新',
cancelText: '稍后更新',
confirmColor: '#576B95',
success(res) {
if (res.confirm) {
console.log('用户确认更新,开始应用新版本');
// 显示更新中提示
wx.showLoading({
title: '更新中...',
mask: true
});
try {
// 应用新版本并重启
updateManager.applyUpdate();
} catch (error) {
console.error('应用更新失败:', error);
wx.hideLoading();
wx.showToast({
title: '更新失败,请稍后重试',
icon: 'error'
});
}
} else {
console.log('用户选择稍后更新');
// 可以在这里设置定时提醒或其他逻辑
setTimeout(() => {
wx.showToast({
title: '建议及时更新以获得最佳体验',
icon: 'none',
duration: 3000
});
}, 30000); // 30秒后提醒
}
},
fail(error) {
console.error('显示更新弹窗失败:', error);
}
});
});
// 监听新版本下载失败
updateManager.onUpdateFailed(function (error) {
console.error('新版本下载失败:', error);
updateRetryCount++;
wx.hideToast(); // 隐藏下载提示
if (updateRetryCount < MAX_RETRY_COUNT) {
console.log(`${updateRetryCount}次重试下载更新`);
wx.showModal({
title: '下载失败',
content: `新版本下载失败,是否重试?(${updateRetryCount}/${MAX_RETRY_COUNT})`,
confirmText: '重试',
cancelText: '取消',
success(res) {
if (res.confirm) {
// 重新检查更新
setTimeout(() => {
console.log('重新检查更新...');
// 这里可以添加重新检查更新的逻辑
// 注意:微信会自动检查更新,开发者无需主动触发
}, 2000);
} else {
console.log('用户取消重试');
updateRetryCount = 0; // 重置重试次数
}
}
});
} else {
console.error('更新重试次数已达上限');
updateRetryCount = 0; // 重置重试次数
wx.showModal({
title: '更新失败',
content: '新版本下载多次失败,请检查网络连接或稍后再试',
showCancel: false,
confirmText: '知道了',
success() {
console.log('用户确认更新失败提示');
}
});
}
});
console.log('UpdateManager 初始化完成');
};

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

View File

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

20477
miniprogram/config/index.js Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,85 @@
import { SuperComponent } from '../common/src/index';
export default class ActionSheet extends SuperComponent {
static show: (options: import("./show").ActionSheetShowOption) => WechatMiniprogram.Component.TrivialInstance;
behaviors: string[];
externalClasses: string[];
properties: {
align?: {
type: StringConstructor;
value?: "left" | "center";
};
cancelText?: {
type: StringConstructor;
value?: string;
};
count?: {
type: NumberConstructor;
value?: number;
};
description?: {
type: StringConstructor;
value?: string;
};
items: {
type: ArrayConstructor;
value?: (string | import("./type").ActionSheetItem)[];
required?: boolean;
};
popupProps?: {
type: ObjectConstructor;
value?: import("../popup").TdPopupProps;
};
showCancel?: {
type: BooleanConstructor;
value?: boolean;
};
showOverlay?: {
type: BooleanConstructor;
value?: boolean;
};
theme?: {
type: StringConstructor;
value?: "list" | "grid";
};
usingCustomNavbar?: {
type: BooleanConstructor;
value?: boolean;
};
visible?: {
type: BooleanConstructor;
value?: boolean;
};
defaultVisible?: {
type: BooleanConstructor;
value?: boolean;
};
};
data: {
prefix: string;
classPrefix: string;
gridThemeItems: any[];
currentSwiperIndex: number;
defaultPopUpProps: {};
defaultPopUpzIndex: number;
};
controlledProps: {
key: string;
event: string;
}[];
observers: {
'visible, items'(visible: boolean): void;
};
methods: {
init(): void;
memoInitialData(): void;
splitGridThemeActions(): void;
show(options: any): void;
close(): void;
onPopupVisibleChange({ detail }: {
detail: any;
}): void;
onSwiperChange(e: WechatMiniprogram.TouchEvent): void;
onSelect(event: WechatMiniprogram.TouchEvent): void;
onCancel(): void;
};
}

View File

@@ -0,0 +1 @@
import{__decorate}from"tslib";import{chunk}from"../common/utils";import{SuperComponent,wxComponent}from"../common/src/index";import config from"../common/config";import{ActionSheetTheme,show}from"./show";import props from"./props";import useCustomNavbar from"../mixins/using-custom-navbar";const{prefix:prefix}=config,name=`${prefix}-action-sheet`;let ActionSheet=class extends SuperComponent{constructor(){super(...arguments),this.behaviors=[useCustomNavbar],this.externalClasses=[`${prefix}-class`,`${prefix}-class-content`,`${prefix}-class-cancel`],this.properties=Object.assign({},props),this.data={prefix:prefix,classPrefix:name,gridThemeItems:[],currentSwiperIndex:0,defaultPopUpProps:{},defaultPopUpzIndex:11500},this.controlledProps=[{key:"visible",event:"visible-change"}],this.observers={"visible, items"(e){e&&this.init()}},this.methods={init(){this.memoInitialData(),this.splitGridThemeActions()},memoInitialData(){this.initialData=Object.assign(Object.assign({},this.properties),this.data)},splitGridThemeActions(){this.data.theme===ActionSheetTheme.Grid&&this.setData({gridThemeItems:chunk(this.data.items,this.data.count)})},show(e){this.setData(Object.assign(Object.assign(Object.assign({},this.initialData),e),{visible:!0})),this.splitGridThemeActions(),this.autoClose=!0,this._trigger("visible-change",{visible:!0})},close(){this.triggerEvent("close",{trigger:"command"}),this._trigger("visible-change",{visible:!1})},onPopupVisibleChange({detail:e}){e.visible||(this.triggerEvent("close",{trigger:"overlay"}),this._trigger("visible-change",{visible:!1})),this.autoClose&&(this.setData({visible:!1}),this.autoClose=!1)},onSwiperChange(e){const{current:t}=e.detail;this.setData({currentSwiperIndex:t})},onSelect(e){const{currentSwiperIndex:t,items:i,gridThemeItems:s,count:o,theme:r}=this.data,{index:n}=e.currentTarget.dataset,a=r===ActionSheetTheme.Grid,h=a?s[t][n]:i[n],c=a?n+t*o:n;h&&(this.triggerEvent("selected",{selected:h,index:c}),h.disabled||(this.triggerEvent("close",{trigger:"select"}),this._trigger("visible-change",{visible:!1})))},onCancel(){this.triggerEvent("cancel"),this.autoClose&&(this.setData({visible:!1}),this.autoClose=!1)}}}};ActionSheet.show=show,ActionSheet=__decorate([wxComponent()],ActionSheet);export default ActionSheet;

View File

@@ -0,0 +1 @@
{"component":true,"styleIsolation":"apply-shared","usingComponents":{"t-icon":"../icon/icon","t-popup":"../popup/popup","t-grid":"../grid/grid","t-grid-item":"../grid-item/grid-item"}}

View File

@@ -0,0 +1 @@
<wxs src="./action-sheet.wxs" module="_this"/><wxs src="../common/utils.wxs" module="_"/><import src="./template/list.wxml"/><import src="./template/grid.wxml"/><view id="{{classPrefix}}" style="{{_._style([style, customStyle])}}" class="{{classPrefix}} class {{prefix}}-class"><t-popup visible="{{visible}}" placement="bottom" usingCustomNavbar="{{usingCustomNavbar}}" bind:visible-change="onPopupVisibleChange" show-overlay="{{showOverlay}}" z-index="{{ popupProps.zIndex || defaultPopUpzIndex }}" overlay-props="{{ popupProps.overlayProps || defaultPopUpProps }}"><view class="{{_.cls(classPrefix + '__content', [['grid', gridThemeItems.length]])}} {{prefix}}-class-content" tabindex="0"><view wx:if="{{description}}" tabindex="0" class="{{_.cls(classPrefix + '__description', [align])}}">{{description}}</view><block wx:if="{{gridThemeItems.length}}"><template is="grid" data="{{classPrefix, prefix, gridThemeItems, count, currentSwiperIndex}}"/></block><view wx:elif="{{items && items.length}}" class="{{classPrefix}}__list"><block wx:for="{{ items }}" wx:key="index"><template is="list" data="{{index, classPrefix, listThemeItemClass: _.cls(classPrefix + '__list-item', [align, [disabled, item.disabled]]), item}}"/></block></view></view><slot/><view wx:if="{{showCancel}}" class="{{classPrefix}}__footer"><view class="{{classPrefix}}__gap-{{theme}}"/><view class="{{classPrefix}}__cancel {{prefix}}-class-cancel" hover-class="{{classPrefix}}__cancel--hover" hover-stay-time="70" bind:tap="onCancel" aria-role="button">{{ cancelText || '取消' }}</view></view></t-popup></view>

View File

@@ -0,0 +1,19 @@
var getListThemeItemClass = function (props) {
var classPrefix = props.classPrefix;
var item = props.item;
var prefix = props.prefix;
var classList = [classPrefix + '__list-item'];
if (item.disabled) {
classList.push(prefix + '-is-disabled');
}
return classList.join(' ');
};
var isImage = function (name) {
return name.indexOf('/') !== -1;
};
module.exports = {
getListThemeItemClass: getListThemeItemClass,
isImage: isImage,
};

View File

@@ -0,0 +1 @@
@import '../common/style/index.wxss';.t-action-sheet__content{color:var(--td-action-sheet-color,var(--td-text-color-primary,var(--td-font-gray-1,rgba(0,0,0,.9))));border-top-left-radius:var(--td-action-sheet-border-radius,var(--td-radius-extra-large,24rpx));border-top-right-radius:var(--td-action-sheet-border-radius,var(--td-radius-extra-large,24rpx));background-color:var(--td-bg-color-container,var(--td-font-white-1,#fff));overflow:hidden}.t-action-sheet__content--grid{padding-top:16rpx}.t-action-sheet__content:focus{outline:0}.t-action-sheet__grid{padding-bottom:16rpx}.t-action-sheet__grid--swiper{padding-bottom:48rpx}.t-action-sheet__description{color:var(--td-action-sheet-description-color,var(--td-text-color-placeholder,var(--td-font-gray-3,rgba(0,0,0,.4))));line-height:44rpx;font-size:28rpx;text-align:var(--td-action-sheet-text-align,center);padding:24rpx 32rpx;position:relative}.t-action-sheet__description:focus{outline:0}.t-action-sheet__description::after{content:'';display:block;position:absolute;top:unset;bottom:0;left:unset;right:unset;background-color:var(--td-action-sheet-border-color,var(--td-border-level-1-color,var(--td-gray-color-3,#e7e7e7)))}.t-action-sheet__description::after{height:1px;left:0;right:0;transform:scaleY(.5)}.t-action-sheet__description--left{text-align:left}.t-action-sheet__description--left::after{left:32rpx}.t-action-sheet__list-item{display:flex;align-items:center;justify-content:center;position:relative;height:var(--td-action-sheet-list-item-height,112rpx);padding:0 32rpx}.t-action-sheet__list-item::after{content:'';display:block;position:absolute;top:unset;bottom:0;left:unset;right:unset;background-color:var(--td-action-sheet-border-color,var(--td-border-level-1-color,var(--td-gray-color-3,#e7e7e7)))}.t-action-sheet__list-item::after{height:1px;left:0;right:0;transform:scaleY(.5)}.t-action-sheet__list-item:focus{outline:0}.t-action-sheet__list-item--left{justify-content:start}.t-action-sheet__list-item--left::after{left:32rpx}.t-action-sheet__list-item--disabled{color:var(--td-action-sheet-list-item-disabled-color,var(--td-text-color-disabled,var(--td-font-gray-4,rgba(0,0,0,.26))))}.t-action-sheet__list-item-text{font-size:var(--td-font-size-m,32rpx);word-wrap:normal;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.t-action-sheet__list-item-icon{margin-right:16rpx}.t-action-sheet__list-item-icon--suffix{margin-left:auto}.t-action-sheet__swiper-wrap{margin-top:8rpx;position:relative}.t-action-sheet__footer{background-color:var(--td-bg-color-container,var(--td-font-white-1,#fff))}.t-action-sheet__gap-list{height:16rpx;background-color:var(--td-action-sheet-gap-color,var(--td-bg-color-page,var(--td-gray-color-1,#f3f3f3)))}.t-action-sheet__gap-grid{height:1rpx;background-color:var(--td-action-sheet-border-color,var(--td-border-level-1-color,var(--td-gray-color-3,#e7e7e7)))}.t-action-sheet__cancel{display:flex;align-items:center;justify-content:center;color:var(--td-action-sheet-cancel-color,var(--td-text-color-primary,var(--td-font-gray-1,rgba(0,0,0,.9))));height:var(--td-action-sheet-cancel-height,96rpx)}.t-action-sheet__dots{position:absolute;left:50%;bottom:32rpx;transform:translateX(-50%);display:flex;flex-direction:row}.t-action-sheet__dots-item{width:16rpx;height:16rpx;background-color:#dcdcdc;border-radius:50%;margin:0 16rpx;transition:all .4s ease-in}.t-action-sheet__dots-item.t-is-active{background-color:#0052d9}

View File

@@ -0,0 +1,8 @@
/// <reference types="miniprogram-api-typings" />
import { ActionSheetItem, ActionSheetTheme, ActionSheetShowOption } from './show';
export { ActionSheetItem, ActionSheetTheme, ActionSheetShowOption };
declare const _default: {
show(options: ActionSheetShowOption): WechatMiniprogram.Component.TrivialInstance;
close(options: ActionSheetShowOption): void;
};
export default _default;

View File

@@ -0,0 +1 @@
import{show,close,ActionSheetTheme}from"./show";export{ActionSheetTheme};export default{show:e=>show(e),close:e=>close(e)};

View File

@@ -0,0 +1,3 @@
import { TdActionSheetProps } from './type';
declare const props: TdActionSheetProps;
export default props;

View File

@@ -0,0 +1 @@
const props={align:{type:String,value:"center"},cancelText:{type:String,value:""},count:{type:Number,value:8},description:{type:String,value:""},items:{type:Array,required:!0},popupProps:{type:Object,value:{}},showCancel:{type:Boolean,value:!0},showOverlay:{type:Boolean,value:!0},theme:{type:String,value:"list"},usingCustomNavbar:{type:Boolean,value:!1},visible:{type:Boolean,value:null},defaultVisible:{type:Boolean,value:!1}};export default props;

View File

@@ -0,0 +1,26 @@
/// <reference types="miniprogram-api-typings" />
/// <reference types="miniprogram-api-typings" />
import { ActionSheetItem } from './type';
export { ActionSheetItem };
declare type Context = WechatMiniprogram.Page.TrivialInstance | WechatMiniprogram.Component.TrivialInstance;
export declare enum ActionSheetTheme {
List = "list",
Grid = "grid"
}
interface ActionSheetProps {
align: 'center' | 'left';
cancelText?: string;
count?: number;
description: string;
items: Array<string | ActionSheetItem>;
showCancel?: boolean;
theme?: ActionSheetTheme;
visible: boolean;
defaultVisible?: boolean;
}
export interface ActionSheetShowOption extends Omit<ActionSheetProps, 'visible'> {
context?: Context;
selector?: string;
}
export declare const show: (options: ActionSheetShowOption) => WechatMiniprogram.Component.TrivialInstance;
export declare const close: (options: ActionSheetShowOption) => void;

View File

@@ -0,0 +1 @@
import{__rest}from"tslib";import{getInstance}from"../common/utils";export var ActionSheetTheme;!function(t){t.List="list",t.Grid="grid"}(ActionSheetTheme||(ActionSheetTheme={}));export const show=function(t){const e=Object.assign({},t),{context:o,selector:n="#t-action-sheet"}=e,c=__rest(e,["context","selector"]),s=getInstance(o,n);if(s)return s.show(Object.assign({},c)),s;console.error("未找到组件,请确认 selector && context 是否正确")};export const close=function(t){const{context:e,selector:o="#t-action-sheet"}=Object.assign({},t),n=getInstance(e,o);n&&n.close()};

View File

@@ -0,0 +1 @@
<template name="grid"><block wx:if="{{gridThemeItems.length === 1}}"><t-grid align="center" t-class="{{classPrefix}}__grid" column="{{count / 2}}" class="{{classPrefix}}__single-wrap"><t-grid-item t-class="{{classPrefix}}__grid-item" class="{{classPrefix}}__square" wx:for="{{gridThemeItems[0]}}" wx:key="index" bind:tap="onSelect" data-index="{{index}}" icon="{{ { name: item.icon, color: item.color } }}" text="{{item.label || ''}}" image="{{item.image || ''}}" style="--td-grid-item-text-color: {{item.color}}"></t-grid-item></t-grid></block><block wx:elif="{{gridThemeItems.length > 1}}"><view class="{{classPrefix}}__swiper-wrap"><swiper style="height: 456rpx" autoplay="{{false}}" current="{{currentSwiperIndex}}" bindchange="onSwiperChange"><swiper-item wx:for="{{gridThemeItems}}" wx:key="index"><t-grid align="center" t-class="{{classPrefix}}__grid {{classPrefix}}__grid--swiper" column="{{count / 2}}"><t-grid-item t-class="{{classPrefix}}__grid-item" class="{{classPrefix}}__square" wx:for="{{item}}" wx:key="index" data-index="{{index}}" bind:tap="onSelect" icon="{{ { name: item.icon, color: item.color } }}" text="{{item.label || ''}}" image="{{item.image || ''}}" style="--td-grid-item-text-color: {{item.color}}"></t-grid-item></t-grid></swiper-item></swiper><view class="{{classPrefix}}__nav"><view class="{{classPrefix}}__dots"><view wx:for="{{gridThemeItems.length}}" wx:key="index" class="{{classPrefix}}__dots-item {{index === currentSwiperIndex ? prefix + '-is-active' : ''}}"/></view></view></view></block></template>

View File

@@ -0,0 +1 @@
<template name="list"><view data-index="{{index}}" style="{{ item.color ? 'color: ' + item.color : '' }}" class="{{listThemeItemClass}}" bind:tap="onSelect" aria-role="{{ariaRole || 'button'}}" aria-label="{{item.label || item}}" tabindex="0"><t-icon wx:if="{{item.icon}}" name="{{item.icon}}" class="{{classPrefix}}__list-item-icon" size="48rpx"></t-icon><view class="{{classPrefix}}__list-item-text">{{item.label || item}}</view><t-icon wx:if="{{item.suffixIcon}}" name="{{item.suffixIcon}}" class="{{classPrefix}}__list-item-icon {{classPrefix}}__list-item-icon--suffix" size="48rpx"></t-icon></view></template>

View File

@@ -0,0 +1,59 @@
import { PopupProps } from '../popup/index';
export interface TdActionSheetProps {
align?: {
type: StringConstructor;
value?: 'center' | 'left';
};
cancelText?: {
type: StringConstructor;
value?: string;
};
count?: {
type: NumberConstructor;
value?: number;
};
description?: {
type: StringConstructor;
value?: string;
};
items: {
type: ArrayConstructor;
value?: Array<string | ActionSheetItem>;
required?: boolean;
};
popupProps?: {
type: ObjectConstructor;
value?: PopupProps;
};
showCancel?: {
type: BooleanConstructor;
value?: boolean;
};
showOverlay?: {
type: BooleanConstructor;
value?: boolean;
};
theme?: {
type: StringConstructor;
value?: 'list' | 'grid';
};
usingCustomNavbar?: {
type: BooleanConstructor;
value?: boolean;
};
visible?: {
type: BooleanConstructor;
value?: boolean;
};
defaultVisible?: {
type: BooleanConstructor;
value?: boolean;
};
}
export interface ActionSheetItem {
label: string;
color?: string;
disabled?: boolean;
icon?: string;
suffixIcon?: string;
}

View File

@@ -0,0 +1 @@
export{};

View File

@@ -0,0 +1,28 @@
import { SuperComponent, RelationsOptions } from '../common/src/index';
export default class AvatarGroup extends SuperComponent {
externalClasses: string[];
properties: import("./type").TdAvatarGroupProps;
data: {
prefix: string;
classPrefix: string;
hasChild: boolean;
length: number;
className: string;
};
options: {
multipleSlots: boolean;
};
relations: RelationsOptions;
lifetimes: {
attached(): void;
ready(): void;
};
observers: {
'cascading, size'(): void;
};
methods: {
setClass(): void;
handleMax(): void;
onCollapsedItemClick(e: WechatMiniprogram.CustomEvent): void;
};
}

View File

@@ -0,0 +1 @@
import{__decorate}from"tslib";import{SuperComponent,wxComponent}from"../common/src/index";import config from"../common/config";import avatarGroupProps from"./props";const{prefix:prefix}=config,name=`${prefix}-avatar-group`;let AvatarGroup=class extends SuperComponent{constructor(){super(...arguments),this.externalClasses=[`${prefix}-class`,`${prefix}-class-content`,`${prefix}-class-image`],this.properties=avatarGroupProps,this.data={prefix:prefix,classPrefix:name,hasChild:!0,length:0,className:""},this.options={multipleSlots:!0},this.relations={"../avatar/avatar":{type:"descendant"}},this.lifetimes={attached(){this.setClass()},ready(){this.setData({length:this.$children.length}),this.handleMax()}},this.observers={"cascading, size"(){this.setClass()}},this.methods={setClass(){const{cascading:e,size:t}=this.properties,s=e.split("-")[0],a=[name,`${prefix}-class`,`${name}-offset-${s}`,`${name}-offset-${s}-${t.indexOf("px")>-1?"medium":t||"medium"}`];this.setData({className:a.join(" ")})},handleMax(){const{max:e}=this.data,t=this.$children.length;if(!e||e>t)return;this.$children.splice(e,t-e).forEach((e=>{e.hide()}))},onCollapsedItemClick(e){this.triggerEvent("collapsed-item-click",e.detail)}}}};AvatarGroup=__decorate([wxComponent()],AvatarGroup);export default AvatarGroup;

View File

@@ -0,0 +1 @@
{"component":true,"styleIsolation":"shared","usingComponents":{"t-avatar":"../avatar/avatar"}}

View File

@@ -0,0 +1 @@
<wxs src="../common/utils.wxs" module="_"/><view style="{{_._style([style, customStyle])}}" class="{{className}} class"><slot/><view class="{{classPrefix}}__collapse--slot"><slot name="collapse-avatar"/></view><view class="{{classPrefix}}__collapse--default" wx:if="{{max && (max < length)}}" bindtap="onCollapsedItemClick"><t-avatar t-class-image="{{prefix}}-avatar--border {{prefix}}-avatar--border-{{size}} {{prefix}}-class-image" t-class-content="{{prefix}}-class-content" size="{{size}}" shape="{{shape}}" icon="{{ collapseAvatar ? '' : 'user-add'}}" aria-role="none">{{collapseAvatar}}</t-avatar></view></view>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import { TdAvatarGroupProps } from './type';
declare const props: TdAvatarGroupProps;
export default props;

View File

@@ -0,0 +1 @@
const props={cascading:{type:String,value:"left-up"},collapseAvatar:{type:String},max:{type:Number},shape:{type:String},size:{type:String,value:""}};export default props;

View File

@@ -0,0 +1,24 @@
import { ShapeEnum } from '../common/common';
export interface TdAvatarGroupProps {
cascading?: {
type: StringConstructor;
value?: CascadingValue;
};
collapseAvatar?: {
type: StringConstructor;
value?: string;
};
max?: {
type: NumberConstructor;
value?: number;
};
shape?: {
type: StringConstructor;
value?: ShapeEnum;
};
size?: {
type: StringConstructor;
value?: string;
};
}
export declare type CascadingValue = 'left-up' | 'right-up';

View File

@@ -0,0 +1 @@
export{};

View File

@@ -0,0 +1,22 @@
/// <reference types="miniprogram-api-typings" />
import { SuperComponent, RelationsOptions } from '../common/src/index';
export default class Avatar extends SuperComponent {
options: WechatMiniprogram.Component.ComponentOptions;
externalClasses: string[];
properties: import("./type").TdAvatarProps;
data: {
prefix: string;
classPrefix: string;
isShow: boolean;
zIndex: number;
systemInfo: WechatMiniprogram.WindowInfo | WechatMiniprogram.SystemInfo;
};
relations: RelationsOptions;
observers: {
icon(icon: any): void;
};
methods: {
hide(): void;
onLoadError(e: WechatMiniprogram.CustomEvent): void;
};
}

View File

@@ -0,0 +1 @@
import{__decorate}from"tslib";import{SuperComponent,wxComponent}from"../common/src/index";import config from"../common/config";import avatarProps from"./props";import{setIcon,systemInfo}from"../common/utils";const{prefix:prefix}=config,name=`${prefix}-avatar`;let Avatar=class extends SuperComponent{constructor(){super(...arguments),this.options={multipleSlots:!0},this.externalClasses=[`${prefix}-class`,`${prefix}-class-image`,`${prefix}-class-icon`,`${prefix}-class-alt`,`${prefix}-class-content`],this.properties=avatarProps,this.data={prefix:prefix,classPrefix:name,isShow:!0,zIndex:0,systemInfo:systemInfo},this.relations={"../avatar-group/avatar-group":{type:"ancestor",linked(t){this.parent=t,this.setData({shape:this.data.shape||t.data.shape||"circle",size:this.data.size||t.data.size,bordered:!0})}}},this.observers={icon(t){const s=setIcon("icon",t,"");this.setData(Object.assign({},s))}},this.methods={hide(){this.setData({isShow:!1})},onLoadError(t){this.properties.hideOnLoadFailed&&this.setData({isShow:!1}),this.triggerEvent("error",t.detail)}}}};Avatar=__decorate([wxComponent()],Avatar);export default Avatar;

View File

@@ -0,0 +1 @@
{"component":true,"styleIsolation":"shared","usingComponents":{"t-icon":"../icon/icon","t-badge":"../badge/badge","t-image":"../image/image"}}

View File

@@ -0,0 +1 @@
<import src="../common/template/icon.wxml"/><wxs src="../common/utils.wxs" module="_"/><wxs src="./avatar.wxs" module="_this"/><view class="{{classPrefix}}__wrapper class {{prefix}}-class" style="{{_._style([_this.getStyles(isShow), style, customStyle])}}"><t-badge color="{{badgeProps.color || ''}}" content="{{badgeProps.content || ''}}" count="{{badgeProps.count || 0}}" dot="{{badgeProps.dot || false}}" max-count="{{badgeProps.maxCount || 99}}" offset="{{badgeProps.offset || []}}" shape="{{badgeProps.shape || 'circle'}}" show-zero="{{badgeProps.showZero || false}}" size="{{badgeProps.size || 'medium'}}" t-class="{{badgeProps.tClass}}" t-class-content="{{badgeProps.tClassContent}}" t-class-count="{{badgeProps.tClassCount}}"><view class="{{_this.getClass(classPrefix, size || 'medium', shape, bordered)}} {{prefix}}-class-image" style="{{_this.getSize(size, systemInfo)}}" aria-label="{{ ariaLabel || alt ||'头像'}}" aria-role="{{ ariaRole || 'img'}}" aria-hidden="{{ ariaHidden }}"><t-image wx:if="{{image}}" t-class="{{prefix}}-image {{classPrefix}}__image" t-class-load="{{prefix}}-class-alt" style="{{imageProps && imageProps.style || ''}}" src="{{image}}" mode="{{imageProps && imageProps.mode || 'aspectFill'}}" lazy="{{imageProps && imageProps.lazy || false}}" loading="{{imageProps && imageProps.loading || 'default'}}" shape="{{imageProps && imageProps.shape || 'round'}}" webp="{{imageProps && imageProps.webp || false}}" error="{{alt || 'default'}}" bind:error="onLoadError"/><template wx:elif="{{iconName || _.isNoEmptyObj(iconData)}}" is="icon" data="{{tClass: classPrefix + '__icon ' + prefix + '-class-icon', name: iconName, ...iconData}}"/><view wx:else class="{{classPrefix}}__text {{prefix}}-class-content"><slot/></view></view></t-badge></view>

View File

@@ -0,0 +1,30 @@
module.exports = {
getClass: function (classPrefix, size, shape, bordered) {
var hasPx = (size || '').indexOf('px') > -1;
var borderSize = hasPx ? 'medium' : size;
var classNames = [
classPrefix,
classPrefix + (shape === 'round' ? '--round' : '--circle'),
bordered ? classPrefix + '--border' + ' ' + classPrefix + '--border-' + borderSize : '',
hasPx ? '' : classPrefix + '--' + size,
];
return classNames.join(' ');
},
getSize: function (size = 'medium', systemInfo) {
var res = getRegExp('^([0-9]+)(px|rpx)$').exec(size);
if (res && res.length >= 3) {
var px = res[1];
if (res[2] === 'rpx') {
px = Math.floor((systemInfo.windowWidth * res[1]) / 750);
}
return 'width:' + size + ';height:' + size + ';font-size:' + ((px / 8) * 3 + 2) + 'px';
}
},
getStyles: function (isShow) {
return isShow ? '' : 'display: none;';
},
};

View File

@@ -0,0 +1 @@
@import '../common/style/index.wxss';.t-avatar{display:flex;align-items:center;justify-content:center;box-sizing:border-box;background-color:var(--td-avatar-bg-color,var(--td-brand-color-light-active,var(--td-primary-color-2,#d9e1ff)));color:var(--td-avatar-content-color,var(--td-brand-color,var(--td-primary-color-7,#0052d9)))}.t-avatar__wrapper{display:inline-flex;position:relative;vertical-align:top;margin-left:var(--td-avatar-margin-left,0)}.t-avatar--large{width:var(--td-avatar-large-width,128rpx);height:var(--td-avatar-large-width,128rpx);font-size:var(--td-avatar-text-large-font-size,var(--td-font-size-xl,40rpx))}.t-avatar--large .t-avatar__icon{font-size:var(--td-avatar-icon-large-font-size,64rpx)}.t-avatar--medium{width:var(--td-avatar-medium-width,96rpx);height:var(--td-avatar-medium-width,96rpx);font-size:var(--td-avatar-text-medium-font-size,var(--td-font-size-m,32rpx))}.t-avatar--medium .t-avatar__icon{font-size:var(--td-avatar-icon-medium-font-size,48rpx)}.t-avatar--small{width:var(--td-avatar-small-width,80rpx);height:var(--td-avatar-small-width,80rpx);font-size:var(--td-avatar-text-small-font-size,var(--td-font-size-base,28rpx))}.t-avatar--small .t-avatar__icon{font-size:var(--td-avatar-icon-small-font-size,40rpx)}.t-avatar .t-image,.t-avatar__image{width:100%;height:100%}.t-avatar--circle{border-radius:var(--td-avatar-circle-border-radius,var(--td-radius-circle,50%));overflow:hidden}.t-avatar--round{border-radius:var(--td-avatar-round-border-radius,var(--td-radius-default,12rpx));overflow:hidden}.t-avatar__icon,.t-avatar__text{width:100%;height:100%;display:flex;align-items:center;justify-content:center}.t-avatar__icon:empty,.t-avatar__text:empty{width:0;height:0}.t-avatar--border{border-color:var(--td-avatar-border-color,#fff);border-style:solid}.t-avatar--border-small{border-width:var(--td-avatar-border-width-small,2rpx)}.t-avatar--border-medium{border-width:var(--td-avatar-border-width-medium,4rpx)}.t-avatar--border-large{border-width:var(--td-avatar-border-width-large,6rpx)}

View File

@@ -0,0 +1,3 @@
import { TdAvatarProps } from './type';
declare const props: TdAvatarProps;
export default props;

View File

@@ -0,0 +1 @@
const props={alt:{type:String,value:""},badgeProps:{type:Object},bordered:{type:Boolean,value:!1},hideOnLoadFailed:{type:Boolean,value:!1},icon:{type:null},image:{type:String,value:""},imageProps:{type:Object},shape:{type:String},size:{type:String,value:""}};export default props;

View File

@@ -0,0 +1,41 @@
import { BadgeProps } from '../badge/index';
import { ImageProps } from '../image/index';
import { ShapeEnum } from '../common/common';
export interface TdAvatarProps {
alt?: {
type: StringConstructor;
value?: string;
};
badgeProps?: {
type: ObjectConstructor;
value?: BadgeProps;
};
bordered?: {
type: BooleanConstructor;
value?: boolean;
};
hideOnLoadFailed?: {
type: BooleanConstructor;
value?: boolean;
};
icon?: {
type: null;
value?: string | object;
};
image?: {
type: StringConstructor;
value?: string;
};
imageProps?: {
type: ObjectConstructor;
value?: ImageProps;
};
shape?: {
type: StringConstructor;
value?: ShapeEnum;
};
size?: {
type: StringConstructor;
value?: string;
};
}

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