From 82dbb842bad30d0d0c699ae0308a40231281ed13 Mon Sep 17 00:00:00 2001 From: limqhz Date: Fri, 11 Nov 2022 21:51:57 +0800 Subject: [PATCH] project init --- app.js | 4 +- app.json | 8 +- components/behaviors/dom.d.ts | 2 + components/behaviors/dom.js | 27 +++ components/behaviors/touch.d.ts | 2 + components/behaviors/touch.js | 35 ++++ components/cell/README.md | 57 ++++++ components/cell/cell.d.ts | 14 ++ components/cell/cell.js | 51 ++++++ components/cell/cell.json | 6 + components/cell/cell.wxml | 40 +++++ components/cell/cell.wxss | 121 +++++++++++++ components/cell/props.d.ts | 3 + components/cell/props.js | 51 ++++++ components/cell/type.d.ts | 72 ++++++++ components/cell/type.js | 1 + components/mixins/page-scroll.d.ts | 7 + components/mixins/page-scroll.js | 50 ++++++ components/mixins/transition.d.ts | 1 + components/mixins/transition.js | 123 +++++++++++++ components/sticky/README.md | 56 ++++++ components/sticky/index.d.ts | 3 + components/sticky/index.js | 3 + components/sticky/props.d.ts | 3 + components/sticky/props.js | 21 +++ components/sticky/sticky.d.ts | 30 ++++ components/sticky/sticky.js | 118 +++++++++++++ components/sticky/sticky.json | 4 + components/sticky/sticky.wxml | 5 + components/sticky/sticky.wxss | 33 ++++ components/sticky/type.d.ts | 27 +++ components/sticky/type.js | 2 + components/swipe-cell/README.md | 46 +++++ components/swipe-cell/props.d.ts | 3 + components/swipe-cell/props.js | 17 ++ components/swipe-cell/swipe-cell.d.ts | 23 +++ components/swipe-cell/swipe-cell.js | 77 ++++++++ components/swipe-cell/swipe-cell.json | 3 + components/swipe-cell/swipe-cell.wxml | 46 +++++ components/swipe-cell/swipe-cell.wxs | 176 +++++++++++++++++++ components/swipe-cell/swipe-cell.wxss | 45 +++++ components/swipe-cell/type.d.ts | 30 ++++ components/swipe-cell/type.js | 3 + components/tabs/README.en-US.md | 34 ++++ components/tabs/README.md | 141 +++++++++++++++ components/tabs/props.d.ts | 3 + components/tabs/props.js | 35 ++++ components/tabs/tab-panel-props.d.ts | 3 + components/tabs/tab-panel-props.js | 21 +++ components/tabs/tab-panel.d.ts | 17 ++ components/tabs/tab-panel.js | 54 ++++++ components/tabs/tab-panel.json | 4 + components/tabs/tab-panel.wxml | 9 + components/tabs/tab-panel.wxss | 37 ++++ components/tabs/tabs.d.ts | 49 ++++++ components/tabs/tabs.js | 241 ++++++++++++++++++++++++++ components/tabs/tabs.json | 6 + components/tabs/tabs.wxml | 53 ++++++ components/tabs/tabs.wxs | 24 +++ components/tabs/tabs.wxss | 175 +++++++++++++++++++ components/tabs/type.d.ts | 65 +++++++ components/tabs/type.js | 1 + pages/foot-tab/foot-tab.json | 2 - pages/today/index.js | 63 +++---- pages/today/index.wxml | 39 ++++- pages/today/index.wxss | 53 +++++- project.private.config.json | 3 +- utils/api.js | 233 +++++++++++++++++++++++++ 68 files changed, 2771 insertions(+), 43 deletions(-) create mode 100644 components/behaviors/dom.d.ts create mode 100644 components/behaviors/dom.js create mode 100644 components/behaviors/touch.d.ts create mode 100644 components/behaviors/touch.js create mode 100644 components/cell/README.md create mode 100644 components/cell/cell.d.ts create mode 100644 components/cell/cell.js create mode 100644 components/cell/cell.json create mode 100644 components/cell/cell.wxml create mode 100644 components/cell/cell.wxss create mode 100644 components/cell/props.d.ts create mode 100644 components/cell/props.js create mode 100644 components/cell/type.d.ts create mode 100644 components/cell/type.js create mode 100644 components/mixins/page-scroll.d.ts create mode 100644 components/mixins/page-scroll.js create mode 100644 components/mixins/transition.d.ts create mode 100644 components/mixins/transition.js create mode 100644 components/sticky/README.md create mode 100644 components/sticky/index.d.ts create mode 100644 components/sticky/index.js create mode 100644 components/sticky/props.d.ts create mode 100644 components/sticky/props.js create mode 100644 components/sticky/sticky.d.ts create mode 100644 components/sticky/sticky.js create mode 100644 components/sticky/sticky.json create mode 100644 components/sticky/sticky.wxml create mode 100644 components/sticky/sticky.wxss create mode 100644 components/sticky/type.d.ts create mode 100644 components/sticky/type.js create mode 100644 components/swipe-cell/README.md create mode 100644 components/swipe-cell/props.d.ts create mode 100644 components/swipe-cell/props.js create mode 100644 components/swipe-cell/swipe-cell.d.ts create mode 100644 components/swipe-cell/swipe-cell.js create mode 100644 components/swipe-cell/swipe-cell.json create mode 100644 components/swipe-cell/swipe-cell.wxml create mode 100644 components/swipe-cell/swipe-cell.wxs create mode 100644 components/swipe-cell/swipe-cell.wxss create mode 100644 components/swipe-cell/type.d.ts create mode 100644 components/swipe-cell/type.js create mode 100644 components/tabs/README.en-US.md create mode 100644 components/tabs/README.md create mode 100644 components/tabs/props.d.ts create mode 100644 components/tabs/props.js create mode 100644 components/tabs/tab-panel-props.d.ts create mode 100644 components/tabs/tab-panel-props.js create mode 100644 components/tabs/tab-panel.d.ts create mode 100644 components/tabs/tab-panel.js create mode 100644 components/tabs/tab-panel.json create mode 100644 components/tabs/tab-panel.wxml create mode 100644 components/tabs/tab-panel.wxss create mode 100644 components/tabs/tabs.d.ts create mode 100644 components/tabs/tabs.js create mode 100644 components/tabs/tabs.json create mode 100644 components/tabs/tabs.wxml create mode 100644 components/tabs/tabs.wxs create mode 100644 components/tabs/tabs.wxss create mode 100644 components/tabs/type.d.ts create mode 100644 components/tabs/type.js create mode 100644 utils/api.js diff --git a/app.js b/app.js index 1ed57c4..dc53c84 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,5 @@ // app.js +import api from './utils/api.js'; App({ onLaunch() { // 展示本地存储能力 @@ -15,5 +16,6 @@ App({ }, globalData: { userInfo: null - } + }, + $api: api, }) diff --git a/app.json b/app.json index f289833..e13ed22 100644 --- a/app.json +++ b/app.json @@ -8,7 +8,11 @@ ], "usingComponents": { "t-tab-bar": "/components/tab-bar/tab-bar", - "t-tab-bar-item": "/components/tab-bar/tab-bar-item" + "t-tab-bar-item": "/components/tab-bar/tab-bar-item", + "t-cell": "/components/cell/cell", + "t-swipe-cell": "/components/swipe-cell/swipe-cell", + "t-tabs": "/components/tabs/tabs", + "t-tab-panel": "/components/tabs/tab-panel" }, "window": { "backgroundTextStyle": "light", @@ -18,4 +22,4 @@ }, "style": "v2", "sitemapLocation": "sitemap.json" -} \ No newline at end of file +} diff --git a/components/behaviors/dom.d.ts b/components/behaviors/dom.d.ts new file mode 100644 index 0000000..ae80eca --- /dev/null +++ b/components/behaviors/dom.d.ts @@ -0,0 +1,2 @@ +declare const _default: string; +export default _default; diff --git a/components/behaviors/dom.js b/components/behaviors/dom.js new file mode 100644 index 0000000..4aa85ff --- /dev/null +++ b/components/behaviors/dom.js @@ -0,0 +1,27 @@ +export default Behavior({ + methods: { + gettingBoundingClientRect(selector, all) { + return new Promise((resolve, reject) => { + try { + wx.createSelectorQuery() + .in(this)[all ? 'selectAll' : 'select'](selector) + .boundingClientRect((rect) => { + if (all && Array.isArray(rect) && rect.length) { + resolve(rect); + } + else if (!all && rect) { + resolve(rect); + } + else { + reject(); + } + }) + .exec(); + } + catch (err) { + reject(err); + } + }); + }, + }, +}); diff --git a/components/behaviors/touch.d.ts b/components/behaviors/touch.d.ts new file mode 100644 index 0000000..ae80eca --- /dev/null +++ b/components/behaviors/touch.d.ts @@ -0,0 +1,2 @@ +declare const _default: string; +export default _default; diff --git a/components/behaviors/touch.js b/components/behaviors/touch.js new file mode 100644 index 0000000..cbabccc --- /dev/null +++ b/components/behaviors/touch.js @@ -0,0 +1,35 @@ +const MinDistance = 10; +const getDirection = (x, y) => { + if (x > y && x > MinDistance) { + return 'horizontal'; + } + if (y > x && y > MinDistance) { + return 'vertical'; + } + return ''; +}; +export default Behavior({ + methods: { + resetTouchStatus() { + this.direction = ''; + this.deltaX = 0; + this.deltaY = 0; + this.offsetX = 0; + this.offsetY = 0; + }, + touchStart(event) { + this.resetTouchStatus(); + const [touch] = event.touches; + this.startX = touch.clientX; + this.startY = touch.clientY; + }, + touchMove(event) { + const [touch] = event.touches; + this.deltaX = touch.clientX - this.startX; + this.deltaY = touch.clientY - this.startY; + this.offsetX = Math.abs(this.deltaX); + this.offsetY = Math.abs(this.deltaY); + this.direction = getDirection(this.offsetX, this.offsetY); + }, + }, +}); diff --git a/components/cell/README.md b/components/cell/README.md new file mode 100644 index 0000000..4bdc541 --- /dev/null +++ b/components/cell/README.md @@ -0,0 +1,57 @@ +--- +title: Cell 单元格 +description: 用于各个类别行的信息展示。 +spline: data +isComponent: true +--- + + +## 引入 + +全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +"usingComponents": { + "t-cell": "tdesign-miniprogram/cell/cell" +} +``` + +## 代码演示 + +### 单行单元格 + + + +{{ base }} + +### 多行单元格 + + + +{{ multiple }} + +## API +### Cell Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +align | String | middle | 内容的对齐方式,默认居中对齐。可选项:top/middle/bottom | N +arrow | Boolean | false | 是否显示右侧箭头 | N +bordered | Boolean | true | 是否显示下边框 | N +description | String / Slot | - | 下方内容描述 | N +external-classes | Array | - | 组件类名,分别用于设置 组件外层类名、标题类名、下方描述内容类名、右侧说明文字类名、激活态类名、图片类名、左侧内容、左侧图标类名、右侧内容、右侧图标类名 等。`['t-class', 't-class-title', 't-class-description', 't-class-note', 't-class-hover', 't-class-image', 't-class-left', 't-class-left-icon', 't-class-right', 't-class-right-icon']` | N +hover | Boolean | - | 是否开启点击反馈 | N +image | String / Slot | - | 主图 | N +jump-type | String | navigateTo | 链接跳转类型。可选项:switchTab/reLaunch/redirectTo/navigateTo | N +left-icon | String / Slot | - | 左侧图标,出现在单元格标题的左侧 | N +note | String / Slot | - | 和标题同行的说明文字 | N +required | Boolean | false | 是否显示表单必填星号 | N +right-icon | String / Slot | - | 最右侧图标 | N +title | String / Slot | - | 标题 | N +url | String | - | 点击后跳转链接地址。如果值为空,则表示不需要跳转 | N + +### Cell Events + +名称 | 参数 | 描述 +-- | -- | -- +click | - | 右侧内容 diff --git a/components/cell/cell.d.ts b/components/cell/cell.d.ts new file mode 100644 index 0000000..92eb1c9 --- /dev/null +++ b/components/cell/cell.d.ts @@ -0,0 +1,14 @@ +import { SuperComponent } from '../common/src/index'; +export default class Cell extends SuperComponent { + externalClasses: string[]; + options: { + multipleSlots: boolean; + }; + properties: import("./type").TdCellProps; + data: { + prefix: string; + classPrefix: string; + }; + onClick(e: any): void; + jumpLink(urlKey?: string, link?: string): void; +} diff --git a/components/cell/cell.js b/components/cell/cell.js new file mode 100644 index 0000000..7950416 --- /dev/null +++ b/components/cell/cell.js @@ -0,0 +1,51 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +import { SuperComponent, wxComponent } from '../common/src/index'; +import config from '../common/config'; +import props from './props'; +const { prefix } = config; +const name = `${prefix}-cell`; +let Cell = class Cell extends SuperComponent { + constructor() { + super(...arguments); + this.externalClasses = [ + `${prefix}-class`, + `${prefix}-class-title`, + `${prefix}-class-description`, + `${prefix}-class-note`, + `${prefix}-class-hover`, + `${prefix}-class-image`, + `${prefix}-class-left`, + `${prefix}-class-left-icon`, + `${prefix}-class-right`, + `${prefix}-class-right-icon`, + ]; + this.options = { + multipleSlots: true, + }; + this.properties = props; + this.data = { + prefix, + classPrefix: name, + }; + } + onClick(e) { + this.triggerEvent('click', e.detail); + this.jumpLink(); + } + jumpLink(urlKey = 'url', link = 'jumpType') { + const url = this.data[urlKey]; + const jumpType = this.data[link]; + if (url) { + wx[jumpType]({ url }); + } + } +}; +Cell = __decorate([ + wxComponent() +], Cell); +export default Cell; diff --git a/components/cell/cell.json b/components/cell/cell.json new file mode 100644 index 0000000..049940c --- /dev/null +++ b/components/cell/cell.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-icon": "../icon/icon" + } +} diff --git a/components/cell/cell.wxml b/components/cell/cell.wxml new file mode 100644 index 0000000..c3ba655 --- /dev/null +++ b/components/cell/cell.wxml @@ -0,0 +1,40 @@ + + + + + + + + + + {{ title}} + + +  * + + + + + {{description}} + + + + + + {{note}} + + + + + + + + + + + diff --git a/components/cell/cell.wxss b/components/cell/cell.wxss new file mode 100644 index 0000000..fa30bf4 --- /dev/null +++ b/components/cell/cell.wxss @@ -0,0 +1,121 @@ +.t-float-left { + float: left; +} +.t-float-right { + float: right; +} +@keyframes tdesign-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.hotspot-expanded.relative { + position: relative; +} +.hotspot-expanded::after { + content: ''; + display: block; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + transform: scale(1.5); +} +.t-cell { + position: relative; + display: flex; + box-sizing: border-box; + width: 100%; + padding: 24rpx 32rpx; + font-size: 32rpx; + line-height: 48rpx; + color: rgba(0, 0, 0, 0.9); + background-color: #ffffff; +} +.t-cell::after { + position: absolute; + box-sizing: border-box; + content: ' '; + pointer-events: none; + right: 0; + left: 0; + bottom: 0; + border-bottom: 1px solid #e7e7e7; + transform: scaleY(0.5); + left: 32rpx; +} +.t-cell--borderless::after { + display: none; +} +.t-cell__description { + font-size: 28rpx; + line-height: 44rpx; + color: rgba(0, 0, 0, 0.4); +} +.t-cell__description-text { + margin-top: 8rpx; +} +.t-cell__note { + display: flex; + align-items: center; + justify-content: flex-end; + overflow: hidden; + color: rgba(0, 0, 0, 0.4); +} +.t-cell__title, +.t-cell__note { + flex: 1 1 auto; +} +.t-cell__title:empty, +.t-cell__note:empty { + display: none; +} +.t-cell__title-text { + display: flex; +} +.t-cell__left, +.t-cell__right { + display: flex; + align-items: center; + font-size: 48rpx; + line-height: 48rpx; +} +.t-cell__left :not(:empty) { + margin-right: 16rpx; +} +.t-cell__left-icon { + font-size: 48rpx; +} +.t-cell__left-image { + height: 112rpx; + width: 112rpx; +} +.t-cell__right { + margin-left: 8rpx; + color: #bbb; +} +.t-cell__right-icon { + color: #bbb; + font-size: 48rpx; + line-height: 48rpx; +} +.t-cell--hover.t-cell--hover-class { + background-color: #f2f3f5; +} +.t-cell--required { + font-size: 32rpx; + color: #e34d59; +} +.t-cell--middle { + align-items: center; +} +.t-cell--top { + align-items: flex-start; +} +.t-cell--bottom { + align-items: flex-end; +} diff --git a/components/cell/props.d.ts b/components/cell/props.d.ts new file mode 100644 index 0000000..ad657e5 --- /dev/null +++ b/components/cell/props.d.ts @@ -0,0 +1,3 @@ +import { TdCellProps } from './type'; +declare const props: TdCellProps; +export default props; diff --git a/components/cell/props.js b/components/cell/props.js new file mode 100644 index 0000000..8e741e8 --- /dev/null +++ b/components/cell/props.js @@ -0,0 +1,51 @@ +const props = { + align: { + type: String, + value: 'middle', + }, + arrow: { + type: Boolean, + value: false, + }, + bordered: { + type: Boolean, + value: true, + }, + description: { + type: String, + }, + externalClasses: { + type: Array, + }, + hover: { + type: Boolean, + }, + image: { + type: String, + }, + jumpType: { + type: String, + value: 'navigateTo', + }, + leftIcon: { + type: String, + }, + note: { + type: String, + }, + required: { + type: Boolean, + value: false, + }, + rightIcon: { + type: String, + }, + title: { + type: String, + }, + url: { + type: String, + value: '', + }, +}; +export default props; diff --git a/components/cell/type.d.ts b/components/cell/type.d.ts new file mode 100644 index 0000000..00d874b --- /dev/null +++ b/components/cell/type.d.ts @@ -0,0 +1,72 @@ +export interface TdCellProps { + align?: { + type: StringConstructor; + value?: 'top' | 'middle' | 'bottom'; + required?: boolean; + }; + arrow?: { + type: BooleanConstructor; + value?: boolean; + required?: boolean; + }; + bordered?: { + type: BooleanConstructor; + value?: boolean; + required?: boolean; + }; + description?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + externalClasses?: { + type: ArrayConstructor; + value?: ['t-class', 't-class-title', 't-class-note', 't-class-description', 't-class-thumb', 't-class-hover', 't-class-left', 't-class-right']; + required?: boolean; + }; + hover?: { + type: BooleanConstructor; + value?: boolean; + required?: boolean; + }; + image?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + jumpType?: { + type: StringConstructor; + value?: 'switchTab' | 'reLaunch' | 'redirectTo' | 'navigateTo'; + required?: boolean; + }; + leftIcon?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + note?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + required?: { + type: BooleanConstructor; + value?: boolean; + required?: boolean; + }; + rightIcon?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + title?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; + url?: { + type: StringConstructor; + value?: string; + required?: boolean; + }; +} diff --git a/components/cell/type.js b/components/cell/type.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/components/cell/type.js @@ -0,0 +1 @@ +export {}; diff --git a/components/mixins/page-scroll.d.ts b/components/mixins/page-scroll.d.ts new file mode 100644 index 0000000..efe4f63 --- /dev/null +++ b/components/mixins/page-scroll.d.ts @@ -0,0 +1,7 @@ +/// +/// +declare type IPageScrollOption = WechatMiniprogram.Page.IPageScrollOption; +declare type Scroller = (this: WechatMiniprogram.Component.TrivialInstance, event?: IPageScrollOption) => void; +export declare const pageScrollMixin: (scroller: Scroller) => string; +export declare const getRect: (context: any, selector: string) => Promise; +export {}; diff --git a/components/mixins/page-scroll.js b/components/mixins/page-scroll.js new file mode 100644 index 0000000..2cf9fc4 --- /dev/null +++ b/components/mixins/page-scroll.js @@ -0,0 +1,50 @@ +const getCurrentPage = function () { + const pages = getCurrentPages(); + return pages[pages.length - 1]; +}; +const onPageScroll = function (event) { + const page = getCurrentPage(); + if (!page) + return; + const { pageScroller } = page; + pageScroller.forEach((scroller) => { + if (typeof scroller === 'function') { + scroller(event); + } + }); +}; +export const pageScrollMixin = (scroller) => { + let bindScroller = scroller; + return Behavior({ + attached() { + const page = getCurrentPage(); + if (!page) + return; + bindScroller = scroller.bind(this); + if (Array.isArray(page.pageScroller)) { + page.pageScroller.push(bindScroller); + } + else { + page.pageScroller = + typeof page.onPageScroll === 'function' ? [page.onPageScroll.bind(page), bindScroller] : [bindScroller]; + } + page.onPageScroll = onPageScroll; + }, + detached() { + var _a; + const page = getCurrentPage(); + if (!page) + return; + page.pageScroller = ((_a = page.pageScroller) === null || _a === void 0 ? void 0 : _a.filter((item) => item !== scroller)) || []; + }, + }); +}; +export const getRect = function (context, selector) { + return new Promise((resolve) => { + wx.createSelectorQuery() + .in(context) + .select(selector) + .boundingClientRect() + .exec((rect = []) => resolve(rect[0])); + }); +}; diff --git a/components/mixins/transition.d.ts b/components/mixins/transition.d.ts new file mode 100644 index 0000000..8c23e35 --- /dev/null +++ b/components/mixins/transition.d.ts @@ -0,0 +1 @@ +export default function transition(): string; diff --git a/components/mixins/transition.js b/components/mixins/transition.js new file mode 100644 index 0000000..ee5215a --- /dev/null +++ b/components/mixins/transition.js @@ -0,0 +1,123 @@ +import config from '../common/config'; +const { prefix } = config; +export default function transition() { + return Behavior({ + properties: { + visible: { + type: Boolean, + value: null, + observer: 'watchVisible', + }, + appear: Boolean, + name: { + type: String, + value: 'fade', + }, + durations: { + type: Number, + optionalTypes: [Array], + }, + }, + data: { + transitionClass: '', + transitionDurations: 300, + className: '', + realVisible: false, + }, + created() { + this.status = ''; + this.transitionT = 0; + }, + attached() { + this.durations = this.getDurations(); + if (this.data.visible) { + this.enter(); + } + this.inited = true; + }, + detached() { + clearTimeout(this.transitionT); + }, + methods: { + watchVisible(curr, prev) { + if (this.inited && curr !== prev) { + curr ? this.enter() : this.leave(); + } + }, + getDurations() { + const { durations } = this.data; + if (Array.isArray(durations)) { + return durations.map((item) => Number(item)); + } + return [Number(durations), Number(durations)]; + }, + enter() { + const { name } = this.data; + const [duration] = this.durations; + this.status = 'entering'; + this.setData({ + realVisible: true, + transitionClass: `${prefix}-${name}-enter ${prefix}-${name}-enter-active`, + }); + setTimeout(() => { + this.setData({ + transitionClass: `${prefix}-${name}-enter-active ${prefix}-${name}-enter-to`, + }); + }, 30); + if (typeof duration === 'number' && duration > 0) { + this.transitionT = setTimeout(this.entered.bind(this), duration + 30); + } + }, + entered() { + this.customDuration = false; + clearTimeout(this.transitionT); + this.status = 'entered'; + this.setData({ + transitionClass: '', + }); + }, + leave() { + const { name } = this.data; + const [, duration] = this.durations; + this.status = 'leaving'; + this.setData({ + transitionClass: `${prefix}-${name}-leave ${prefix}-${name}-leave-active`, + }); + clearTimeout(this.transitionT); + setTimeout(() => { + this.setData({ + transitionClass: `${prefix}-${name}-leave-active ${prefix}-${name}-leave-to`, + }); + }, 30); + if (typeof duration === 'number' && duration > 0) { + this.customDuration = true; + this.transitionT = setTimeout(this.leaved.bind(this), duration + 30); + } + }, + leaved() { + this.customDuration = false; + this.triggerEvent('leaved'); + clearTimeout(this.transitionT); + this.status = 'leaved'; + this.setData({ + transitionClass: '', + }); + }, + onTransitionEnd() { + if (this.customDuration) { + return; + } + clearTimeout(this.transitionT); + if (this.status === 'entering' && this.data.visible) { + this.entered(); + } + else if (this.status === 'leaving' && !this.data.visible) { + this.leaved(); + this.setData({ + realVisible: false, + }); + } + }, + }, + }); +} diff --git a/components/sticky/README.md b/components/sticky/README.md new file mode 100644 index 0000000..ec56105 --- /dev/null +++ b/components/sticky/README.md @@ -0,0 +1,56 @@ +--- +title: Sticky 吸顶容器 +description: 用于常驻页面顶部的信息、操作展示。 +spline: data +isComponent: true +--- + + +## 引入 + +全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +"usingComponents": { + "t-sticky": "/components/sticky/sticky" +} +``` + +## 代码演示 + +将内容包裹在 `Sticky` 组件内 + + + +### 基础吸顶 + +{{ base }} + + +### 吸顶距离 + +{{ offset }} + +### 指定容器 + +{{ container }} + + + +## API + +### Sticky Props + +| 名称 | 类型 | 默认值 | 说明 | 必传 | +| ---------------- | -------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------- | ---- | +| container | Function | - | 函数返回容器对应的 NodesRef 节点,将对应节点指定为组件的外部容器,滚动时组件会始终保持在容器范围内,当组件即将超出容器底部时,会返回原位置。 | N | +| disabled | Boolean | false | 是否禁用组件 | N | +| external-classes | Array | - | 根结点外部样式。`['t-class']` | N | +| offset-top | Number | 0 | 吸顶时与顶部的距离,单位`px` | N | +| z-index | Number | 99 | 吸顶时的 z-index | N | + +### Sticky Events + +| 名称 | 参数 | 描述 | +| ------ | --------------------------------------------------- | ------------------------------------------------------ | +| scroll | `(detail: { scrollTop: number, isFixed: boolean })` | 滚动时触发,scrollTop: 距离顶部位置,isFixed: 是否吸顶 | diff --git a/components/sticky/index.d.ts b/components/sticky/index.d.ts new file mode 100644 index 0000000..a6df66d --- /dev/null +++ b/components/sticky/index.d.ts @@ -0,0 +1,3 @@ +export * from './props'; +export * from './type'; +export * from './sticky'; diff --git a/components/sticky/index.js b/components/sticky/index.js new file mode 100644 index 0000000..a6df66d --- /dev/null +++ b/components/sticky/index.js @@ -0,0 +1,3 @@ +export * from './props'; +export * from './type'; +export * from './sticky'; diff --git a/components/sticky/props.d.ts b/components/sticky/props.d.ts new file mode 100644 index 0000000..175af72 --- /dev/null +++ b/components/sticky/props.d.ts @@ -0,0 +1,3 @@ +import { TdStickyProps } from './type'; +declare const props: TdStickyProps; +export default props; diff --git a/components/sticky/props.js b/components/sticky/props.js new file mode 100644 index 0000000..7e5cb71 --- /dev/null +++ b/components/sticky/props.js @@ -0,0 +1,21 @@ +const props = { + container: { + type: null, + }, + disabled: { + type: Boolean, + value: false, + }, + externalClasses: { + type: Array, + }, + offsetTop: { + type: Number, + value: 0, + }, + zIndex: { + type: Number, + value: 99, + }, +}; +export default props; diff --git a/components/sticky/sticky.d.ts b/components/sticky/sticky.d.ts new file mode 100644 index 0000000..c2c0138 --- /dev/null +++ b/components/sticky/sticky.d.ts @@ -0,0 +1,30 @@ +import { SuperComponent } from '../common/src/index'; +import type { TdStickyProps } from './type'; +export interface StickyProps extends TdStickyProps { +} +export default class Sticky extends SuperComponent { + externalClasses: string[]; + properties: TdStickyProps; + behaviors: string[]; + observers: { + 'offsetTop, disabled, container'(): void; + }; + data: { + prefix: string; + containerStyle: string; + contentStyle: string; + classPrefix: string; + }; + ready(): void; + methods: { + onScroll(event?: { + scrollTop: number; + }): void; + setDataAfterDiff(data: { + isFixed: boolean; + height?: number; + transform?: number; + }): void; + getContainerRect(): Promise; + }; +} diff --git a/components/sticky/sticky.js b/components/sticky/sticky.js new file mode 100644 index 0000000..08999ef --- /dev/null +++ b/components/sticky/sticky.js @@ -0,0 +1,118 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +import { SuperComponent, wxComponent } from '../common/src/index'; +import props from './props'; +import config from '../common/config'; +import { pageScrollMixin, getRect } from '../mixins/page-scroll'; +const { prefix } = config; +const ContainerClass = `.${prefix}-sticky`; +let Sticky = class Sticky extends SuperComponent { + constructor() { + super(...arguments); + this.externalClasses = [`${prefix}-class`]; + this.properties = props; + this.behaviors = [ + pageScrollMixin(function (event) { + this.onScroll(event); + }), + ]; + this.observers = { + 'offsetTop, disabled, container'() { + this.onScroll(); + }, + }; + this.data = { + prefix, + containerStyle: '', + contentStyle: '', + classPrefix: `.${prefix}-sticky`, + }; + this.methods = { + onScroll(event) { + const { scrollTop } = event || {}; + const { container, offsetTop, disabled } = this.properties; + if (disabled) { + this.setDataAfterDiff({ + isFixed: false, + transform: 0, + }); + return; + } + this.scrollTop = scrollTop || this.scrollTop; + if (typeof container === 'function') { + Promise.all([getRect(this, ContainerClass), this.getContainerRect()]).then(([root, container]) => { + if (!root || !container) + return; + if (offsetTop + root.height > container.height + container.top) { + this.setDataAfterDiff({ + isFixed: false, + transform: container.height - root.height, + }); + } + else if (offsetTop >= root.top) { + this.setDataAfterDiff({ + isFixed: true, + height: root.height, + transform: 0, + }); + } + else { + this.setDataAfterDiff({ isFixed: false, transform: 0 }); + } + }); + return; + } + getRect(this, ContainerClass).then((root) => { + if (!root) + return; + if (offsetTop >= root.top) { + this.setDataAfterDiff({ isFixed: true, height: root.height }); + this.transform = 0; + } + else { + this.setDataAfterDiff({ isFixed: false }); + } + }); + }, + setDataAfterDiff(data) { + const { offsetTop } = this.properties; + const { containerStyle: prevContainerStyle, contentStyle: prevContentStyle } = this.data; + const { isFixed, height, transform } = data; + wx.nextTick(() => { + let containerStyle = ''; + let contentStyle = ''; + if (isFixed) { + containerStyle += `height:${height}px;`; + contentStyle += `position:fixed;top:${offsetTop}px`; + } + if (transform) { + const translate = `translate3d(0, ${transform}px, 0)`; + contentStyle += `-webkit-transform:${translate};transform:${translate};`; + } + if (prevContainerStyle !== containerStyle || prevContentStyle !== contentStyle) { + this.setData({ containerStyle, contentStyle }); + } + this.triggerEvent('scroll', { + scrollTop: this.scrollTop, + isFixed, + }); + }); + }, + getContainerRect() { + const nodesRef = this.properties.container(); + return new Promise((resolve) => nodesRef.boundingClientRect(resolve).exec()); + }, + }; + } + ready() { + this.onScroll(); + } +}; +Sticky = __decorate([ + wxComponent() +], Sticky); +export default Sticky; diff --git a/components/sticky/sticky.json b/components/sticky/sticky.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/components/sticky/sticky.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/components/sticky/sticky.wxml b/components/sticky/sticky.wxml new file mode 100644 index 0000000..c1781f1 --- /dev/null +++ b/components/sticky/sticky.wxml @@ -0,0 +1,5 @@ + + + + + diff --git a/components/sticky/sticky.wxss b/components/sticky/sticky.wxss new file mode 100644 index 0000000..730de8a --- /dev/null +++ b/components/sticky/sticky.wxss @@ -0,0 +1,33 @@ +.t-float-left { + float: left; +} +.t-float-right { + float: right; +} +@keyframes tdesign-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.hotspot-expanded.relative { + position: relative; +} +.hotspot-expanded::after { + content: ''; + display: block; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + transform: scale(1.5); +} +.t-sticky { + position: relative; +} +.t-sticky__content { + width: 100%; +} diff --git a/components/sticky/type.d.ts b/components/sticky/type.d.ts new file mode 100644 index 0000000..acdbedb --- /dev/null +++ b/components/sticky/type.d.ts @@ -0,0 +1,27 @@ +export interface TdStickyProps { + container?: { + type: null; + value?: null; + required?: boolean; + }; + disabled?: { + type: BooleanConstructor; + value?: boolean; + required?: boolean; + }; + externalClasses?: { + type: ArrayConstructor; + value?: ['t-class']; + required?: boolean; + }; + offsetTop?: { + type: NumberConstructor; + value?: number; + required?: boolean; + }; + zIndex?: { + type: NumberConstructor; + value?: number; + required?: boolean; + }; +} diff --git a/components/sticky/type.js b/components/sticky/type.js new file mode 100644 index 0000000..95da36c --- /dev/null +++ b/components/sticky/type.js @@ -0,0 +1,2 @@ +; +export {}; diff --git a/components/swipe-cell/README.md b/components/swipe-cell/README.md new file mode 100644 index 0000000..0fd1156 --- /dev/null +++ b/components/swipe-cell/README.md @@ -0,0 +1,46 @@ +--- +title: SwipeCell 滑动操作 +description: 用于承载列表中的更多操作,通过左右滑动来展示,按钮的宽度固定高度根据列表高度而变化。 +spline: message +isComponent: true +--- + + +## 引入 + +全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +"usingComponents": { + "t-swipe-cell": "tdesign-miniprogram/swipe-cell/swipe-cell" +} +``` + +## 代码演示 + + + +### 往左滑动 + +{{ left }} + +### 往右滑动 + +{{ right }} + +## API + +### SwipeCell Props + +| 名称 | 类型 | 默认值 | 说明| 必传 | +| -------- | --------------- | ------ | -- | --------------- | +| disabled | Boolean | - | 是否禁用滑动| N | +| left | Array / Slot | - | 左侧滑动操作项。所有行为同 `right`。TS 类型:`Array`| N | +| opened | Boolean / Array | false | 操作项是否呈现为打开态,值为数组时表示分别控制左右滑动的展开和收起状态。TS 类型:`boolean| Array` | N | +| right | Array / Slot | - | 右侧滑动操作项。有两种定义方式,一种是使用数组,二种是使用插槽。`right.text` 表示操作文本,`right.className` 表示操作项类名,`right.style` 表示操作项样式,`right.onClick` 表示点击操作项后执行的回调函数。示例:`[{ text: '删除', style: 'background-color: red', onClick: () => { /** TO DO */ } }]`。TS 类型:`Array`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/swipe-cell/type.ts) | N | + +### SwipeCell Events + +名称 | 参数 | 描述 +-- | -- | -- +click | `(action: SwipeActionItem, source: SwipeSource)` | 操作项点击时触发(插槽写法组件不触发,业务侧自定义内容和事件)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/swipe-cell/type.ts)。
`type SwipeSource = 'left' | 'right'`
diff --git a/components/swipe-cell/props.d.ts b/components/swipe-cell/props.d.ts new file mode 100644 index 0000000..9a3159c --- /dev/null +++ b/components/swipe-cell/props.d.ts @@ -0,0 +1,3 @@ +import { TdSwipeCellProps } from './type'; +declare const props: TdSwipeCellProps; +export default props; diff --git a/components/swipe-cell/props.js b/components/swipe-cell/props.js new file mode 100644 index 0000000..5298eef --- /dev/null +++ b/components/swipe-cell/props.js @@ -0,0 +1,17 @@ +const props = { + disabled: { + type: Boolean, + }, + left: { + type: Array, + }, + opened: { + type: Boolean, + optionalTypes: [Array], + value: false, + }, + right: { + type: Array, + }, +}; +export default props; diff --git a/components/swipe-cell/swipe-cell.d.ts b/components/swipe-cell/swipe-cell.d.ts new file mode 100644 index 0000000..4f7a37a --- /dev/null +++ b/components/swipe-cell/swipe-cell.d.ts @@ -0,0 +1,23 @@ +import { SuperComponent } from '../common/src/index'; +export default class SwiperCell extends SuperComponent { + behaviors: string[]; + externalClasses: string[]; + options: { + multipleSlots: boolean; + }; + properties: import("./type").TdSwipeCellProps; + data: { + wrapperStyle: string; + closed: boolean; + opened: boolean; + classPrefix: string; + }; + attached(): void; + setSwipeWidth(): Promise; + detached(): void; + open(): void; + close(): void; + closeOther(): void; + onTap(): void; + onActionTap(event: any): void; +} diff --git a/components/swipe-cell/swipe-cell.js b/components/swipe-cell/swipe-cell.js new file mode 100644 index 0000000..848d2da --- /dev/null +++ b/components/swipe-cell/swipe-cell.js @@ -0,0 +1,77 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +import dom from '../behaviors/dom'; +import { SuperComponent, wxComponent } from '../common/src/index'; +import config from '../common/config'; +import props from './props'; +let ARRAY = []; +const { prefix } = config; +let SwiperCell = class SwiperCell extends SuperComponent { + constructor() { + super(...arguments); + this.behaviors = [dom]; + this.externalClasses = ['t-class']; + this.options = { + multipleSlots: true, + }; + this.properties = props; + this.data = { + wrapperStyle: '', + closed: true, + opened: false, + classPrefix: `.${prefix}-swipe-cell`, + }; + } + attached() { + ARRAY.push(this); + wx.nextTick(() => { + this.setSwipeWidth(); + }); + } + setSwipeWidth() { + return __awaiter(this, void 0, void 0, function* () { + const rightRect = yield this.gettingBoundingClientRect(`${this.data.classPrefix}__right`); + const leftRect = yield this.gettingBoundingClientRect(`${this.data.classPrefix}__left`); + this.setData({ + leftWidth: leftRect.width, + rightWidth: rightRect.width, + }); + }); + } + detached() { + ARRAY = ARRAY.filter((item) => item !== this); + } + open() { + this.setData({ opened: true }); + } + close() { + this.setData({ opened: false }); + } + closeOther() { + ARRAY.filter((item) => item !== this).forEach((item) => item.close()); + } + onTap() { + this.close(); + } + onActionTap(event) { + const { currentTarget: { dataset: { action }, }, } = event; + this.triggerEvent('click', action); + } +}; +SwiperCell = __decorate([ + wxComponent() +], SwiperCell); +export default SwiperCell; diff --git a/components/swipe-cell/swipe-cell.json b/components/swipe-cell/swipe-cell.json new file mode 100644 index 0000000..467ce29 --- /dev/null +++ b/components/swipe-cell/swipe-cell.json @@ -0,0 +1,3 @@ +{ + "component": true +} diff --git a/components/swipe-cell/swipe-cell.wxml b/components/swipe-cell/swipe-cell.wxml new file mode 100644 index 0000000..170fbf6 --- /dev/null +++ b/components/swipe-cell/swipe-cell.wxml @@ -0,0 +1,46 @@ + + + + + + + {{item.text}} + + + + + + + {{item.text}} + + + + diff --git a/components/swipe-cell/swipe-cell.wxs b/components/swipe-cell/swipe-cell.wxs new file mode 100644 index 0000000..dbb7fec --- /dev/null +++ b/components/swipe-cell/swipe-cell.wxs @@ -0,0 +1,176 @@ +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; + state.opened = state.opened || false; +}; + +var initRightWidth = function (newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.rightWidth = newVal; + initOpen(ownerInstance); +}; + +var initLeftWidth = function (newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.leftWidth = newVal; + initOpen(ownerInstance); +}; + +var initOpen = function (ownerInstance) { + getState(ownerInstance); + if (state.opened.constructor === 'Boolean') { + // opened为boolen类型,判断默认打开 + if (state.opened && state.rightWidth > 0) { + swipeMove(-state.rightWidth); + } else if (state.opened && state.leftWidth > 0) { + swipeMove(state.leftWidth); + } + } + + if (state.opened.constructor === 'Array') { + // opened为array类型,判断默认打开,同时设定左右action时优先打开右边 + if (state.opened[1] && state.rightWidth > 0) { + swipeMove(-state.rightWidth); + } else if (state.opened[0] && state.leftWidth > 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) { + if (_offset === undefined) _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 onOpenedChange = function (newVal, oldVal, ownerInstance) { + getState(ownerInstance); + state.opened = newVal; + 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, + onOpenedChange: onOpenedChange, +}; diff --git a/components/swipe-cell/swipe-cell.wxss b/components/swipe-cell/swipe-cell.wxss new file mode 100644 index 0000000..1d1ab98 --- /dev/null +++ b/components/swipe-cell/swipe-cell.wxss @@ -0,0 +1,45 @@ +.t-float-left { + float: left; +} +.t-float-right { + float: right; +} +@keyframes tdesign-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.hotspot-expanded.relative { + position: relative; +} +.hotspot-expanded::after { + content: ''; + display: block; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + transform: scale(1.5); +} +.t-swipe-cell { + position: relative; + overflow: hidden; +} +.t-swipe-cell__left, +.t-swipe-cell__right { + position: absolute; + top: 0; + height: 100%; +} +.t-swipe-cell__left { + left: 0; + transform: translate3d(-100%, 0, 0); +} +.t-swipe-cell__right { + right: 0; + transform: translate3d(100%, 0, 0); +} diff --git a/components/swipe-cell/type.d.ts b/components/swipe-cell/type.d.ts new file mode 100644 index 0000000..27ab05d --- /dev/null +++ b/components/swipe-cell/type.d.ts @@ -0,0 +1,30 @@ +export interface TdSwipeCellProps { + disabled?: { + type: BooleanConstructor; + value?: boolean; + required?: boolean; + }; + left?: { + type: ArrayConstructor; + value?: Array; + required?: boolean; + }; + opened?: { + type: BooleanConstructor; + optionalTypes: Array; + value?: boolean | Array; + required?: boolean; + }; + right?: { + type: ArrayConstructor; + value?: Array; + required?: boolean; + }; +} +export interface SwipeActionItem { + text: string; + className?: string; + style?: string; + onClick?: () => void; + [key: string]: any; +} diff --git a/components/swipe-cell/type.js b/components/swipe-cell/type.js new file mode 100644 index 0000000..5f16a47 --- /dev/null +++ b/components/swipe-cell/type.js @@ -0,0 +1,3 @@ +; +; +export {}; diff --git a/components/tabs/README.en-US.md b/components/tabs/README.en-US.md new file mode 100644 index 0000000..cb5f68b --- /dev/null +++ b/components/tabs/README.en-US.md @@ -0,0 +1,34 @@ +:: BASE_DOC :: + +## API +### Tabs Props + +name | type | default | description | required +-- | -- | -- | -- | -- +animation | Object | - | Typescript:`TabAnimation` `type TabAnimation = { duration: number } & Record`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N +external-classes | Array | - | `['t-class', 't-class-item', 't-class-active', 't-class-track']` | N +placement | String | top | options:left/top | N +show-bottom-line | Boolean | true | \- | N +sticky | Boolean | false | \- | N +sticky-props | Object | - | Typescript:`StickyProps`,[Sticky API Documents](./sticky?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N +swipeable | Boolean | true | \- | N +value | String / Number | - | Typescript:`TabValue` `type TabValue = string | number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N +default-value | String / Number | undefined | uncontrolled property。Typescript:`TabValue` `type TabValue = string | number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N + +### Tabs Events + +name | params | description +-- | -- | -- +change | `(value: TabValue, label: string)` | \- +click | `(value: TabValue, label: string)` | \- +scroll | `({ scrollTop: number, isFixed: boolean })` | \- + +### TabPanel Props + +name | type | default | description | required +-- | -- | -- | -- | -- +destroy-on-hide | Boolean | true | \- | N +disabled | Boolean | false | \- | N +label | String | - | \- | N +panel | String / Slot | - | \- | N +value | String / Number | - | Typescript:`TabValue` | N diff --git a/components/tabs/README.md b/components/tabs/README.md new file mode 100644 index 0000000..1e97cfd --- /dev/null +++ b/components/tabs/README.md @@ -0,0 +1,141 @@ +--- +title: Tabs 选项卡 +description: 用于内容分类后的展示切换。 +spline: navigation +isComponent: true +--- + + +## 引入 + +全局引入,在 miniprogram 根目录下的`app.json`中配置,局部引入,在需要引入的页面或组件的`index.json`中配置。 + +```json +"usingComponents": { + "t-tabs": "tdesign-miniprogram/tabs/tabs", + "t-tab-panel": "tdesign-miniprogram/tabs/tab-panel" +} +``` + +## 主题定制 + +CSS 变量名|说明 +--|-- +--td-tab-nav-bg-color | 选项卡背景颜色 +--td-tab-item-color | 选项卡字体颜色 +--td-tab-item-active-color | 选项卡激活时字体颜色 +--td-tab-item-disabled-color | 选项卡禁止状态时字体颜色 +--td-tab-track-color | 选项卡滑块颜色 +--td-tab-track-thickness | 选项卡滑块厚度(水平时为高度,垂直时为宽度) +--td-tab-border-color | 选项卡底部边框颜色 + +## 代码演示 + +### 基础选项卡 + +{{ base }} + +### 超过屏幕滚动 +{{ scroll }} + +### 无下划线 +{{ unline }} + +### 动画时间可调整 +{{ adjust-time }} + +### 选项卡状态 +{{ status }} + +### 竖向选项卡 +{{ vertical }} + +### 选中态文字尺寸规格 +{{ size }} + + + + + + +### 受控用法 + +```html + + 标签一内容 + 标签二内容 + +``` + +```js +Page({ + data: { + value: '0', + }, + onTabsChange(e) { + this.setData({ value: e.detail.value }) + }, +}); +``` + +### 与 Popup 使用 + +```html + + + 标签一内容 + 标签二内容 + 标签三内容 + + +``` + +```js +Page({ + data: { + visible: false + }, + showPopup() { + this.setData({ + visible: true + }, () => { + const tabs = this.selectComponent('tabs'); + + tabs.setTrack(); // 这一步很重要,因为小程序的无法正确执行生命周期,所以需要手动设置下 tabs 的滑块 + }) + } +}) +``` + +## API +### Tabs Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +animation | Object | - | 动画效果设置。其中 duration 表示动画时长。TS 类型:`TabAnimation` `type TabAnimation = { duration: number } & Record`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N +external-classes | Array | - | 组件类名,分别用于设置 组件外层元素、选项卡单项、选项卡激活态、滚动条样式类名 等类名。`['t-class', 't-class-item', 't-class-active', 't-class-track']` | N +placement | String | top | 选项卡位置。可选项:left/top | N +show-bottom-line | Boolean | true | 是否展示底部激活线条 | N +sticky | Boolean | false | 是否开启粘性布局 | N +sticky-props | Object | - | 透传至 Sticky 组件。TS 类型:`StickyProps`,[Sticky API Documents](./sticky?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N +swipeable | Boolean | true | 是否可以滑动切换 | N +value | String / Number | - | 激活的选项卡值。TS 类型:`TabValue` `type TabValue = string | number`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N +default-value | String / Number | undefined | 激活的选项卡值。非受控属性。TS 类型:`TabValue` `type TabValue = string | number`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/src/tabs/type.ts) | N + +### Tabs Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(value: TabValue, label: string)` | 激活的选项卡发生变化时触发 +click | `(value: TabValue, label: string)` | 点击 tab 选项卡时触发 +scroll | `({ scrollTop: number, isFixed: boolean })` | 页面滚动时触发,scrollTop: 距离顶部位置,isFixed: 是否吸顶 + +### TabPanel Props + +名称 | 类型 | 默认值 | 说明 | 必传 +-- | -- | -- | -- | -- +destroy-on-hide | Boolean | true | 选项卡内容隐藏时是否销毁 | N +disabled | Boolean | false | 是否禁用当前选项卡 | N +label | String | - | 选项卡名称 | N +panel | String / Slot | - | 用于自定义选项卡面板内容 | N +value | String / Number | - | 选项卡的值,唯一标识。TS 类型:`TabValue` | N diff --git a/components/tabs/props.d.ts b/components/tabs/props.d.ts new file mode 100644 index 0000000..215fa49 --- /dev/null +++ b/components/tabs/props.d.ts @@ -0,0 +1,3 @@ +import { TdTabsProps } from './type'; +declare const props: TdTabsProps; +export default props; diff --git a/components/tabs/props.js b/components/tabs/props.js new file mode 100644 index 0000000..da54bec --- /dev/null +++ b/components/tabs/props.js @@ -0,0 +1,35 @@ +const props = { + animation: { + type: Object, + }, + externalClasses: { + type: Array, + }, + placement: { + type: String, + value: 'top', + }, + showBottomLine: { + type: Boolean, + value: true, + }, + sticky: { + type: Boolean, + value: false, + }, + stickyProps: { + type: Object, + }, + swipeable: { + type: Boolean, + value: true, + }, + value: { + type: null, + value: null, + }, + defaultValue: { + type: null, + }, +}; +export default props; diff --git a/components/tabs/tab-panel-props.d.ts b/components/tabs/tab-panel-props.d.ts new file mode 100644 index 0000000..9329be0 --- /dev/null +++ b/components/tabs/tab-panel-props.d.ts @@ -0,0 +1,3 @@ +import { TdTabPanelProps } from './type'; +declare const props: TdTabPanelProps; +export default props; diff --git a/components/tabs/tab-panel-props.js b/components/tabs/tab-panel-props.js new file mode 100644 index 0000000..c2a6e07 --- /dev/null +++ b/components/tabs/tab-panel-props.js @@ -0,0 +1,21 @@ +const props = { + destroyOnHide: { + type: Boolean, + value: true, + }, + disabled: { + type: Boolean, + value: false, + }, + label: { + type: String, + value: '', + }, + panel: { + type: String, + }, + value: { + type: null, + }, +}; +export default props; diff --git a/components/tabs/tab-panel.d.ts b/components/tabs/tab-panel.d.ts new file mode 100644 index 0000000..7403133 --- /dev/null +++ b/components/tabs/tab-panel.d.ts @@ -0,0 +1,17 @@ +import { SuperComponent, RelationsOptions } from '../common/src/index'; +export default class TabPanel extends SuperComponent { + relations: RelationsOptions; + properties: import("./type").TdTabPanelProps; + data: { + prefix: string; + classPrefix: string; + active: boolean; + hide: boolean; + }; + observers: { + label(): void; + }; + getComputedName(): string; + update(): void; + render(active: Boolean, parent: any): void; +} diff --git a/components/tabs/tab-panel.js b/components/tabs/tab-panel.js new file mode 100644 index 0000000..e24af40 --- /dev/null +++ b/components/tabs/tab-panel.js @@ -0,0 +1,54 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +import { SuperComponent, wxComponent } from '../common/src/index'; +import props from './tab-panel-props'; +import config from '../common/config'; +const { prefix } = config; +const name = `${prefix}-tab-panel`; +let TabPanel = class TabPanel extends SuperComponent { + constructor() { + super(...arguments); + this.relations = { + './tabs': { + type: 'ancestor', + }, + }; + this.properties = props; + this.data = { + prefix, + classPrefix: name, + active: false, + hide: true, + }; + this.observers = { + label() { + this.update(); + }, + }; + } + getComputedName() { + if (this.properties.value != null) { + return `${this.properties.value}`; + } + return `${this.index}`; + } + update() { + if (this.parent) { + this.parent.updateTabs(); + } + } + render(active, parent) { + this.setData({ + active, + hide: !parent.animated && !active, + }); + } +}; +TabPanel = __decorate([ + wxComponent() +], TabPanel); +export default TabPanel; diff --git a/components/tabs/tab-panel.json b/components/tabs/tab-panel.json new file mode 100644 index 0000000..a89ef4d --- /dev/null +++ b/components/tabs/tab-panel.json @@ -0,0 +1,4 @@ +{ + "component": true, + "usingComponents": {} +} diff --git a/components/tabs/tab-panel.wxml b/components/tabs/tab-panel.wxml new file mode 100644 index 0000000..ab1f6f2 --- /dev/null +++ b/components/tabs/tab-panel.wxml @@ -0,0 +1,9 @@ + + + {{panel}} + + diff --git a/components/tabs/tab-panel.wxss b/components/tabs/tab-panel.wxss new file mode 100644 index 0000000..2a5dd88 --- /dev/null +++ b/components/tabs/tab-panel.wxss @@ -0,0 +1,37 @@ +.t-float-left { + float: left; +} +.t-float-right { + float: right; +} +@keyframes tdesign-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.hotspot-expanded.relative { + position: relative; +} +.hotspot-expanded::after { + content: ''; + display: block; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + transform: scale(1.5); +} +:host { + flex-shrink: 0; + width: 100%; + height: 100%; + box-sizing: border-box; +} +.t-tab-panel { + height: inherit; + width: inherit; +} diff --git a/components/tabs/tabs.d.ts b/components/tabs/tabs.d.ts new file mode 100644 index 0000000..809620f --- /dev/null +++ b/components/tabs/tabs.d.ts @@ -0,0 +1,49 @@ +/// +import { SuperComponent, RelationsOptions } from '../common/src/index'; +export default class Tabs extends SuperComponent { + behaviors: string[]; + externalClasses: string[]; + relations: RelationsOptions; + properties: import("./type").TdTabsProps; + controlledProps: { + key: string; + event: string; + }[]; + observers: { + value(name: any): void; + animation(v: any): void; + placement(): void; + }; + data: { + prefix: string; + classPrefix: string; + tabs: any[]; + currentIndex: number; + trackStyle: string; + isScrollX: boolean; + isScrollY: boolean; + direction: string; + animate: { + duration: number; + }; + offset: number; + }; + created(): void; + attached(): void; + methods: { + adjustPlacement(): void; + }; + updateTabs(cb: any): void; + setCurrentIndexByName(name: any): void; + setCurrentIndex(index: number): void; + getCurrentName(): any; + calcScrollOffset(containerWidth: number, targetLeft: number, targetWidth: number, offset: number, currentIndex: number): number; + setTrack(): void; + onTabTap(event: any): void; + onTouchStart(event: any): void; + onTouchMove(event: any): void; + onTouchEnd(): void; + onTouchScroll(event: WechatMiniprogram.CustomEvent): void; + changeIndex(index: any): void; + getAvailableTabIndex(deltaX: number): number; +} diff --git a/components/tabs/tabs.js b/components/tabs/tabs.js new file mode 100644 index 0000000..d99a014 --- /dev/null +++ b/components/tabs/tabs.js @@ -0,0 +1,241 @@ +var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +}; +import dom from '../behaviors/dom'; +import touch from '../behaviors/touch'; +import { SuperComponent, wxComponent } from '../common/src/index'; +import props from './props'; +import config from '../common/config'; +const { prefix } = config; +const name = `${prefix}-tabs`; +var Position; +(function (Position) { + Position["top"] = "top"; + Position["right"] = "right"; + Position["bottom"] = "bottom"; + Position["left"] = "left"; +})(Position || (Position = {})); +const trackLineWidth = 30; +let Tabs = class Tabs extends SuperComponent { + constructor() { + super(...arguments); + this.behaviors = [dom, touch]; + this.externalClasses = [`${prefix}-class`, `${prefix}-class-item`, `${prefix}-class-active`, `${prefix}-class-track`]; + this.relations = { + './tab-panel': { + type: 'descendant', + linked(target) { + this.children.push(target); + target.index = this.children.length - 1; + this.updateTabs(); + }, + unlinked(target) { + this.children = this.children.filter((item) => item.index !== target.index); + this.updateTabs(() => this.setTrack()); + }, + }, + }; + this.properties = props; + this.controlledProps = [ + { + key: 'value', + event: 'change', + }, + ]; + this.observers = { + value(name) { + if (name !== this.getCurrentName()) { + this.setCurrentIndexByName(name); + } + }, + animation(v) { + this.setData({ animate: v }); + }, + placement() { + this.adjustPlacement(); + }, + }; + this.data = { + prefix, + classPrefix: name, + tabs: [], + currentIndex: -1, + trackStyle: '', + isScrollX: true, + isScrollY: false, + direction: 'X', + animate: { duration: 0 }, + offset: 0, + }; + this.methods = { + adjustPlacement() { + const { placement } = this.properties; + let isScrollX = false; + let isScrollY = false; + if (placement === Position.top || placement === Position.bottom) { + isScrollX = true; + } + else { + isScrollY = true; + } + this.setData({ + isScrollX, + isScrollY, + direction: isScrollX ? 'X' : 'Y', + }); + }, + }; + } + created() { + this.children = this.children || []; + } + attached() { + wx.nextTick(() => { + this.setTrack(); + }); + this.adjustPlacement(); + this.gettingBoundingClientRect(`.${name}`, true).then((res) => { + this.containerWidth = res[0].width; + }); + } + updateTabs(cb) { + const { children } = this; + this.setData({ + tabs: children.map((child) => child.data), + }, cb); + this.setCurrentIndexByName(this.properties.value); + } + setCurrentIndexByName(name) { + const { children } = this; + const index = children.findIndex((child) => child.getComputedName() === `${name}`); + if (index > -1) { + this.setCurrentIndex(index); + } + } + setCurrentIndex(index) { + if (index <= -1 || index >= this.children.length) + return; + this.children.forEach((child, idx) => { + const isActive = index === idx; + if (isActive !== child.data.active) { + child.render(isActive, this); + } + }); + if (this.data.currentIndex === index) + return; + this.setData({ + currentIndex: index, + }); + this.setTrack(); + } + getCurrentName() { + if (this.children) { + const activeTab = this.children[this.data.currentIndex]; + if (activeTab) { + return activeTab.getComputedName(); + } + } + } + calcScrollOffset(containerWidth, targetLeft, targetWidth, offset, currentIndex) { + return currentIndex * targetWidth - (1 / 2) * containerWidth + targetWidth / 2; + } + setTrack() { + if (!this.properties.showBottomLine) + return; + const { children } = this; + if (!children) + return; + const { currentIndex, isScrollX, direction } = this.data; + if (currentIndex <= -1) + return; + this.gettingBoundingClientRect(`.${prefix}-tabs__item`, true) + .then((res) => { + const rect = res[currentIndex]; + if (!rect) + return; + let count = 0; + let distance = 0; + for (const item of res) { + if (count < currentIndex) { + distance += isScrollX ? item.width : item.height; + count += 1; + } + } + if (this.containerWidth) { + const offset = this.calcScrollOffset(this.containerWidth, rect.left, rect.width, this.data.offset, currentIndex); + this.setData({ + offset, + }); + } + if (isScrollX) { + distance += (rect.width - trackLineWidth) / 2; + } + let trackStyle = `-webkit-transform: translate${direction}(${distance}px); + transform: translate${direction}(${distance}px); + `; + trackStyle += isScrollX ? `width: ${trackLineWidth}px;` : `height: ${rect.height}px;`; + this.setData({ + trackStyle, + }); + }) + .catch((err) => { + this.triggerEvent('error', err); + }); + } + onTabTap(event) { + const { index } = event.currentTarget.dataset; + this.changeIndex(index); + } + onTouchStart(event) { + if (!this.properties.swipeable) + return; + this.touchStart(event); + } + onTouchMove(event) { + if (!this.properties.swipeable) + return; + this.touchMove(event); + } + onTouchEnd() { + if (!this.properties.swipeable) + return; + const { direction, deltaX, offsetX } = this; + const minSwipeDistance = 50; + if (direction === 'horizontal' && offsetX >= minSwipeDistance) { + const index = this.getAvailableTabIndex(deltaX); + if (index !== -1) { + this.changeIndex(index); + } + } + } + onTouchScroll(event) { + this._trigger('scroll', event.detail); + } + changeIndex(index) { + const currentTab = this.data.tabs[index]; + const { value, label } = currentTab; + if (!(currentTab === null || currentTab === void 0 ? void 0 : currentTab.disabled) && index !== this.data.currentIndex) { + this._trigger('change', { value, label }); + } + this._trigger('click', { value, label }); + } + getAvailableTabIndex(deltaX) { + const step = deltaX > 0 ? -1 : 1; + const { currentIndex, tabs } = this.data; + const len = tabs.length; + for (let i = step; currentIndex + step >= 0 && currentIndex + step < len; i += step) { + const newIndex = currentIndex + i; + if (newIndex >= 0 && newIndex < len && tabs[newIndex] && !tabs[newIndex].disabled) { + return newIndex; + } + } + return -1; + } +}; +Tabs = __decorate([ + wxComponent() +], Tabs); +export default Tabs; diff --git a/components/tabs/tabs.json b/components/tabs/tabs.json new file mode 100644 index 0000000..93d5828 --- /dev/null +++ b/components/tabs/tabs.json @@ -0,0 +1,6 @@ +{ + "component": true, + "usingComponents": { + "t-sticky": "/components/sticky/sticky" + } +} diff --git a/components/tabs/tabs.wxml b/components/tabs/tabs.wxml new file mode 100644 index 0000000..34e6a54 --- /dev/null +++ b/components/tabs/tabs.wxml @@ -0,0 +1,53 @@ + + + + + + + + + {{item.label}} + + + + + + + + + + + + diff --git a/components/tabs/tabs.wxs b/components/tabs/tabs.wxs new file mode 100644 index 0000000..ca86ad5 --- /dev/null +++ b/components/tabs/tabs.wxs @@ -0,0 +1,24 @@ +/* eslint-disable */ +/* utils */ + +/** + * animate */ +// 为tab切换添加动画 +function animate(options) { + var result = + '-webkit-transition-duration: ' + + options.duration + + 's' + + ';transition-duration: ' + + options.duration + + 's'; + result += + options.direction === 'Y' + ? ';transform: translate3d( 0,' + -100 * options.currentIndex + '%, 0)' + : ';transform: translate3d( ' + -100 * options.currentIndex + '%,0, 0)'; + return result; +} + +module.exports = { + animate: animate, +}; diff --git a/components/tabs/tabs.wxss b/components/tabs/tabs.wxss new file mode 100644 index 0000000..9f89849 --- /dev/null +++ b/components/tabs/tabs.wxss @@ -0,0 +1,175 @@ +.t-float-left { + float: left; +} +.t-float-right { + float: right; +} +@keyframes tdesign-fade-out { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +.hotspot-expanded.relative { + position: relative; +} +.hotspot-expanded::after { + content: ''; + display: block; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + transform: scale(1.5); +} +page { + --td-tab-nav-bg-color: #fff; + --td-tab-item-color: rgba(0, 0, 0, 0.6); + --td-tab-item-active-color: #0052d9; + --td-tab-item-disabled-color: #c8c9cc; + --td-tab-track-color: #0052d9; + --td-tab-track-thickness: 4rpx; + --td-tab-border-color: rgba(150, 95, 95, 0.12); +} +.t-tabs { + position: relative; + font-size: 28rpx; + background-color: #fff; +} +.t-tabs__wrapper { + display: flex; + overflow: hidden; +} +.t-tabs .t-is-active { + font-weight: 700; + color: var(--td-tab-item-active-color, #0052d9); +} +.t-tabs .t-is-disabled { + color: var(--td-tab-item-disabled-color, #c8c9cc); +} +.t-tabs__item { + flex: 1; + font-weight: 400; + color: var(--td-tab-item-color, rgba(0, 0, 0, 0.6)); +} +.t-tabs__item--top, +.t-tabs__item--bottom { + height: 92rpx; + line-height: 92rpx; + text-align: center; + min-width: 162rpx; +} +.t-tabs__item--left, +.t-tabs__item--right { + text-align: center; + height: 108rpx; + line-height: 108rpx; + width: 208rpx; + background-color: #f3f3f3; +} +.t-tabs__item--left.t-is-active, +.t-tabs__item--right.t-is-active { + background-color: #fff; +} +.t-tabs__content { + overflow: hidden; +} +.t-tabs__nav { + position: relative; + user-select: none; + width: 100%; + display: flex; + flex-wrap: nowrap; + align-items: center; +} +.t-tabs__nav--left, +.t-tabs__nav--right { + flex-direction: column; +} +.t-tabs__track { + position: absolute; + font-weight: 600; + z-index: 1; + transition-duration: 0.3s; + background-color: var(--td-tab-track-color, #0052d9); +} +.t-tabs__track--left { + left: 0; + top: 0; + width: var(--td-tab-track-thickness, 4rpx); +} +.t-tabs__track--right { + right: 0; + top: 0; + width: var(--td-tab-track-thickness, 4rpx); +} +.t-tabs__scroll--top, +.t-tabs__scroll--bottom { + height: 92rpx; + position: relative; + background-color: var(--td-tab-nav-bg-color, #fff); +} +.t-tabs__scroll--top::after, +.t-tabs__scroll--bottom::after { + content: ''; +} +.t-tabs__scroll--top { + border-bottom: solid 1rpx var(--td-tab-border-color, rgba(150, 95, 95, 0.12)); +} +.t-tabs__scroll--left, +.t-tabs__scroll--right { + width: 208rpx; + max-height: 100vh; +} +.t-tabs__content-inner { + display: block; +} +.t-tabs.t-tabs--top, +.t-tabs.t-tabs--bottom { + flex-wrap: wrap; +} +.t-tabs.t-tabs--top .t-tabs__content-inner, +.t-tabs.t-tabs--bottom .t-tabs__content-inner { + position: relative; + width: 100%; + height: 100%; + display: flex; + transition-property: transform; +} +.t-tabs.t-tabs--top .t-tabs__track, +.t-tabs.t-tabs--bottom .t-tabs__track { + left: 0; + bottom: 0; + height: var(--td-tab-track-thickness, 4rpx); +} +.t-tabs.t-tabs--top .t-tabs__content, +.t-tabs.t-tabs--bottom .t-tabs__content { + width: 100%; +} +.t-tabs.t-tabs--bottom { + flex-direction: column-reverse; +} +.t-tabs.t-tabs--left .t-tabs__content-inner, +.t-tabs.t-tabs--right .t-tabs__content-inner { + position: relative; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + transition-property: transform; +} +.t-tabs.t-tabs--left .t-tabs__content, +.t-tabs.t-tabs--right .t-tabs__content { + width: calc(100% - 208rpx); + height: 100%; + position: absolute; + top: 0; + left: 208rpx; + overflow: hidden; +} +.t-tabs.t-tabs--right { + flex-direction: row-reverse; +} diff --git a/components/tabs/type.d.ts b/components/tabs/type.d.ts new file mode 100644 index 0000000..38a3313 --- /dev/null +++ b/components/tabs/type.d.ts @@ -0,0 +1,65 @@ +import { StickyProps } from '../sticky/index'; +export interface TdTabsProps { + animation?: { + type: ObjectConstructor; + value?: TabAnimation; + }; + externalClasses?: { + type: ArrayConstructor; + value?: ['t-class', 't-class-item', 't-class-active', 't-class-track']; + }; + placement?: { + type: StringConstructor; + value?: 'left' | 'top'; + }; + showBottomLine?: { + type: BooleanConstructor; + value?: boolean; + }; + sticky?: { + type: BooleanConstructor; + value?: boolean; + }; + stickyProps?: { + type: ObjectConstructor; + value?: StickyProps; + }; + swipeable?: { + type: BooleanConstructor; + value?: boolean; + }; + value?: { + type: null; + value?: TabValue; + }; + defaultValue?: { + type: null; + value?: TabValue; + }; +} +export interface TdTabPanelProps { + destroyOnHide?: { + type: BooleanConstructor; + value?: boolean; + }; + disabled?: { + type: BooleanConstructor; + value?: boolean; + }; + label?: { + type: StringConstructor; + value?: string; + }; + panel?: { + type: StringConstructor; + value?: string; + }; + value?: { + type: null; + value?: TabValue; + }; +} +export declare type TabAnimation = { + duration: number; +} & Record; +export declare type TabValue = string | number; diff --git a/components/tabs/type.js b/components/tabs/type.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/components/tabs/type.js @@ -0,0 +1 @@ +export {}; diff --git a/pages/foot-tab/foot-tab.json b/pages/foot-tab/foot-tab.json index ec11f61..20ec068 100644 --- a/pages/foot-tab/foot-tab.json +++ b/pages/foot-tab/foot-tab.json @@ -1,7 +1,5 @@ { "component": true, "usingComponents": { - "t-tab-bar": "/components/tab-bar/tab-bar", - "t-tab-bar-item": "/components/tab-bar/tab-bar-item" } } diff --git a/pages/today/index.js b/pages/today/index.js index 51c576b..1d4b6a8 100644 --- a/pages/today/index.js +++ b/pages/today/index.js @@ -1,11 +1,15 @@ // pages/today/index.js +const app = getApp(); Page({ /** * 页面的初始数据 */ data: { - aIconList: ['check-rectangle','star-filled','notification','info-circle'], + aIconList: ['check-rectangle', 'star-filled', 'notification', 'info-circle'], + taskList: [{'taskId':'1','title':'标题德外旗舰店1','note':'2022-11-11'},{'taskId':'2','title':'标题德外旗舰店2','note':'2022-11-11'}], + todayList: [{'taskId':'1','title':'标题德外旗舰店1','note':'2022-11-11'},{'taskId':'2','title':'标题德外旗舰店2','note':'2022-11-11'}], + delayList: [{'taskId':'1','title':'标题德外旗舰店1','note':'2022-11-11'}], }, /** @@ -26,41 +30,30 @@ Page({ * 生命周期函数--监听页面显示 */ onShow() { - + wx.showLoading({ + title: '请稍候...', + mask: true, + }) + console.log('请求中...') + app.$api.taskList({}).then(res =>{ + console.log(res); + console.log(res.data); + this.setData({ + taskList:res.data.taskList, + }) + }) + }, + onEdit(e) { + console.log('收藏' + e.currentTarget.dataset.id); + }, + onDelete(e) { + console.log('删除' + e.currentTarget.dataset.id); + }, + onTabsChange(event) { + console.log(`Change tab, tab-panel value is ${event.detail.value}.`); }, - /** - * 生命周期函数--监听页面隐藏 - */ - onHide() { - + onTabsClick(event) { + console.log(`Click tab, tab-panel value is ${event.detail.value}.`); }, - - /** - * 生命周期函数--监听页面卸载 - */ - onUnload() { - - }, - - /** - * 页面相关事件处理函数--监听用户下拉动作 - */ - onPullDownRefresh() { - - }, - - /** - * 页面上拉触底事件的处理函数 - */ - onReachBottom() { - - }, - - /** - * 用户点击右上角分享 - */ - onShareAppMessage() { - - } }) diff --git a/pages/today/index.wxml b/pages/today/index.wxml index da94a01..ccb029c 100644 --- a/pages/today/index.wxml +++ b/pages/today/index.wxml @@ -1,5 +1,42 @@ -今日任务 + + + + + + 收藏 + 删除 + + + + + + + + 删除 + + + + + + + + 删除 + + + + + + + + + diff --git a/pages/today/index.wxss b/pages/today/index.wxss index acffbb0..602d006 100644 --- a/pages/today/index.wxss +++ b/pages/today/index.wxss @@ -1 +1,52 @@ -/* pages/today/index.wxss */ \ No newline at end of file +/* pages/today/index.wxss */ +.t-swipe-cell-demo-btn-wrapper { + height: 100%; +} + +.t-swipe-cell-demo-btn { + display: inline-flex; + justify-content: center; + align-items: center; + width: 144rpx !important; + height: 100%; + text-align: center; + color: white; +} + +.t-swipe-cell-demo-btn.delete-btn { + background-color: #e34d59; +} + +.t-swipe-cell-demo-btn.edit-btn { + background-color: #ed7b2f; +} + +.t-swipe-cell-demo-btn.favor-btn { + background-color: #0052d9; +} + +.title-image-large { + margin-right: 8rpx; + width: 144rpx; + height: 144rpx; +} + +.text_ath { + text-decoration:line-through +} + +.custom-tabs t-tab-panel { + text-align: center; + justify-content: center; + height: 172rpx; + line-height: 172rpx; + color: rgba(0, 0, 0, 0.26); +} + +.phone-box { + position: absolute; + bottom: 30rpx; + left: 30rpx; + width: 130rpx; + height: 130rpx; +} diff --git a/project.private.config.json b/project.private.config.json index aa2ae36..1e99f25 100644 --- a/project.private.config.json +++ b/project.private.config.json @@ -2,6 +2,7 @@ "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", "projectname": "quinn-wx", "setting": { - "compileHotReLoad": true + "compileHotReLoad": true, + "urlCheck": false } } \ No newline at end of file diff --git a/utils/api.js b/utils/api.js new file mode 100644 index 0000000..5d8b485 --- /dev/null +++ b/utils/api.js @@ -0,0 +1,233 @@ +//testURL +const BASE_URL = 'http://30.251.62.186:10393/mock/68a4cea5-9a96-4c33-b7b9-0f32269ef34f/test?apipost_id=872f12'; +//prodURL +// const BASE_URL = 'https://api.hongyutiyu.top'; + +function buildURL(url, needToken) { + let token = wx.getStorageSync('accessToken'); + + if (!needToken) { + return token ? url + (url.indexOf('?') >= 0 ? '&' : '?') + "access_token=" + token : url + } + return url + (url.indexOf('?') >= 0 ? '&' : '?'); +} + +export function fetchPost(url, params, needToken, multiple) { + url = buildURL(url, needToken); + + // 如果url不存在,返回错误 TODO + // if (!url) { + // return new Promise((resolve, reject) => { + // reject(); + // }) + // } + + console.log("网络请求", BASE_URL + url, params); + + // 如果上传图片 + if (multiple) { + return new Promise((resolve, reject) => { + wx.uploadFile({ + url: BASE_URL + url, + filePath: params.filePath, + name: 'image', + formData: {}, + success: function(response) { + console.log(response.data); + let res = JSON.parse(response.data); + console.log(res); + if (response.statusCode == 200) { + if (res.err_code == 0) { + // wx.hideLoading(); + resolve(res); + } else { + if (res.err_code == 10003 || res.err_code == 10006 || res.err_code == 20006) { + // wx.removeStorageSync('accessToken'); + // wx.redirectTo({ + // url: '/pages/login/index', + // }) + isUnLogin(); + reject(res); + return; + } else if (res.err_code == 20005) { + wx.removeStorageSync('accessToken'); + wx.removeStorageSync('history'); + wx.hideLoading(); + wx.showToast({ + title: res.err_msg, + icon: 'none', + duration: 2000 + }) + reject(res); + } else { + wx.hideLoading(); + wx.showToast({ + title: res.err_msg, + icon: 'none', + duration: 2000 + }) + reject(res); + } + } + } else { + wx.hideLoading(); + wx.showToast({ + title: '网络错误', + icon: 'none', + }) + reject(response); + } + }, + fail: function(err) { + console.log(err); + wx.hideLoading(); + wx.showToast({ + title: '网络错误', + icon: 'none', + }) + reject(response); + }, + }) + }) + } + + // 获取POST数据 + return new Promise((resolve, reject) => { + wx.request({ + url: BASE_URL + url, + data: params, + header: { + 'content-type': 'application/x-www-form-urlencoded' + }, + method: 'POST', + success: function(res) { + console.log("POST返回数据", url,res); + if (res.data.err_code == 0) { + wx.hideLoading(); + resolve(res.data); + } else { + if (res.data.err_code == 10003 || res.data.err_code == 10006 || res.data.err_code == 20006) { + // wx.removeStorageSync('accessToken'); + // wx.redirectTo({ + // url: '/pages/login/index', + // }) + isUnLogin(); + reject(res); + return; + } else if (res.data.err_code == 30022) { + reject(res); + } else if (res.data.err_code == 20005) { + wx.removeStorageSync('accessToken'); + wx.removeStorageSync('history'); + wx.hideLoading(); + wx.showToast({ + title: res.data.err_msg, + icon: 'none', + duration: 2000 + }) + reject(res); + } else { + wx.hideLoading(); + wx.showToast({ + title: res.data.err_msg, + icon: 'none', + duration: 2000 + }) + reject(res); + } + + } + }, + fail: function(res) { + wx.hideLoading(); + wx.showToast({ + title: '网络错误', + icon: 'none', + duration: 2000 + }) + reject(res); + + }, + }) + }) +} + +// get请求 +export function fetchGet(url, params, needToken) { + url = buildURL(url, needToken); + // 如果url不存在,返回错误 TODO + // if (!url) { + // return new Promise((resolve, reject) => { + // reject(); + // }) + // } + + console.log("网络请求", BASE_URL + url, params); + return new Promise((resolve, reject) => { + wx.request({ + url: BASE_URL + url, + data: params, + method: 'GET', + success: function(res) { + console.log("GET返回数据", url, res); + if (res.statusCode == 200) { + if (res.data.err_code == 0) { + wx.hideLoading(); + resolve(res.data); + } else { + if (res.data.err_code == 10003 || res.data.err_code == 10006 || res.data.err_code == 20006) { + isUnLogin(); + reject(res); + return; + } else if (res.data.err_code == 20005) { + wx.removeStorageSync('accessToken'); + wx.removeStorageSync('history'); + wx.hideLoading(); + wx.showToast({ + title: res.data.err_msg, + icon: 'none', + duration: 2000 + }) + reject(res); + } else { + wx.hideLoading(); + wx.showToast({ + title: res.data.err_msg, + icon: 'none', + duration: 2000 + }) + reject(res); + } + } + } else { + wx.hideLoading(); + reject(res) + wx.showToast({ + title: '网络错误', + icon: 'none', + duration: 2000 + }) + } + }, + fail: function(res) { + wx.hideLoading(); + reject(res) + wx.showToast({ + title: '网络错误', + icon: 'none', + duration: 2000 + }) + }, + }) + }) +} + +// 暴露接口 +export default { + /** + * 任务清单 + */ + taskList(params) { + return fetchGet('', params, false); + }, +}