commit c5af079d8c915f0410e818b48d421bf9a6ea27c5 Author: lingxiao865 <1060369102@qq.com> Date: Tue Feb 10 08:05:03 2026 +0800 first commit diff --git a/App.vue b/App.vue new file mode 100644 index 0000000..b555cb1 --- /dev/null +++ b/App.vue @@ -0,0 +1,76 @@ + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..b5d330d --- /dev/null +++ b/index.html @@ -0,0 +1,20 @@ + + + + + + + + + + +
+ + + diff --git a/main.js b/main.js new file mode 100644 index 0000000..6def51e --- /dev/null +++ b/main.js @@ -0,0 +1,24 @@ +import App from './App' + +// #ifndef VUE3 +import Vue from 'vue' +import './uni.promisify.adaptor' +import '@tdesign/uniapp/common/style/theme/index.css'; + +Vue.config.productionTip = false +App.mpType = 'app' +const app = new Vue({ + ...App +}) +app.$mount() +// #endif + +// #ifdef VUE3 +import { createSSRApp } from 'vue' +export function createApp() { + const app = createSSRApp(App) + return { + app + } +} +// #endif \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..0ae6fe3 --- /dev/null +++ b/manifest.json @@ -0,0 +1,80 @@ +{ + "name" : "yu", + "appid" : "__UNI__90009A1", + "description" : "", + "versionName" : "1.0.0", + "versionCode" : "100", + "transformPx" : false, + /* 5+App特有相关 */ + "app-plus" : { + "usingComponents" : true, + "nvueStyleCompiler" : "uni-app", + "compilerVersion" : 3, + "splashscreen" : { + "alwaysShowBeforeRender" : true, + "waiting" : true, + "autoclose" : true, + "delay" : 0 + }, + /* 模块配置 */ + "modules" : {}, + /* 应用发布信息 */ + "distribute" : { + /* android打包配置 */ + "android" : { + "permissions" : [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ] + }, + /* ios打包配置 */ + "ios" : {}, + /* SDK配置 */ + "sdkConfigs" : {} + } + }, + /* 快应用特有相关 */ + "quickapp" : {}, + /* 小程序特有相关 */ + "mp-weixin" : { + "appid" : "wx7677406cd2dfb7ac", + "setting" : { + "urlCheck" : false, + "minified" : true + }, + "usingComponents" : true + }, + "mp-alipay" : { + "usingComponents" : true + }, + "mp-baidu" : { + "usingComponents" : true + }, + "mp-toutiao" : { + "usingComponents" : true + }, + "uniStatistics" : { + "enable" : false + }, + "vueVersion" : "3", + "h5" : { + "optimization" : { + "treeShaking" : { + "enable" : true + } + } + } +} diff --git a/pages.json b/pages.json new file mode 100644 index 0000000..d5aaedd --- /dev/null +++ b/pages.json @@ -0,0 +1,47 @@ +{ + "easycom": { + "autoscan": true, + "custom": { + "^t-(.*)": "@/uni_modules/tdesign-uniapp/components/$1/$1.vue" + } + }, + "pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages + { + "path": "pages/index/index", + "style": { + "navigationBarTitleText": "预约系统" + } + }, + { + "path": "pages/login/login", + "style": { + "navigationBarTitleText": "登录" + } + }, + { + "path": "pages/register/register", + "style": { + "navigationBarTitleText": "注册" + } + }, + { + "path": "pages/booking/booking", + "style": { + "navigationBarTitleText": "我要预约" + } + }, + { + "path": "pages/appointments/appointments", + "style": { + "navigationBarTitleText": "我的预约" + } + } + ], + "globalStyle": { + "navigationBarTextStyle": "white", + "navigationBarTitleText": "预约系统", + "navigationBarBackgroundColor": "#FF7A00", + "backgroundColor": "#F8F8F8" + }, + "uniIdRouter": {} +} diff --git a/pages/appointments/appointments.vue b/pages/appointments/appointments.vue new file mode 100644 index 0000000..14d4872 --- /dev/null +++ b/pages/appointments/appointments.vue @@ -0,0 +1,237 @@ + + + + + \ No newline at end of file diff --git a/pages/booking/booking.vue b/pages/booking/booking.vue new file mode 100644 index 0000000..dd8f397 --- /dev/null +++ b/pages/booking/booking.vue @@ -0,0 +1,533 @@ + + + + + + + diff --git a/pages/index/index.vue b/pages/index/index.vue new file mode 100644 index 0000000..4f4d35e --- /dev/null +++ b/pages/index/index.vue @@ -0,0 +1,183 @@ + + + + + diff --git a/pages/login/login.vue b/pages/login/login.vue new file mode 100644 index 0000000..365c4f9 --- /dev/null +++ b/pages/login/login.vue @@ -0,0 +1,264 @@ + + + + + + + \ No newline at end of file diff --git a/pages/register/register.vue b/pages/register/register.vue new file mode 100644 index 0000000..4bfad29 --- /dev/null +++ b/pages/register/register.vue @@ -0,0 +1,170 @@ + + + + + diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..b21a762 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "node", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "jsx": "preserve", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowJs": true, + "noEmit": true, + "resolveJsonModule": true, + "isolatedModules": true, + "baseUrl": ".", + "paths": { + "@/*": ["*"], + "@tdesign/uniapp/*": ["./uni_modules/tdesign-uniapp/*"] + }, + "types": ["@dcloudio/types"] + }, + "include": ["**/*.ts", "**/*.tsx", "**/*.vue"], + "exclude": ["node_modules", "unpackage"] +} diff --git a/uni.promisify.adaptor.js b/uni.promisify.adaptor.js new file mode 100644 index 0000000..5fec4f3 --- /dev/null +++ b/uni.promisify.adaptor.js @@ -0,0 +1,13 @@ +uni.addInterceptor({ + returnValue (res) { + if (!(!!res && (typeof res === "object" || typeof res === "function") && typeof res.then === "function")) { + return res; + } + return new Promise((resolve, reject) => { + res.then((res) => { + if (!res) return resolve(res) + return res[0] ? reject(res[0]) : resolve(res[1]) + }); + }); + }, +}); \ No newline at end of file diff --git a/uni.scss b/uni.scss new file mode 100644 index 0000000..b9249e9 --- /dev/null +++ b/uni.scss @@ -0,0 +1,76 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ + +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ + +/* 颜色变量 */ + +/* 行为相关颜色 */ +$uni-color-primary: #007aff; +$uni-color-success: #4cd964; +$uni-color-warning: #f0ad4e; +$uni-color-error: #dd524d; + +/* 文字基本颜色 */ +$uni-text-color:#333;//基本色 +$uni-text-color-inverse:#fff;//反色 +$uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 +$uni-text-color-placeholder: #808080; +$uni-text-color-disable:#c0c0c0; + +/* 背景颜色 */ +$uni-bg-color:#ffffff; +$uni-bg-color-grey:#f8f8f8; +$uni-bg-color-hover:#f1f1f1;//点击状态颜色 +$uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 + +/* 边框颜色 */ +$uni-border-color:#c8c7cc; + +/* 尺寸变量 */ + +/* 文字尺寸 */ +$uni-font-size-sm:12px; +$uni-font-size-base:14px; +$uni-font-size-lg:16px; + +/* 图片尺寸 */ +$uni-img-size-sm:20px; +$uni-img-size-base:26px; +$uni-img-size-lg:40px; + +/* Border Radius */ +$uni-border-radius-sm: 2px; +$uni-border-radius-base: 3px; +$uni-border-radius-lg: 6px; +$uni-border-radius-circle: 50%; + +/* 水平间距 */ +$uni-spacing-row-sm: 5px; +$uni-spacing-row-base: 10px; +$uni-spacing-row-lg: 15px; + +/* 垂直间距 */ +$uni-spacing-col-sm: 4px; +$uni-spacing-col-base: 8px; +$uni-spacing-col-lg: 12px; + +/* 透明度 */ +$uni-opacity-disabled: 0.3; // 组件禁用态的透明度 + +/* 文章场景相关 */ +$uni-color-title: #2C405A; // 文章标题颜色 +$uni-font-size-title:20px; +$uni-color-subtitle: #555555; // 二级标题颜色 +$uni-font-size-subtitle:26px; +$uni-color-paragraph: #3F536E; // 文章段落颜色 +$uni-font-size-paragraph:15px; diff --git a/uni_modules/tdesign-uniapp/changelog.md b/uni_modules/tdesign-uniapp/changelog.md new file mode 100644 index 0000000..a963fa8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/changelog.md @@ -0,0 +1,212 @@ +## 0.7.0(2026-02-02) +### Features + +- `ActionSheet`: `item` 属性补充 `description` 字段 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Badge`: + - `shape` 属性新增 `ribbon-right/ribbon-left/triangle-right/triangle-left` 可选项,其中 `ribbon` 与 `ribbon-right` 等效 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 优化 `ribbon` 实现,改用 `background: linear-gradient()`,移除伪元素相关样式 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Calendar`: 新增 `allowSameDay` 属性,允许 `type='range'` 场景的起止时间相同 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Cascader`: + - 支持通过 `keys` 属性定义 `children / disabled` 在 `options` 中对应的字段别名 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 新增 `middle-content` 插槽,用于自定义中间区域内容 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `CollapsePanel`: 新增 `--td-collapse-disabled-color` 和 `--td-collapse-left-icon-color`,用于自定义禁用态颜色和左侧图标颜色 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `ImageViewer`: 新增 `image-props` 属性 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Navbar`: 新增 `placeholder` 属性,默认值为 `false`;新增 `zIndex` 属性,默认值为 `1` @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Picker`: + - 优化性能减少掉帧 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 优化大量数据时列表滚动性能 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - `itemHeight` 默认单位改用 `px`,避免单位转换带来的精度问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 新增 `visibleItemCount` 属性,可自定义可视区域 `PickerItem` 的子项个数 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Popover`: + - 新增 `fixed` API,适用于触发元素为 `fixed` 场景。当触发元素为 `fixed` 时,除了需要显示指定 `fixed` 属性为 `true`,还需在触发元素层添加 `t-popover-wrapper--fixed` 类,用于定位触发元素 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 新增 `--td-popover-[theme]-color` 和 `--td-popover-[theme]-bg-color` 系列 `CSS Vars` @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `QRCode`: 组件新增 `init()`,用于外部调用,重新绘制二维码 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Search`: + - 确保点击清空按钮后,组件内容清空但保持聚焦 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 新增 `cursor-color` 属性 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `SidebarItem`: + - 新增默认插槽,可自定义侧边栏子项内容 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 支持由标签内容撑开高度 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 完善激活项的前缀和后缀元素显示逻辑 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `TabBar`: 新增 `placeholder` 属性,默认值为 `false`;新增 `zIndex` 属性,默认值为 `1` @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + +### Bug Fixes + +- `ActionSheet`: + - 修复左对齐场景下,子项 `border` 左间距错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复 `grid` 主题 + 无 `description` 描述文本场景下,顶部间距错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - `list` 主题最后一项不应设置底边框 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复 `item` 属性的 `disabled` 配置无效 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `BackTop`: 修复文本字重错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Badge`: 修复 `count` 插槽异常 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Calendar`: + - 修复 `value[]` 结合 `swich-mode` 时,初始化错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复翻页按钮状态错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Checkbox`: 修复 `icon` 属性使用 `svg` 资源时在 `iOS` 上不显示 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `CollapsePanel`: + - 修复深色模式下面板右侧图标颜色错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复左侧图标颜色错误,默认主题色,支持使用 `css vars` 自定义 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `ColorPicker`: 修复组件深色模式背景、边框、文本色错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `DateTimePicker`: 修复插槽名重复导致的控制台告警 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `DropdownItem`: 修复在 `iOS 26` 中弹窗定位不准 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Fab`: 修复 `yBounds` 未传值时,控制台报错问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Grid`: 修复 `column` 小于 4 或大于 4 时,文本字号大小错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Message`: 修复 `error` 主题图标错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Picker`: + - 修复 `autoClose` 为 `false` 时,点击遮罩层会重置选项为拨动前选项值的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复平铺模式 `value` 变化未能准确监听 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复 `keys` 动态变更时,子项列表数据不显示 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复 `popupProps.showOverlay` 无效 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Popup`: 修复 `duration` 参数无效的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Progress`: + - 修复深色模式下环形进度条内部背景色错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复环形进度条内部文本间距错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复深色模式下环形进度条内部背景色错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复环形进度条内部文本间距错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Slider`: 修复受控 + 双游标滑块模式下陷入死循环的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `SwipeCell`: 消除 `IntersectionObserver is using slowest path` 警告 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `TabBar`: 修复子项背景色叠加的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Tabs`: 消除 `IntersectionObserver is using slowest path` 警告 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Toast`: + - 修复 `Toast` 嵌套调用时 `close` 回调陷入循环的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复弹窗与遮罩消失不同步的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复圆角样式错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复 `showOverlay` 和 `preventScrollThrough` 均为 `true` 时,遮罩背景色错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `Upload`: + - 修复企业微信/桌面端环境中部分机型无法唤起上传 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复当 `request-method` 返回 `Promise` 时,无法上传的问题 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复 `draggable` 值变换时组件显示错误 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + - 修复拖拽结束后拖拽元素 `zIndex` 异常 @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + +### Others + +- `改用 Font token`,调整部分组件的 CSS Vars @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) +- `--td-xx-icon-font-size` 统一更名为 `--td-xx-icon-size` @novlan1 ([#4201](https://github.com/Tencent/tdesign-miniprogram/pull/4201)) + +## 0.6.3(2026-01-05) +### Features +- `Form`: 支持 `change` 类型 `trigger` @novlan1 ([#143](https://github.com/novlan1/tdesign-uniapp/pull/143)) +- `Image`: 点击事件不禁用冒泡 @novlan1 ([#142](https://github.com/novlan1/tdesign-uniapp/pull/142)) + +## 0.6.2(2025-12-30) +### Bug Fixes +- `DateTimePicker`: 修复滚动日期时其他列向上跳动问题 @novlan1 ([#138](https://github.com/novlan1/tdesign-uniapp/pull/138)) +## 0.6.1(2025-12-29) +### Features +- `Navbar`: 增加 `right` 插槽 @novlan1 ([#136](https://github.com/novlan1/tdesign-uniapp/pull/136)) + +### Bug Fixes +- `Upload`: 修复小程序下图片点击事件不触发问题 @novlan1 ([#136](https://github.com/novlan1/tdesign-uniapp/pull/136)) + +## 0.6.0(2025-12-17) +### Features +- `Popover`: 新增组件 @novlan1 ([#126](https://github.com/novlan1/tdesign-uniapp/pull/126)) +## 0.5.9(2025-12-11) +### Bug Fixes +- `Input`: 修复数据回显问题 @novlan1 ([#121](https://github.com/novlan1/tdesign-uniapp/pull/121)) + + +## 0.5.8(2025-12-01) +#### Features +- `Form`: 新增 `contentAlign` 属性 @novlan1 ([#115](https://github.com/novlan1/tdesign-uniapp/pull/115)) + +#### Bug Fixes +- `SwipeCell`: 修复多个组件共存时的复位问题 @novlan1 ([#114](https://github.com/novlan1/tdesign-uniapp/pull/114)) +- `Form`: 修复 `help/label` 插槽不存在的问题 @novlan1 ([#115](https://github.com/novlan1/tdesign-uniapp/pull/115)) + +## 0.5.7(2025-11-27) +#### Features +- `Textarea`: 支持 `v-model:value` @novlan1 ([#102](https://github.com/novlan1/tdesign-uniapp/pull/102)) +- `Toast`: 函数式调用组件时,支持组件 Dom 预埋在页面下 @novlan1 ([#103](https://github.com/novlan1/tdesign-uniapp/pull/103)) + +#### Bug Fixes +- `Input`: 修复 `clear` 事件的意外冒泡问题 @novlan1 ([#100](https://github.com/novlan1/tdesign-uniapp/pull/100)) +- `Calendar`: 修复 `switchMode` 为 `year-month` 时的编译问题 @novlan1 ([#106](https://github.com/novlan1/tdesign-uniapp/pull/106)) + +## 0.5.6(2025-11-25) +#### Features +- `Popup`: 支持 `v-model:visible` @novlan1 ([#90](https://github.com/novlan1/tdesign-uniapp/pull/90)) +- `Form`: 支持 `validate` 方法传参 @novlan1 ([#90](https://github.com/novlan1/tdesign-uniapp/pull/90)) +- `Input`: 支持 `v-model:value` @novlan1 ([#89](https://github.com/novlan1/tdesign-uniapp/pull/89)) + +#### Bug Fixes +- `Form`: 修复深色模式背景错误问题 @novlan1 ([#91](https://github.com/novlan1/tdesign-uniapp/pull/91)) + +## 0.5.5(2025-11-18) +#### Features +- `Picker`: 支持 `v-model:visible` 语法糖 @novlan1 ([#77](https://github.com/novlan1/tdesign-uniapp/pull/77)) +- `PullDownRefresh`: 修改 `dragstart/dragging/dragend` 事件参数为 `TouchEvent` @novlan1 ([#63](https://github.com/novlan1/tdesign-uniapp/pull/63)) + +#### Bug Fixes +- `Input`: 修复 `readonly` 时点击无效问题 @novlan1 ([#76](https://github.com/novlan1/tdesign-uniapp/pull/76)) +- `PullDownRefresh`: 修复加载中再次点击时触发的 `value` 变化问题 @novlan1 ([#63](https://github.com/novlan1/tdesign-uniapp/pull/63)) +## 0.5.4(2025-11-14) +#### Bug Fixes +- `DropdownMenu`: 修复使用自带导航栏时的高度错误 @novlan1 ([#56](https://github.com/novlan1/tdesign-uniapp/pull/56)) +## 0.5.3.beta-4(2025-11-12) +#### 🐞 Bug Fixes +- `Form`: 修复 `FormItem` 中 `padding` 错误问题 @novlan1 + +#### 🚧 Others +- site: 使用 `history` 模式 @novlan1 ([#35](https://github.com/novlan1/tdesign-uniapp/pull/35)) +- ci: 使用多种 CI @novlan1 ([#42](https://github.com/novlan1/tdesign-uniapp/pull/42)) + +## 0.5.3.beta-3(2025-11-12) +#### Bug Fixes +- `Form`: 修复 `FormItem` 中 `padding` 错误问题 @novlan1 + +#### Others +- site: 使用 `history` 模式 @novlan1 ([#35](https://github.com/novlan1/tdesign-uniapp/pull/35)) +- ci: 使用多种 CI @novlan1 ([#42](https://github.com/novlan1/tdesign-uniapp/pull/42)) + +## 0.5.3.beta-2(2025-11-12) +### Bug Fixes +- `Form`: 修复 `FormItem` 中 `padding` 错误问题 @novlan1 + +### Others +- site: 使用 `history` 模式 @novlan1 ([#35](https://github.com/novlan1/tdesign-uniapp/pull/35)) +- ci: 使用多种 CI @novlan1 ([#42](https://github.com/novlan1/tdesign-uniapp/pull/42)) + +## 0.5.3.beta-1(2025-11-12) +### Bug Fixes +- `Form`: 修复 `FormItem` 中 `padding` 错误问题 @novlan1 +### Others +- site: 使用 `history` 模式 @novlan1 ([#35](https://github.com/novlan1/tdesign-uniapp/pull/35)) +- ci: 使用多种 CI @novlan1 ([#42](https://github.com/novlan1/tdesign-uniapp/pull/42)) + +## 0.5.3(2025-11-12) +🐞 Bug Fixes +- `Form`: 修复 `FormItem` 中 `padding` 错误问题 @novlan1 + +🚧 Others +- site: 使用 `history` 模式 @novlan1 ([#35](https://github.com/novlan1/tdesign-uniapp/pull/35)) +- ci: 使用多种 CI @novlan1 ([#42](https://github.com/novlan1/tdesign-uniapp/pull/42)) + +## 0.5.2(2025-11-10) +- Popup: 修复滚动穿透问题 +- Tabs:修复scroll-view事件参数问题 +## 0.5.1(2025-11-07) +- PullDownRefresh: 支持 successDuration 属性 +## 0.5.0(2025-11-07) +- 支持类型提示 +## 0.4.2(2025-11-01) +- 优化多个组件在支付宝小程序的表现 +## 0.4.0(2025-10-31) +- 支持支付宝小程序 +## 0.3.2(2025-10-30) +- `Calendar`: 修复引入问题 +## 0.3.1(2025-10-27) +- Calendar: 支持 title 和 confirm-btn 的 slot +## 0.3.0(2025-10-25) +- `Guide`: 新增组件 +- `Watermark`: 新增组件 +## 0.2.5(2025-10-22) +- 统一多个组件的事件参数 +## 0.2.3(2025-10-22) +### 🚀 Features + +- `ColorPicker`: 支持 APP-PLUS +- `Collapse`: 支持 APP-PLUS +- `QRCode`: 支持 APP-PLUS +## 0.2.2(2025-10-17) +- Swiper: 新增组件 diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/README.en-US.md b/uni_modules/tdesign-uniapp/components/action-sheet/README.en-US.md new file mode 100644 index 0000000..653c503 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/README.en-US.md @@ -0,0 +1,60 @@ +:: BASE_DOC :: + +## API + +### ActionSheet Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +align | String | center | options: center/left | N +cancel-text | String | - | \- | N +count | Number | 8 | \- | N +description | String | - | \- | N +items | Array | [] | Typescript: `Array` `interface ActionSheetItem { label: string; description?: string; color?: string; disabled?: boolean; icon?: string; suffixIcon?: string }`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/action-sheet/type.ts) | N +popup-props | Object | {} | Typescript: `PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/action-sheet/type.ts) | N +show-cancel | Boolean | true | \- | N +show-overlay | Boolean | true | \- | N +theme | String | list | options: list/grid | N +using-custom-navbar | Boolean | false | \- | N +visible | Boolean | false | `v-model:visible` is supported | N +default-visible | Boolean | false | uncontrolled property | N + +### ActionSheet Events + +name | params | description +-- | -- | -- +cancel | \- | \- +close | `(context: { trigger: ActionSheetTriggerSource })` | [see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/action-sheet/type.ts)。
`type ActionSheetTriggerSource = 'overlay' \| 'command' \| 'select' `
+selected | `(context: { selected: ActionSheetItem \| string, index: number })` | \- + +### ActionSheet Slots + +name | Description +-- | -- +\- | \- + +### ActionSheet External Classes + +className | Description +-- | -- +t-class | \- +t-class-cancel | \- +t-class-content | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-action-sheet-border-color | @component-stroke | - +--td-action-sheet-border-radius | @radius-extraLarge | - +--td-action-sheet-cancel-color | @text-color-primary | - +--td-action-sheet-color | @text-color-primary | - +--td-action-sheet-description-color | @text-color-placeholder | - +--td-action-sheet-description-font | @font-body-medium | - +--td-action-sheet-disabled-color | @text-color-disabled | - +--td-action-sheet-dot-active-color | @brand-color | - +--td-action-sheet-dot-color | @text-color-disabled | - +--td-action-sheet-dot-size | 16rpx | - +--td-action-sheet-gap-color | @bg-color-page | - diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/README.md b/uni_modules/tdesign-uniapp/components/action-sheet/README.md new file mode 100644 index 0000000..7ea4d87 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/README.md @@ -0,0 +1,131 @@ +--- +title: ActionSheet 动作面板 +description: 由用户操作后触发的一种特定的模态弹出框 ,呈现一组与当前情境相关的两个或多个选项。 +spline: data +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TActionSheet from '@tdesign/uniapp/action-sheet/action-sheet.vue'; +``` + + +### 组件类型 + +列表型动作面板 + +{{ list }} + +宫格型动作面板 + +{{ grid }} + +### 组件状态 + +宫格型动作面板 + +{{ status }} + +### 组件样式 + +列表型对齐方式 + +{{ align }} + +### 支持指令调用 + +```javascript +import ActionSheet, { ActionSheetTheme } from '@tdesign/uniapp/action-sheet/index'; + +// 指令调用不同于组件引用不需要传入visible +const basicListOption: ActionSheetShowOption = { + theme: ActionSheetTheme.List, + selector: '#t-action-sheet', + items: [ + { + label: '默认选项', + }, + { + label: '失效选项', + disabled: true, + }, + { + label: '警告选项', + color: '#e34d59', + }, + ], +}; + +const handler = ActionSheet.show(basicListOption); +``` + +指令调用的关闭如下 + +```javascript +handler.close(); +``` + + +## API + +### ActionSheet Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +align | String | center | 水平对齐方式。可选项:center/left | N +cancel-text | String | - | 设置取消按钮的文本 | N +count | Number | 8 | 设置每页展示菜单的数量,仅当 type=grid 时有效 | N +description | String | - | 动作面板描述文字 | N +items | Array | [] | 菜单项。TS 类型:`Array` `interface ActionSheetItem { label: string; description?: string; color?: string; disabled?: boolean; icon?: string; suffixIcon?: string }`。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/action-sheet/type.ts) | N +popup-props | Object | {} | 透传 Popup 组件全部属性。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/action-sheet/type.ts) | N +show-cancel | Boolean | true | 是否显示取消按钮 | N +show-overlay | Boolean | true | 是否显示遮罩层 | N +theme | String | list | 展示类型,列表和表格形式展示。可选项:list/grid | N +using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N +visible | Boolean | false | 显示与隐藏。支持语法糖 `v-model:visible` | N +default-visible | Boolean | false | 显示与隐藏。非受控属性 | N + +### ActionSheet Events + +名称 | 参数 | 描述 +-- | -- | -- +cancel | \- | 点击取消按钮时触发 +close | `(context: { trigger: ActionSheetTriggerSource })` | 关闭时触发。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/action-sheet/type.ts)。
`type ActionSheetTriggerSource = 'overlay' \| 'command' \| 'select' `
+selected | `(context: { selected: ActionSheetItem \| string, index: number })` | 选择菜单项时触发 + +### ActionSheet Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 + +### ActionSheet External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-cancel | 取消样式类 +t-class-content | 内容样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-action-sheet-border-color | @component-stroke | - +--td-action-sheet-border-radius | @radius-extraLarge | - +--td-action-sheet-cancel-color | @text-color-primary | - +--td-action-sheet-color | @text-color-primary | - +--td-action-sheet-description-color | @text-color-placeholder | - +--td-action-sheet-description-font | @font-body-medium | - +--td-action-sheet-disabled-color | @text-color-disabled | - +--td-action-sheet-dot-active-color | @brand-color | - +--td-action-sheet-dot-color | @text-color-disabled | - +--td-action-sheet-dot-size | 16rpx | - +--td-action-sheet-gap-color | @bg-color-page | - diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.css b/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.css new file mode 100644 index 0000000..fb1c05a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.css @@ -0,0 +1,162 @@ +.t-action-sheet__content { + color: var(--td-action-sheet-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + border-top-left-radius: var(--td-action-sheet-border-radius, var(--td-radius-extraLarge, 24rpx)); + border-top-right-radius: var(--td-action-sheet-border-radius, var(--td-radius-extraLarge, 24rpx)); + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); + overflow: hidden; +} +.t-action-sheet__content:focus { + outline: 0; +} +.t-action-sheet--grid { + padding-top: var(--td-spacer, 16rpx); +} +.t-action-sheet--grid .t-action-sheet__description::after { + display: none; +} +.t-action-sheet--left .t-action-sheet__description { + text-align: left; +} +.t-action-sheet--left .t-action-sheet__list-item-content, +.t-action-sheet--left .t-action-sheet__list-item-desc { + justify-content: start; +} +.t-action-sheet--left .t-action-sheet__list-item-icon--suffix { + margin-left: auto; +} +.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, 0.4)))); + font: var(--td-action-sheet-description-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + text-align: center; + padding: var(--td-spacer-1, 24rpx) var(--td-spacer-2, 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-component-stroke, var(--td-gray-color-3, #e7e7e7))); +} +.t-action-sheet__description::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-action-sheet__list-item { + flex-direction: column; + padding: var(--td-spacer-2, 32rpx); + display: flex; + align-items: center; + justify-content: center; +} +.t-action-sheet__list-item:not(:last-child) { + position: relative; +} +.t-action-sheet__list-item:not(:last-child)::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-action-sheet-border-color, var(--td-component-stroke, var(--td-gray-color-3, #e7e7e7))); +} +.t-action-sheet__list-item:not(:last-child)::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-action-sheet__list-item:focus { + outline: 0; +} +.t-action-sheet__list-item--disabled { + color: var(--td-action-sheet-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-action-sheet__list-item-content, +.t-action-sheet__list-item-desc { + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} +.t-action-sheet__list-item-text { + font: var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); +} +.t-action-sheet__list-item-desc { + font: var(--td-font-body-small, 24rpx / 40rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + color: var(--td-action-sheet-description-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + margin-top: var(--td-spacer, 16rpx); +} +.t-action-sheet__list-item-text, +.t-action-sheet__list-item-desc { + word-wrap: normal; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.t-action-sheet__list-item-icon { + font-size: 48rpx; + margin-right: var(--td-spacer, 16rpx); +} +.t-action-sheet__list-item-icon--suffix { + margin-right: 0; + margin-left: var(--td-spacer, 16rpx); +} +.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, #ffffff)); +} +.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-component-stroke, var(--td-gray-color-3, #e7e7e7))); +} +.t-action-sheet__cancel { + padding: var(--td-spacer-1, 24rpx) var(--td-spacer-2, 32rpx); + font: var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + color: var(--td-action-sheet-cancel-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + display: flex; + align-items: center; + justify-content: center; +} +.t-action-sheet__dots { + position: absolute; + left: 50%; + bottom: var(--td-spacer-2, 32rpx); + transform: translateX(-50%); + display: flex; + flex-direction: row; +} +.t-action-sheet__dots-item { + width: var(--td-action-sheet-dot-size, 16rpx); + height: var(--td-action-sheet-dot-size, 16rpx); + background-color: var(--td-action-sheet-dot-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); + border-radius: 50%; + margin: 0 var(--td-spacer, 16rpx); + transition: all 0.4s ease-in; +} +.t-action-sheet__dots-item.t-is-active { + background-color: var(--td-action-sheet-dot-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.vue b/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.vue new file mode 100644 index 0000000..086eeb1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/action-sheet.vue @@ -0,0 +1,350 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/computed.js b/uni_modules/tdesign-uniapp/components/action-sheet/computed.js new file mode 100644 index 0000000..129bfc1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/computed.js @@ -0,0 +1,15 @@ +export const getListThemeItemClass = function (props) { + const { classPrefix } = props; + const { item } = props; + const { prefix } = props; + const classList = [`${classPrefix}__list-item`]; + if (item.disabled) { + classList.push(`${prefix}-is-disabled`); + } + return classList.join(' '); +}; + +export const isImage = function (name) { + return name.indexOf('/') !== -1; +}; + diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/index.d.ts b/uni_modules/tdesign-uniapp/components/action-sheet/index.d.ts new file mode 100644 index 0000000..fdeb9a7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/index.d.ts @@ -0,0 +1,11 @@ +import type { ActionSheetItem, ActionSheetTheme, ActionSheetShowOption } from './show.d.ts'; + +export { ActionSheetItem, ActionSheetTheme, ActionSheetShowOption }; + +declare type Instance = any; + +declare const Handler: { + show(options: ActionSheetShowOption): Instance; + close(options: ActionSheetShowOption): void; +}; +export default Handler; diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/index.js b/uni_modules/tdesign-uniapp/components/action-sheet/index.js new file mode 100644 index 0000000..1f2fd57 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/index.js @@ -0,0 +1,13 @@ +import { show, close, ActionSheetTheme, actionSheetTheme } from './show'; + +export { ActionSheetTheme, actionSheetTheme }; + +export default { + show(options) { + return show(options); + }, + + close(options) { + return close(options); + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/props.ts b/uni_modules/tdesign-uniapp/components/action-sheet/props.ts new file mode 100644 index 0000000..7cba641 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/props.ts @@ -0,0 +1,86 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdActionSheetProps } from './type'; +export default { + /** 水平对齐方式 */ + align: { + type: String, + default: 'center' as TdActionSheetProps['align'], + validator(val: TdActionSheetProps['align']): boolean { + if (!val) return true; + return ['center', 'left'].includes(val); + }, + }, + /** 设置取消按钮的文本 */ + cancelText: { + type: String, + default: '', + }, + /** 设置每页展示菜单的数量,仅当 type=grid 时有效 */ + count: { + type: Number, + default: 8, + }, + /** 动作面板描述文字 */ + description: { + type: String, + default: '', + }, + /** 菜单项 */ + items: { + type: Array, + default: (): TdActionSheetProps['items'] => [], + }, + /** 透传 Popup 组件全部属性 */ + popupProps: { + type: Object, + default: () => ({}), + }, + /** 是否显示取消按钮 */ + showCancel: { + type: Boolean, + default: true, + }, + /** 是否显示遮罩层 */ + showOverlay: { + type: Boolean, + default: true, + }, + /** 展示类型,列表和表格形式展示 */ + theme: { + type: String, + default: 'list' as TdActionSheetProps['theme'], + validator(val: TdActionSheetProps['theme']): boolean { + if (!val) return true; + return ['list', 'grid'].includes(val); + }, + }, + /** 是否使用了自定义导航栏 */ + usingCustomNavbar: Boolean, + /** 显示与隐藏 */ + visible: { + type: Boolean, + default: undefined, + }, + /** 显示与隐藏,非受控属性 */ + defaultVisible: Boolean, + /** 点击取消按钮时触发 */ + onCancel: { + type: Function, + default: () => ({}), + }, + /** 关闭时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 选择菜单项时触发 */ + onSelected: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/show.d.ts b/uni_modules/tdesign-uniapp/components/action-sheet/show.d.ts new file mode 100644 index 0000000..43c2cd1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/show.d.ts @@ -0,0 +1,28 @@ +import { ActionSheetItem } from './type'; + +export { ActionSheetItem }; +declare type Instance = any; +declare type Context = any; + +export declare enum ActionSheetTheme { + List = 'list', + Grid = 'grid' +} + +interface ActionSheetProps { + align: 'center' | 'left'; + cancelText?: string; + count?: number; + description: string; + items: Array; + showCancel?: boolean; + theme?: ActionSheetTheme; + visible: boolean; + defaultVisible?: boolean; +} +export interface ActionSheetShowOption extends Omit { + context?: Context; + selector?: string; +} +export declare const show: (options: ActionSheetShowOption) => Instance; +export declare const close: (options: ActionSheetShowOption) => void; diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/show.js b/uni_modules/tdesign-uniapp/components/action-sheet/show.js new file mode 100644 index 0000000..8fafac9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/show.js @@ -0,0 +1,33 @@ +import { getInstance } from '../common/utils'; + + +export const ActionSheetTheme = { + List: 'list', + Grid: 'grid', +}; + + +export const actionSheetTheme = { + List: ActionSheetTheme.List, + Grid: ActionSheetTheme.Grid, +} ; + +export const show = function (options) { + const { context, selector = '#t-action-sheet', ...otherOptions } = { ...options }; + const instance = getInstance(context, selector); + if (instance) { + instance.show({ + ...otherOptions, + }); + return instance; + } + console.error('未找到组件,请确认 selector && context 是否正确'); +}; + +export const close = function (options) { + const { context, selector = '#t-action-sheet' } = { ...options }; + const instance = getInstance(context, selector); + if (instance) { + instance.close(); + } +}; diff --git a/uni_modules/tdesign-uniapp/components/action-sheet/type.ts b/uni_modules/tdesign-uniapp/components/action-sheet/type.ts new file mode 100644 index 0000000..49d6b93 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/action-sheet/type.ts @@ -0,0 +1,93 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdPopupProps as PopupProps } from '../popup/type'; + +export interface TdActionSheetProps { + /** + * 水平对齐方式 + * @default center + */ + align?: 'center' | 'left'; + /** + * 设置取消按钮的文本 + * @default '' + */ + cancelText?: string; + /** + * 设置每页展示菜单的数量,仅当 type=grid 时有效 + * @default 8 + */ + count?: number; + /** + * 动作面板描述文字 + * @default '' + */ + description?: string; + /** + * 菜单项 + * @default [] + */ + items?: Array; + /** + * 透传 Popup 组件全部属性 + * @default {} + */ + popupProps?: PopupProps; + /** + * 是否显示取消按钮 + * @default true + */ + showCancel?: boolean; + /** + * 是否显示遮罩层 + * @default true + */ + showOverlay?: boolean; + /** + * 展示类型,列表和表格形式展示 + * @default list + */ + theme?: 'list' | 'grid'; + /** + * 是否使用了自定义导航栏 + * @default false + */ + usingCustomNavbar?: boolean; + /** + * 显示与隐藏 + * @default false + */ + visible?: boolean; + /** + * 显示与隐藏,非受控属性 + * @default false + */ + defaultVisible?: boolean; + /** + * 点击取消按钮时触发 + */ + onCancel?: () => void; + /** + * 关闭时触发 + */ + onClose?: (context: { trigger: ActionSheetTriggerSource }) => void; + /** + * 选择菜单项时触发 + */ + onSelected?: (context: { selected: ActionSheetItem | string; index: number }) => void; +} + +export interface ActionSheetItem { + label: string; + description?: string; + color?: string; + disabled?: boolean; + icon?: string; + suffixIcon?: string; +} + +export type ActionSheetTriggerSource = 'overlay' | 'command' | 'select'; diff --git a/uni_modules/tdesign-uniapp/components/avatar-group/avatar-group.css b/uni_modules/tdesign-uniapp/components/avatar-group/avatar-group.css new file mode 100644 index 0000000..a11e948 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar-group/avatar-group.css @@ -0,0 +1,187 @@ +.t-avatar-group { + display: inline-flex; + flex-wrap: wrap; + align-items: center; +} +.t-avatar-group-offset-left .t-avatar__wrapper, +.t-avatar-group-offset-right .t-avatar__wrapper { + padding: var(--td-avatar-group-line-spacing, 4rpx) 0; +} +.t-avatar-group-offset-left-small, +.t-avatar-group-offset-right-small { + --td-avatar-margin-left: var(--td-avatar-group-margin-left-small, -16rpx); +} +.t-avatar-group-offset-left-medium, +.t-avatar-group-offset-right-medium { + --td-avatar-margin-left: var(--td-avatar-group-margin-left-medium, -16rpx); +} +.t-avatar-group-offset-left-large, +.t-avatar-group-offset-right-large { + --td-avatar-margin-left: var(--td-avatar-group-margin-left-large, -16rpx); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(1) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 1); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(2) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 2); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(3) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 3); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(4) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 4); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(5) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 5); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(6) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 6); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(7) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 7); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(8) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 8); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(9) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 9); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(10) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 10); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(11) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 11); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(12) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 12); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(13) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 13); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(14) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 14); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(15) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 15); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(16) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 16); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(17) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 17); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(18) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 18); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(19) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 19); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(20) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 20); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(21) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 21); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(22) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 22); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(23) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 23); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(24) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 24); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(25) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 25); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(26) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 26); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(27) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 27); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(28) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 28); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(29) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 29); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(30) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 30); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(31) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 31); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(32) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 32); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(33) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 33); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(34) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 34); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(35) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 35); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(36) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 36); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(37) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 37); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(38) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 38); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(39) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 39); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(40) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 40); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(41) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 41); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(42) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 42); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(43) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 43); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(44) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 44); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(45) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 45); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(46) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 46); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(47) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 47); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(48) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 48); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(49) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 49); +} +.t-avatar-group-offset-left .t-avatar__wrapper:nth-child(50) { + z-index: calc(var(--td-avatar-group-init-z-index, 50) - 50); +} +.t-avatar-group__collapse--slot, +.t-avatar-group__collapse--default { + z-index: 0; + font-weight: 600; +} +.t-avatar-group__collapse--slot { + float: left; +} +.t-avatar-group__collapse--slot:not(:empty) + .t-avatar-group__collapse--default { + display: none; + float: left; +} +.t-avatar-group__collapse--slot:empty + .t-avatar-group__collapse--default { + display: block; + float: left; +} diff --git a/uni_modules/tdesign-uniapp/components/avatar-group/avatar-group.vue b/uni_modules/tdesign-uniapp/components/avatar-group/avatar-group.vue new file mode 100644 index 0000000..d32c539 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar-group/avatar-group.vue @@ -0,0 +1,121 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/avatar-group/props.ts b/uni_modules/tdesign-uniapp/components/avatar-group/props.ts new file mode 100644 index 0000000..d20b734 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar-group/props.ts @@ -0,0 +1,44 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdAvatarGroupProps } from './type'; +export default { + /** 图片之间的层叠关系,可选值:左侧图片在上和右侧图片在上 */ + cascading: { + type: String, + default: 'left-up' as TdAvatarGroupProps['cascading'], + validator(val: TdAvatarGroupProps['cascading']): boolean { + if (!val) return true; + return ['left-up', 'right-up'].includes(val); + }, + }, + /** 头像数量超出时,会出现一个头像折叠元素。该元素内容可自定义。默认为 `+N`。示例:`+5`,`...`, `更多` */ + collapseAvatar: { + type: String, + }, + /** 能够同时显示的最多头像数量 */ + max: { + type: Number, + }, + /** 形状。优先级低于 Avatar.shape */ + shape: { + type: String, + validator(val: TdAvatarGroupProps['shape']): boolean { + if (!val) return true; + return ['circle', 'round'].includes(val); + }, + }, + /** 尺寸,示例值:small/medium/large/24px/38px 等。优先级低于 Avatar.size */ + size: { + type: String, + default: '', + }, + /** 点击头像折叠元素触发 */ + onCollapsedItemClick: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/avatar-group/type.ts b/uni_modules/tdesign-uniapp/components/avatar-group/type.ts new file mode 100644 index 0000000..8e91848 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar-group/type.ts @@ -0,0 +1,38 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { ShapeEnum } from '../common/common'; + +export interface TdAvatarGroupProps { + /** + * 图片之间的层叠关系,可选值:左侧图片在上和右侧图片在上 + * @default 'left-up' + */ + cascading?: CascadingValue; + /** + * 头像数量超出时,会出现一个头像折叠元素。该元素内容可自定义。默认为 `+N`。示例:`+5`,`...`, `更多` + */ + collapseAvatar?: string; + /** + * 能够同时显示的最多头像数量 + */ + max?: number; + /** + * 形状。优先级低于 Avatar.shape + */ + shape?: ShapeEnum; + /** + * 尺寸,示例值:small/medium/large/24px/38px 等。优先级低于 Avatar.size + * @default '' + */ + size?: string; + /** + * 点击头像折叠元素触发 + */ + onCollapsedItemClick?: (e: MouseEvent) => void; +} + +export type CascadingValue = 'left-up' | 'right-up'; diff --git a/uni_modules/tdesign-uniapp/components/avatar/README.en-US.md b/uni_modules/tdesign-uniapp/components/avatar/README.en-US.md new file mode 100644 index 0000000..6101e75 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/README.en-US.md @@ -0,0 +1,102 @@ +:: BASE_DOC :: + +## API + +### Avatar Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +alt | String | - | show it when url is not valid | N +badge-props | Object | {} | Typescript:`BadgeProps`,[Badge API Documents](./badge?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/avatar/type.ts) | N +bordered | Boolean | false | \- | N +hide-on-load-failed | Boolean | false | hide image when loading image failed | N +icon | String / Object | - | \- | N +image | String | - | images url | N +image-props | Object | - | Typescript:`ImageProps`,[Image API Documents](./image?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/avatar/type.ts) | N +shape | String | - | shape。options: circle/round。Typescript:`ShapeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +size | String | - | size | N + +### Avatar Events + +name | params | description +-- | -- | -- +error | `(e: Event)` | trigger on image load failed + +### Avatar Slots + +name | Description +-- | -- +\- | \- + +### Avatar External Classes + +className | Description +-- | -- +t-class | \- +t-class-alt | \- +t-class-content | \- +t-class-icon | \- +t-class-image | \- + + +### AvatarGroup Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +cascading | String | 'left-up' | multiple images cascading。options: left-up/right-up。Typescript:`CascadingValue` `type CascadingValue = 'left-up' \| 'right-up'`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/avatar-group/type.ts) | N +collapse-avatar | String | - | \- | N +max | Number | - | \- | N +shape | String | - | shape。options: circle/round。Typescript:`ShapeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +size | String | - | size | N + +### AvatarGroup Events + +name | params | description +-- | -- | -- +collapsed-item-click | `(e: MouseEvent)` | \- + +### AvatarGroup Slots + +name | Description +-- | -- +\- | \- +collapse-avatar | \- + +### AvatarGroup External Classes + +className | Description +-- | -- +t-class | \- +t-class-content | \- +t-class-image | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-avatar-group-init-z-index | @avatar-group-init-zIndex | - +--td-avatar-group-line-spacing | 4rpx | - +--td-avatar-group-margin-left-large | -16rpx | - +--td-avatar-group-margin-left-medium | -16rpx | - +--td-avatar-group-margin-left-small | -16rpx | - +--td-avatar-bg-color | @brand-color-light-active | - +--td-avatar-border-color | #fff | - +--td-avatar-border-width-large | 6rpx | - +--td-avatar-border-width-medium | 4rpx | - +--td-avatar-border-width-small | 2rpx | - +--td-avatar-circle-border-radius | @radius-circle | - +--td-avatar-content-color | @brand-color | - +--td-avatar-icon-large-font-size | 64rpx | - +--td-avatar-icon-medium-font-size | 48rpx | - +--td-avatar-icon-small-font-size | 40rpx | - +--td-avatar-large-width | 128rpx | - +--td-avatar-margin-left | 0 | - +--td-avatar-medium-width | 96rpx | - +--td-avatar-round-border-radius | @radius-default | - +--td-avatar-small-width | 80rpx | - +--td-avatar-text-large-font-size | @font-size-xl | - +--td-avatar-text-medium-font-size | @font-size-m | - +--td-avatar-text-small-font-size | @font-size-base | - diff --git a/uni_modules/tdesign-uniapp/components/avatar/README.md b/uni_modules/tdesign-uniapp/components/avatar/README.md new file mode 100644 index 0000000..897972f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/README.md @@ -0,0 +1,152 @@ +--- +title: Avatar 头像 +description: 用于展示用户头像信息,除了纯展示也可点击进入个人详情等操作。 +spline: data +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TAvatar from '@tdesign/uniapp/avatar/avatar.vue'; +import TAvatarGroup from '@tdesign/uniapp/avatar-group/avatar-group.vue'; +``` + +### 头像类型 + +图片头像 + +{{ image-avatar }} + +字符头像 + +{{ character-avatar }} + +图标头像 + +{{ icon-avatar }} + +徽标头像 + +{{ badge-avatar }} + + +### 组合头像 + +纯展示 + +{{ exhibition }} + +带操作 + +{{ action }} + +### 头像尺寸 + +头像 large/medium/small 尺寸 + +{{ size }} + +## API + +### Avatar Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +alt | String | - | 头像替换文本,仅当图片加载失败时有效 | N +badge-props | Object | {} | 头像右上角提示信息,继承 Badge 组件的全部特性。如:小红点,或者数字。TS 类型:`BadgeProps`,[Badge API Documents](./badge?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/avatar/type.ts) | N +bordered | Boolean | false | 已废弃。是否显示外边框 | N +hide-on-load-failed | Boolean | false | 加载失败时隐藏图片 | N +icon | String / Object | - | 图标。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` | N +image | String | - | 图片地址 | N +image-props | Object | - | 透传至 Image 组件。TS 类型:`ImageProps`,[Image API Documents](./image?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/avatar/type.ts) | N +shape | String | - | 形状。优先级高于 AvatarGroup.shape 。Avatar 单独存在时,默认值为 circle。如果父组件 AvatarGroup 存在,默认值便由 AvatarGroup.shape 决定。可选项:circle/round。TS 类型:`ShapeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +size | String | - | 尺寸,示例值:small/medium/large/24px/38px 等。优先级高于 AvatarGroup.size 。Avatar 单独存在时,默认值为 medium。如果父组件 AvatarGroup 存在,默认值便由 AvatarGroup.size 决定 | N + +### Avatar Events + +名称 | 参数 | 描述 +-- | -- | -- +error | `(e: Event)` | 图片加载失败时触发 + +### Avatar Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 + +### Avatar External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-alt | 替代文本样式类 +t-class-content | 内容样式类 +t-class-icon | 图标样式类 +t-class-image | 图片样式类 + + +### AvatarGroup Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +cascading | String | 'left-up' | 图片之间的层叠关系,可选值:左侧图片在上和右侧图片在上。可选项:left-up/right-up。TS 类型:`CascadingValue` `type CascadingValue = 'left-up' \| 'right-up'`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/avatar-group/type.ts) | N +collapse-avatar | String | - | 头像数量超出时,会出现一个头像折叠元素。该元素内容可自定义。默认为 `+N`。示例:`+5`,`...`, `更多` | N +max | Number | - | 能够同时显示的最多头像数量 | N +shape | String | - | 形状。优先级低于 Avatar.shape。可选项:circle/round。TS 类型:`ShapeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +size | String | - | 尺寸,示例值:small/medium/large/24px/38px 等。优先级低于 Avatar.size | N + +### AvatarGroup Events + +名称 | 参数 | 描述 +-- | -- | -- +collapsed-item-click | `(e: MouseEvent)` | 点击头像折叠元素触发 + +### AvatarGroup Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 +collapse-avatar | 自定义 `collapse-avatar` 显示内容 + +### AvatarGroup External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-content | 内容样式类 +t-class-image | 图片样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-avatar-group-init-z-index | @avatar-group-init-zIndex | - +--td-avatar-group-line-spacing | 4rpx | - +--td-avatar-group-margin-left-large | -16rpx | - +--td-avatar-group-margin-left-medium | -16rpx | - +--td-avatar-group-margin-left-small | -16rpx | - +--td-avatar-bg-color | @brand-color-light-active | - +--td-avatar-border-color | #fff | - +--td-avatar-border-width-large | 6rpx | - +--td-avatar-border-width-medium | 4rpx | - +--td-avatar-border-width-small | 2rpx | - +--td-avatar-circle-border-radius | @radius-circle | - +--td-avatar-content-color | @brand-color | - +--td-avatar-icon-large-font-size | 64rpx | - +--td-avatar-icon-medium-font-size | 48rpx | - +--td-avatar-icon-small-font-size | 40rpx | - +--td-avatar-large-width | 128rpx | - +--td-avatar-margin-left | 0 | - +--td-avatar-medium-width | 96rpx | - +--td-avatar-round-border-radius | @radius-default | - +--td-avatar-small-width | 80rpx | - +--td-avatar-text-large-font-size | @font-size-xl | - +--td-avatar-text-medium-font-size | @font-size-m | - +--td-avatar-text-small-font-size | @font-size-base | - diff --git a/uni_modules/tdesign-uniapp/components/avatar/avatar.css b/uni_modules/tdesign-uniapp/components/avatar/avatar.css new file mode 100644 index 0000000..3718b14 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/avatar.css @@ -0,0 +1,77 @@ +.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__text, +.t-avatar__icon { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} +.t-avatar__text:empty, +.t-avatar__icon: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); +} diff --git a/uni_modules/tdesign-uniapp/components/avatar/avatar.vue b/uni_modules/tdesign-uniapp/components/avatar/avatar.vue new file mode 100644 index 0000000..926f85c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/avatar.vue @@ -0,0 +1,200 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/avatar/computed.js b/uni_modules/tdesign-uniapp/components/avatar/computed.js new file mode 100644 index 0000000..c7da167 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/computed.js @@ -0,0 +1,30 @@ +import { getRegExp } from '../common/runtime/wxs-polyfill'; + +export function getClass(classPrefix, size, shape, bordered) { + const hasPx = (size || '').indexOf('px') > -1; + const borderSize = hasPx ? 'medium' : size; + const classNames = [ + classPrefix, + classPrefix + (shape === 'round' ? '--round' : '--circle'), + bordered ? `${classPrefix}--border ${classPrefix}--border-${borderSize}` : '', + hasPx ? '' : `${classPrefix}--${size}`, + ]; + return classNames.join(' '); +} + +export function getSize(size = 'medium', windowWidth) { + const res = getRegExp('^([0-9]+)(px|rpx)$').exec(size); + + if (res && res.length >= 3) { + let px = res[1]; + if (res[2] === 'rpx') { + px = Math.floor((windowWidth * res[1]) / 750); + } + + return `width:${size};height:${size};font-size:${(px / 8) * 3 + 2}px`; + } +} + +export function getStyles(isShow) { + return isShow ? '' : 'display: none;'; +} diff --git a/uni_modules/tdesign-uniapp/components/avatar/props.ts b/uni_modules/tdesign-uniapp/components/avatar/props.ts new file mode 100644 index 0000000..0d4e6e0 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/props.ts @@ -0,0 +1,54 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdAvatarProps } from './type'; +export default { + /** 头像替换文本,仅当图片加载失败时有效 */ + alt: { + type: String, + default: '', + }, + /** 头像右上角提示信息,继承 Badge 组件的全部特性。如:小红点,或者数字 */ + badgeProps: { + type: Object, + default: () => ({}), + }, + /** 已废弃。是否显示外边框 */ + bordered: Boolean, + /** 加载失败时隐藏图片 */ + hideOnLoadFailed: Boolean, + /** 图标。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` */ + icon: { + type: [String, Object], + }, + /** 图片地址 */ + image: { + type: String, + default: '', + }, + /** 透传至 Image 组件 */ + imageProps: { + type: Object, + }, + /** 形状。优先级高于 AvatarGroup.shape 。Avatar 单独存在时,默认值为 circle。如果父组件 AvatarGroup 存在,默认值便由 AvatarGroup.shape 决定 */ + shape: { + type: String, + validator(val: TdAvatarProps['shape']): boolean { + if (!val) return true; + return ['circle', 'round'].includes(val); + }, + }, + /** 尺寸,示例值:small/medium/large/24px/38px 等。优先级高于 AvatarGroup.size 。Avatar 单独存在时,默认值为 medium。如果父组件 AvatarGroup 存在,默认值便由 AvatarGroup.size 决定 */ + size: { + type: String, + default: '', + }, + /** 图片加载失败时触发 */ + onError: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/avatar/type.ts b/uni_modules/tdesign-uniapp/components/avatar/type.ts new file mode 100644 index 0000000..c7695c0 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/avatar/type.ts @@ -0,0 +1,58 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdBadgeProps as BadgeProps } from '../badge/type'; +import type { TdImageProps as ImageProps } from '../image/type'; +import type { ShapeEnum } from '../common/common'; + +export interface TdAvatarProps { + /** + * 头像替换文本,仅当图片加载失败时有效 + * @default '' + */ + alt?: string; + /** + * 头像右上角提示信息,继承 Badge 组件的全部特性。如:小红点,或者数字 + * @default {} + */ + badgeProps?: BadgeProps; + /** + * 已废弃。是否显示外边框 + * @default false + */ + bordered?: boolean; + /** + * 加载失败时隐藏图片 + * @default false + */ + hideOnLoadFailed?: boolean; + /** + * 图标。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` + */ + icon?: string | object; + /** + * 图片地址 + * @default '' + */ + image?: string; + /** + * 透传至 Image 组件 + */ + imageProps?: ImageProps; + /** + * 形状。优先级高于 AvatarGroup.shape 。Avatar 单独存在时,默认值为 circle。如果父组件 AvatarGroup 存在,默认值便由 AvatarGroup.shape 决定 + */ + shape?: ShapeEnum; + /** + * 尺寸,示例值:small/medium/large/24px/38px 等。优先级高于 AvatarGroup.size 。Avatar 单独存在时,默认值为 medium。如果父组件 AvatarGroup 存在,默认值便由 AvatarGroup.size 决定 + * @default '' + */ + size?: string; + /** + * 图片加载失败时触发 + */ + onError?: (e: Event) => void; +} diff --git a/uni_modules/tdesign-uniapp/components/back-top/README.en-US.md b/uni_modules/tdesign-uniapp/components/back-top/README.en-US.md new file mode 100644 index 0000000..7321518 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/back-top/README.en-US.md @@ -0,0 +1,49 @@ +:: BASE_DOC :: + +## API + +### BackTop Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +fixed | Boolean | true | \- | N +icon | String / Boolean / Object | true | \- | N +scroll-top | Number | 0 | \- | N +text | String | '' | \- | N +theme | String | round | options: round/half-round/round-dark/half-round-dark | N +visibility-height | Number | 200 | \- | N + +### BackTop Events + +name | params | description +-- | -- | -- +to-top | \- | \- + +### BackTop Slots + +name | Description +-- | -- +\- | \- +icon | \- + +### BackTop External Classes + +className | Description +-- | -- +t-class | \- +t-class-icon | \- +t-class-text | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-back-top-half-round-border-radius | @radius-round | - +--td-back-top-round-bg-color | @bg-color-container | - +--td-back-top-round-border-color | @component-border | - +--td-back-top-round-border-radius | @radius-circle | - +--td-back-top-round-color | @text-color-primary | - +--td-back-top-round-dark-bg-color | @gray-color-13 | - +--td-back-top-round-dark-color | @text-color-anti | - diff --git a/uni_modules/tdesign-uniapp/components/back-top/README.md b/uni_modules/tdesign-uniapp/components/back-top/README.md new file mode 100644 index 0000000..5e5f84a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/back-top/README.md @@ -0,0 +1,67 @@ +--- +title: BackTop 返回顶部 +description: 用于当页面过长往下滑动时,帮助用户快速回到页面顶部。 +spline: navigation +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TBackTop from '@tdesign/uniapp/back-top/back-top.vue'; +``` + +### 基础返回顶部 + +{{ base }} + +## API + +### BackTop Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +fixed | Boolean | true | 是否绝对定位固定到屏幕右下方 | N +icon | String / Boolean / Object | true | 图标。值为 `false` 表示不显示图标。不传表示使用默认图标 `'backtop'` | N +scroll-top | Number | 0 | 页面滚动距离 | N +text | String | '' | 文案 | N +theme | String | round | 预设的样式类型。可选项:round/half-round/round-dark/half-round-dark | N +visibility-height | Number | 200 | 滚动高度达到此参数值才出现 | N + +### BackTop Events + +名称 | 参数 | 描述 +-- | -- | -- +to-top | \- | 点击触发 + +### BackTop Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 +icon | 自定义图标内容 + +### BackTop External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-icon | 图标样式类 +t-class-text | 文本样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-back-top-half-round-border-radius | @radius-round | - +--td-back-top-round-bg-color | @bg-color-container | - +--td-back-top-round-border-color | @component-border | - +--td-back-top-round-border-radius | @radius-circle | - +--td-back-top-round-color | @text-color-primary | - +--td-back-top-round-dark-bg-color | @gray-color-13 | - +--td-back-top-round-dark-color | @text-color-anti | - diff --git a/uni_modules/tdesign-uniapp/components/back-top/back-top.css b/uni_modules/tdesign-uniapp/components/back-top/back-top.css new file mode 100644 index 0000000..b2d1c8f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/back-top/back-top.css @@ -0,0 +1,65 @@ +.t-back-top { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background-color: transparent; + overflow: hidden; + box-sizing: border-box; + transition: height 0.2s; + height: auto; +} +.t-back-top--fixed { + position: fixed; + right: var(--td-spacer, 16rpx); + bottom: calc(var(--td-spacer-2, 32rpx) + env(safe-area-inset-bottom)); +} +.t-back-top--round, +.t-back-top--round-dark { + width: 96rpx; + height: 96rpx; + border-radius: var(--td-back-top-round-border-radius, var(--td-radius-circle, 50%)); +} +.t-back-top--round, +.t-back-top--half-round { + color: var(--td-back-top-round-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + border: 1rpx solid var(--td-back-top-round-border-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); + background-color: var(--td-back-top-round-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-back-top--round-dark, +.t-back-top--half-round-dark { + color: var(--td-back-top-round-dark-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); + background-color: var(--td-back-top-round-dark-bg-color, var(--td-gray-color-13, #242424)); +} +.t-back-top--half-round, +.t-back-top--half-round-dark { + width: 120rpx; + height: 80rpx; + border-radius: 0; + border-top-left-radius: var(--td-back-top-half-round-border-radius, var(--td-radius-round, 999px)); + border-bottom-left-radius: var(--td-back-top-half-round-border-radius, var(--td-radius-round, 999px)); + flex-direction: row; + right: 0; +} +.t-back-top__text--round, +.t-back-top__text--round-dark, +.t-back-top__text--half-round, +.t-back-top__text--half-round-dark { + font-weight: 600; + font-size: var(--td-font-size, 20rpx); + line-height: 24rpx; +} +.t-back-top__text--half-round, +.t-back-top__text--half-round-dark { + width: 48rpx; +} +.t-back-top__icon:not(:empty) + .t-back-top__text--half-round, +.t-back-top__icon:not(:empty) + .t-back-top__text--half-round-dark { + margin-left: 8rpx; +} +.t-back-top__icon { + display: flex; + justify-content: center; + align-items: center; + font-size: 44rpx; +} diff --git a/uni_modules/tdesign-uniapp/components/back-top/back-top.vue b/uni_modules/tdesign-uniapp/components/back-top/back-top.vue new file mode 100644 index 0000000..2bd54b7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/back-top/back-top.vue @@ -0,0 +1,122 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/back-top/props.ts b/uni_modules/tdesign-uniapp/components/back-top/props.ts new file mode 100644 index 0000000..3ad55cf --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/back-top/props.ts @@ -0,0 +1,48 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdBackTopProps } from './type'; +export default { + /** 是否绝对定位固定到屏幕右下方 */ + fixed: { + type: Boolean, + default: true, + }, + /** 图标。值为 `false` 表示不显示图标。不传表示使用默认图标 `'backtop'` */ + icon: { + type: [String, Boolean, Object], + default: true as TdBackTopProps['icon'], + }, + /** 页面滚动距离 */ + scrollTop: { + type: Number, + default: 0, + }, + /** 文案 */ + text: { + type: String, + default: '', + }, + /** 预设的样式类型 */ + theme: { + type: String, + default: 'round' as TdBackTopProps['theme'], + validator(val: TdBackTopProps['theme']): boolean { + if (!val) return true; + return ['round', 'half-round', 'round-dark', 'half-round-dark'].includes(val); + }, + }, + /** 滚动高度达到此参数值才出现 */ + visibilityHeight: { + type: Number, + default: 200, + }, + /** 点击触发 */ + onToTop: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/back-top/type.ts b/uni_modules/tdesign-uniapp/components/back-top/type.ts new file mode 100644 index 0000000..8e28115 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/back-top/type.ts @@ -0,0 +1,42 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdBackTopProps { + /** + * 是否绝对定位固定到屏幕右下方 + * @default true + */ + fixed?: boolean; + /** + * 图标。值为 `false` 表示不显示图标。不传表示使用默认图标 `'backtop'` + * @default true + */ + icon?: string | boolean | object; + /** + * 页面滚动距离 + * @default 0 + */ + scrollTop?: number; + /** + * 文案 + * @default '' + */ + text?: string; + /** + * 预设的样式类型 + * @default round + */ + theme?: 'round' | 'half-round' | 'round-dark' | 'half-round-dark'; + /** + * 滚动高度达到此参数值才出现 + * @default 200 + */ + visibilityHeight?: number; + /** + * 点击触发 + */ + onToTop?: () => void; +} diff --git a/uni_modules/tdesign-uniapp/components/badge/README.en-US.md b/uni_modules/tdesign-uniapp/components/badge/README.en-US.md new file mode 100644 index 0000000..9fb489d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/README.en-US.md @@ -0,0 +1,54 @@ +:: BASE_DOC :: + +## API + +### Badge Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +color | String | - | \- | N +content | String | - | \- | N +count | String / Number | 0 | \- | N +dot | Boolean | false | \- | N +max-count | Number | 99 | \- | N +offset | Array | - | Typescript: `Array` | N +shape | String | circle | options: circle/square/bubble/ribbon/ribbon-right/ribbon-left/triangle-right/triangle-left | N +show-zero | Boolean | false | \- | N +size | String | medium | options: medium/large | N + +### Badge Slots + +name | Description +-- | -- +\- | \- +count | \- + +### Badge External Classes + +className | Description +-- | -- +t-class | \- +t-class-content | \- +t-class-count | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-badge-basic-height | 32rpx | - +--td-badge-basic-padding | 8rpx | - +--td-badge-basic-width | 32rpx | - +--td-badge-bg-color | @error-color | - +--td-badge-border-radius | 4rpx | - +--td-badge-bubble-border-radius | 20rpx 20rpx 20rpx 1px | - +--td-badge-content-text-color | @text-color-primary | - +--td-badge-dot-size | 16rpx | - +--td-badge-font | @font-mark-extraSmall | - +--td-badge-large-font | @font-mark-small | - +--td-badge-large-height | 40rpx | - +--td-badge-large-padding | 10rpx | - +--td-badge-text-color | @text-color-anti | - +--td-line-height-mark-extraSmall | 32rpx | - +--td-line-height-mark-small | 40rpx | - diff --git a/uni_modules/tdesign-uniapp/components/badge/README.md b/uni_modules/tdesign-uniapp/components/badge/README.md new file mode 100644 index 0000000..84ed05d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/README.md @@ -0,0 +1,85 @@ +--- +title: Badge 徽标 +description: 用于告知用户,该区域的状态变化或者待处理任务的数量。 +spline: data +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TBadge from '@tdesign/uniapp/badge/badge.vue'; +``` + +### 组件类型 + +{{ base }} + +### 组件样式 + +{{ theme }} + +### 组件尺寸 + +{{ size }} + +## FAQ + +### 如何处理由 ribbon 徽标溢出导致页面出现横向滚动? +角标溢出问题建议从父容器组件处理。如 #3063 ,可以给父容器 `cell` 组件添加 `overflow: hidden`,处理溢出造成页面出现横向滚动的问题。 + +## API + +### Badge Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +color | String | - | 颜色 | N +content | String | - | 徽标内容,示例:`content='自定义内容'`。也可以使用默认插槽定义 | N +count | String / Number | 0 | 徽标右上角内容。可以是数字,也可以是文字。如:'new'/3/99+。特殊:值为空表示使用插槽渲染 | N +dot | Boolean | false | 是否为红点 | N +max-count | Number | 99 | 封顶的数字值 | N +offset | Array | - | 设置状态点的位置偏移,示例:[-10, 20] 或 ['10em', '8rem']。TS 类型:`Array` | N +shape | String | circle | 徽标形状,其中 ribbon 和 ribbon-right 等效。可选项:circle/square/bubble/ribbon/ribbon-right/ribbon-left/triangle-right/triangle-left | N +show-zero | Boolean | false | 当数值为 0 时,是否展示徽标 | N +size | String | medium | 尺寸。可选项:medium/large | N + +### Badge Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 +count | 徽标右上角内容 + +### Badge External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-content | 内容样式类 +t-class-count | 计数样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-badge-basic-height | 32rpx | - +--td-badge-basic-padding | 8rpx | - +--td-badge-basic-width | 32rpx | - +--td-badge-bg-color | @error-color | - +--td-badge-border-radius | 4rpx | - +--td-badge-bubble-border-radius | 20rpx 20rpx 20rpx 1px | - +--td-badge-content-text-color | @text-color-primary | - +--td-badge-dot-size | 16rpx | - +--td-badge-font | @font-mark-extraSmall | - +--td-badge-large-font | @font-mark-small | - +--td-badge-large-height | 40rpx | - +--td-badge-large-padding | 10rpx | - +--td-badge-text-color | @text-color-anti | - +--td-line-height-mark-extraSmall | 32rpx | - +--td-line-height-mark-small | 40rpx | - diff --git a/uni_modules/tdesign-uniapp/components/badge/badge.css b/uni_modules/tdesign-uniapp/components/badge/badge.css new file mode 100644 index 0000000..fd9b162 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/badge.css @@ -0,0 +1,140 @@ +.t-badge { + position: relative; + display: inline-flex; + align-items: start; +} +.t-badge--basic { + z-index: 100; + padding: 0 var(--td-badge-basic-padding, 8rpx); + font: var(--td-badge-font, var(--td-font-mark-extraSmall, 600 20rpx / 32rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-badge-text-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); + background-color: var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + text-align: center; + height: var(--td-badge-basic-height, 32rpx); + border-radius: var(--td-badge-border-radius, 4rpx); +} +.t-badge--dot { + height: var(--td-badge-dot-size, 16rpx); + border-radius: 50%; + min-width: var(--td-badge-dot-size, 16rpx); + padding: 0; +} +.t-badge--count { + min-width: var(--td-badge-basic-width, 32rpx); + white-space: nowrap; + box-sizing: border-box; +} +.t-badge--circle { + border-radius: calc(var(--td-badge-basic-height, 32rpx) / 2); +} +.t-badge__ribbon-outer, +.t-badge__ribbon-right-outer, +.t-badge__triangle-right-outer, +.t-badge__ribbon-left-outer, +.t-badge__triangle-left-outer { + position: absolute; + top: 0; +} +.t-badge__ribbon-outer, +.t-badge__ribbon-right-outer, +.t-badge__triangle-right-outer { + right: 0; +} +.t-badge__ribbon-left-outer, +.t-badge__triangle-left-outer { + left: 0; +} +.t-badge--bubble { + border-radius: var(--td-badge-bubble-border-radius, 20rpx 20rpx 20rpx 1px); +} +.t-badge--ribbon, +.t-badge--ribbon-right, +.t-badge--ribbon-left, +.t-badge--triangle-left, +.t-badge--triangle-right { + width: calc(var(--td-badge-basic-height, 32rpx) * 2); + height: calc(var(--td-badge-basic-height, 32rpx) * 2); + border-radius: 0; + padding: 0; + position: absolute; + top: 0; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; +} +.t-badge--ribbon, +.t-badge--ribbon-right { + background: linear-gradient(45deg, transparent 50%, var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))) 50%, var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))) 85%, transparent 85%); +} +.t-badge--triangle-right { + background: linear-gradient(45deg, transparent 50%, var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))) 50%); +} +.t-badge--ribbon, +.t-badge--ribbon-right, +.t-badge--triangle-right { + right: 0; +} +.t-badge--ribbon .t-badge__count, +.t-badge--ribbon-right .t-badge__count, +.t-badge--triangle-right .t-badge__count { + transform: rotate(45deg) translateY(calc(-1 * var(--td-line-height-mark-extraSmall, 32rpx) / 2 + 1rpx)); +} +.t-badge--ribbon-left { + background: linear-gradient(-45deg, transparent 50%, var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))) 50%, var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))) 85%, transparent 85%); +} +.t-badge--triangle-left { + background: linear-gradient(-45deg, transparent 50%, var(--td-badge-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))) 50%); +} +.t-badge--ribbon-left, +.t-badge--triangle-left { + left: 0; +} +.t-badge--ribbon-left .t-badge__count, +.t-badge--triangle-left .t-badge__count { + transform: rotate(-45deg) translateY(calc(-1 * var(--td-line-height-mark-extraSmall, 32rpx) / 2 + 1rpx)); +} +.t-badge--large { + font: var(--td-badge-large-font, var(--td-font-mark-small, 600 24rpx / 40rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + height: var(--td-badge-large-height, 40rpx); + min-width: var(--td-badge-large-height, 40rpx); + padding: 0 var(--td-badge-large-padding, 10rpx); +} +.t-badge--large.t-badge--circle { + border-radius: calc(var(--td-badge-large-height, 40rpx) / 2); +} +.t-badge--large.t-badge--ribbon, +.t-badge--large.t-badge--ribbon-right, +.t-badge--large.t-badge--ribbon-left, +.t-badge--large.t-badge--triangle-right, +.t-badge--large.t-badge--triangle-left { + width: calc(var(--td-badge-large-height, 40rpx) * 2); + height: calc(var(--td-badge-large-height, 40rpx) * 2); + padding: 0; +} +.t-badge--large.t-badge--ribbon .t-badge__count, +.t-badge--large.t-badge--ribbon-right .t-badge__count, +.t-badge--large.t-badge--triangle-right .t-badge__count { + transform: rotate(45deg) translateY(calc(-1 * var(--td-line-height-mark-small, 40rpx) / 2 + 3rpx)); +} +.t-badge--large.t-badge--ribbon-left .t-badge__count, +.t-badge--large.t-badge--triangle-left .t-badge__count { + transform: rotate(-45deg) translateY(calc(-1 * var(--td-line-height-mark-small, 40rpx) / 2 + 3rpx)); +} +.t-badge__content:not(:empty) + .t-badge--bubble.t-has-count, +.t-badge__content:not(:empty) + .t-badge--circle.t-has-count, +.t-badge__content:not(:empty) + .t-badge--square.t-has-count { + transform-origin: center center; + transform: translate(-50%, -50%); + position: absolute; + top: 0; + left: 100%; +} +.t-badge__content-text { + display: block; + font: var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + color: var(--td-badge-content-text-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-badge__count:empty { + display: none; +} diff --git a/uni_modules/tdesign-uniapp/components/badge/badge.vue b/uni_modules/tdesign-uniapp/components/badge/badge.vue new file mode 100644 index 0000000..2194017 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/badge.vue @@ -0,0 +1,134 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/badge/computed.js b/uni_modules/tdesign-uniapp/components/badge/computed.js new file mode 100644 index 0000000..f4602d4 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/computed.js @@ -0,0 +1,63 @@ +export const getBadgeValue = function (props) { + if (props.dot) { + return ''; + } + // eslint-disable-next-line no-restricted-globals + if (isNaN(props.count) || isNaN(props.maxCount)) { + return props.count; + } + return parseInt(props.count, 10) > props.maxCount ? `${props.maxCount}+` : props.count; +}; + +export const hasUnit = function (unit) { + return ( + unit.indexOf('px') > 0 + || unit.indexOf('rpx') > 0 + || unit.indexOf('em') > 0 + || unit.indexOf('rem') > 0 + || unit.indexOf('%') > 0 + || unit.indexOf('vh') > 0 + || unit.indexOf('vm') > 0 + ); +}; + +export const getBadgeStyles = function (props) { + let styleStr = ''; + if (props.color) { + styleStr += `background:${props.color};`; + } + if (props.offset?.[0]) { + styleStr + += `left: calc(100% + ${hasUnit(props.offset[0].toString()) ? props.offset[0] : `${props.offset[0]}px`});`; + } + if (props.offset?.[1]) { + styleStr += `top:${hasUnit(props.offset[1].toString()) ? props.offset[1] : `${props.offset[1]}px`};`; + } + return styleStr; +}; + + +export const getBadgeInnerClass = function (props) { + const baseClass = props.classPrefix; + const classNames = [ + `${baseClass}--basic`, + props.dot ? `${baseClass}--dot` : '', + `${baseClass}--${props.size}`, + `${baseClass}--${props.shape}`, + !props.dot ? `${baseClass}--count` : '', + ]; + return classNames.join(' '); +}; + +export const isShowBadge = function (props) { + if (props.dot) { + return true; + } + // eslint-disable-next-line no-restricted-globals + if (!props.showZero && !isNaN(props.count) && parseInt(props.count, 10) === 0) { + return false; + } + if (props.count == null) return false; + return true; +}; + diff --git a/uni_modules/tdesign-uniapp/components/badge/props.ts b/uni_modules/tdesign-uniapp/components/badge/props.ts new file mode 100644 index 0000000..885a841 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/props.ts @@ -0,0 +1,55 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdBadgeProps } from './type'; +export default { + /** 颜色 */ + color: { + type: String, + default: '', + }, + /** 徽标内容,示例:`content='自定义内容'`。也可以使用默认插槽定义 */ + content: { + type: String, + default: '', + }, + /** 徽标右上角内容。可以是数字,也可以是文字。如:'new'/3/99+。特殊:值为空表示使用插槽渲染 */ + count: { + type: [String, Number], + default: 0 as TdBadgeProps['count'], + }, + /** 是否为红点 */ + dot: Boolean, + /** 封顶的数字值 */ + maxCount: { + type: Number, + default: 99, + }, + /** 设置状态点的位置偏移,示例:[-10, 20] 或 ['10em', '8rem'] */ + offset: { + type: Array, + }, + /** 徽标形状,其中 ribbon 和 ribbon-right 等效 */ + shape: { + type: String, + default: 'circle' as TdBadgeProps['shape'], + validator(val: TdBadgeProps['shape']): boolean { + if (!val) return true; + return ['circle', 'square', 'bubble', 'ribbon', 'ribbon-right', 'ribbon-left', 'triangle-right', 'triangle-left'].includes(val); + }, + }, + /** 当数值为 0 时,是否展示徽标 */ + showZero: Boolean, + /** 尺寸 */ + size: { + type: String, + default: 'medium' as TdBadgeProps['size'], + validator(val: TdBadgeProps['size']): boolean { + if (!val) return true; + return ['medium', 'large'].includes(val); + }, + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/badge/type.ts b/uni_modules/tdesign-uniapp/components/badge/type.ts new file mode 100644 index 0000000..bceb098 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/badge/type.ts @@ -0,0 +1,60 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdBadgeProps { + /** + * 颜色 + * @default '' + */ + color?: string; + /** + * 徽标内容,示例:`content='自定义内容'`。也可以使用默认插槽定义 + * @default '' + */ + content?: string; + /** + * 徽标右上角内容。可以是数字,也可以是文字。如:'new'/3/99+。特殊:值为空表示使用插槽渲染 + * @default 0 + */ + count?: string | number; + /** + * 是否为红点 + * @default false + */ + dot?: boolean; + /** + * 封顶的数字值 + * @default 99 + */ + maxCount?: number; + /** + * 设置状态点的位置偏移,示例:[-10, 20] 或 ['10em', '8rem'] + */ + offset?: Array; + /** + * 徽标形状,其中 ribbon 和 ribbon-right 等效 + * @default circle + */ + shape?: + | 'circle' + | 'square' + | 'bubble' + | 'ribbon' + | 'ribbon-right' + | 'ribbon-left' + | 'triangle-right' + | 'triangle-left'; + /** + * 当数值为 0 时,是否展示徽标 + * @default false + */ + showZero?: boolean; + /** + * 尺寸 + * @default medium + */ + size?: 'medium' | 'large'; +} diff --git a/uni_modules/tdesign-uniapp/components/button/README.en-US.md b/uni_modules/tdesign-uniapp/components/button/README.en-US.md new file mode 100644 index 0000000..c07c5fb --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/button/README.en-US.md @@ -0,0 +1,238 @@ +--- +title: Button +description: Buttons are used to open a closed-loop task, such as "delete" an object, "buy" an item, etc. +spline: base +isComponent: true +--- + + + + +## Usage + +For global import, configure it in `app.json` in the root directory of the miniprogram. For local import, configure it in `index.json` of the page or component that needs to be imported. + +```json +"usingComponents": { + "t-button": "tdesign-miniprogram/button/button" +} +``` + + +## Code Demo + +### 01 Component Type + +#### Basic Buttons + +{{ base }} + +#### Icon Button + +{{ icon-btn }} + +#### Ghost Button + +{{ ghost-btn }} + +#### Combination Button + +{{ group-btn }} + +#### Banner Button + +{{ block-btn }} + +### 02 Component State + +#### Buttons for different states + +{{ disabled }} + +### 03 Component Style + +#### Different sizes of buttons + +{{ size }} + +#### Different shaped buttons + +{{ shape }} + +#### Different color theme buttons + +{{ theme }} + + + +## API + +### Button Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +app-parameter | String | - | \- | N +block | Boolean | false | make button to be a block-level element | N +content | String | - | button's children elements | N +custom-dataset | String / Number / Boolean / Object / Array | {} | Typescript:`string \| number \| boolean \| object \| Array` | N +disabled | Boolean | undefined | disable the button, make it can not be clicked | N +ghost | Boolean | false | make background-color to be transparent | N +hover-class | String | - | \- | N +hover-start-time | Number | 20 | \- | N +hover-stay-time | Number | 70 | \- | N +hover-stop-propagation | Boolean | false | \- | N +icon | String / Object | - | icon name | N +lang | String | - | message language。options: en/zh_CN/zh_TW | N +loading | Boolean | false | set button to be loading state | N +loading-props | Object | {} | Typescript:`LoadingProps`,[Loading API Documents](./loading?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/button/type.ts) | N +open-type | String | - | open type of button, [Miniprogram Button](https://developers.weixin.qq.com/miniprogram/dev/component/button.html)。options: contact/share/getPhoneNumber/getUserInfo/launchApp/openSetting/feedback/chooseAvatar/agreePrivacyAuthorization | N +phone-number-no-quota-toast | Boolean | true | \- | N +send-message-img | String | 截图 | \- | N +send-message-path | String | 当前分享路径 | \- | N +send-message-title | String | 当前标题 | \- | N +session-from | String | - | \- | N +shape | String | rectangle | button shape。options: rectangle/square/round/circle | N +show-message-card | Boolean | false | \- | N +size | String | medium | a button has four size。options: extra-small/small/medium/large | N +t-id | String | - | id | N +theme | String | default | button theme。options: default/primary/danger/light | N +type | String | - | type of button element, same as formType of Miniprogram。options: submit/reset | N +variant | String | base | variant of button。options: base/outline/dashed/text | N + +### Button Events + +name | params | description +-- | -- | -- +agreeprivacyauthorization | \- | \- +chooseavatar | \- | \- +click | `(e: MouseEvent)` | trigger on click +contact | \- | \- +createliveactivity | \- | \- +error | \- | \- +getphonenumber | \- | \- +getrealtimephonenumber | \- | \- +getuserinfo | \- | \- +launchapp | \- | \- +opensetting | \- | \- + +### Button Slots + +name | Description +-- | -- +\- | \- +content | button's children elements +suffix | \- + +### Button External Classes + +className | Description +-- | -- +t-class | \- +t-class-icon | class name of icon +t-class-loading | class name of loading + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-button-border-radius | @radius-default | - +--td-button-border-width | 4rpx | - +--td-button-danger-active-bg-color | @error-color-active | - +--td-button-danger-active-border-color | @error-color-active | - +--td-button-danger-bg-color | @error-color | - +--td-button-danger-border-color | @error-color | - +--td-button-danger-color | @text-color-anti | - +--td-button-danger-dashed-border-color | @button-danger-dashed-color | - +--td-button-danger-dashed-color | @error-color | - +--td-button-danger-dashed-disabled-color | @button-danger-disabled-color | - +--td-button-danger-disabled-bg | @error-color-3 | - +--td-button-danger-disabled-border-color | @error-color-3 | - +--td-button-danger-disabled-color | @font-white-1 | - +--td-button-danger-outline-active-bg-color | @bg-color-container-active | - +--td-button-danger-outline-active-border-color | @error-color-active | - +--td-button-danger-outline-border-color | @button-danger-outline-color | - +--td-button-danger-outline-color | @error-color | - +--td-button-danger-outline-disabled-color | @error-color-3 | - +--td-button-danger-text-active-bg-color | @bg-color-container-active | - +--td-button-danger-text-color | @error-color | - +--td-button-danger-text-disabled-color | @button-danger-disabled-color | - +--td-button-default-active-bg-color | @bg-color-component-active | - +--td-button-default-active-border-color | @bg-color-component-active | - +--td-button-default-bg-color | @bg-color-component | - +--td-button-default-border-color | @bg-color-component | - +--td-button-default-color | @text-color-primary | - +--td-button-default-disabled-bg | @bg-color-component-disabled | - +--td-button-default-disabled-border-color | @bg-color-component-disabled | - +--td-button-default-disabled-color | @text-color-disabled | - +--td-button-default-outline-active-bg-color | @bg-color-container-active | - +--td-button-default-outline-active-border-color | @component-border | - +--td-button-default-outline-border-color | @component-border | - +--td-button-default-outline-color | @text-color-primary | - +--td-button-default-outline-disabled-color | @component-border | - +--td-button-default-text-active-bg-color | @bg-color-container-active | - +--td-button-extra-small-font-size | @font-size-base | - +--td-button-extra-small-height | 56rpx | - +--td-button-extra-small-icon-size | 36rpx | - +--td-button-extra-small-padding-horizontal | 16rpx | - +--td-button-font-weight | 600 | - +--td-button-ghost-border-color | @button-ghost-color | - +--td-button-ghost-color | @text-color-anti | - +--td-button-ghost-danger-border-color | @error-color | - +--td-button-ghost-danger-color | @error-color | - +--td-button-ghost-danger-hover-color | @error-color-active | - +--td-button-ghost-disabled-color | @font-white-4 | - +--td-button-ghost-hover-color | @font-white-2 | - +--td-button-ghost-primary-border-color | @brand-color | - +--td-button-ghost-primary-color | @brand-color | - +--td-button-ghost-primary-hover-color | @brand-color-active | - +--td-button-icon-border-radius | 8rpx | - +--td-button-icon-spacer | @spacer | - +--td-button-large-font-size | @font-size-m | - +--td-button-large-height | 96rpx | - +--td-button-large-icon-size | 48rpx | - +--td-button-large-padding-horizontal | 40rpx | - +--td-button-light-active-bg-color | @brand-color-light-active | - +--td-button-light-active-border-color | @brand-color-light-active | - +--td-button-light-bg-color | @brand-color-light | - +--td-button-light-border-color | @brand-color-light | - +--td-button-light-color | @brand-color | - +--td-button-light-disabled-bg | @brand-color-light | - +--td-button-light-disabled-border-color | @brand-color-light | - +--td-button-light-disabled-color | @brand-color-disabled | - +--td-button-light-outline-active-bg-color | @brand-color-light-active | - +--td-button-light-outline-active-border-color | @brand-color-active | - +--td-button-light-outline-bg-color | @brand-color-light | - +--td-button-light-outline-border-color | @button-light-outline-color | - +--td-button-light-outline-color | @brand-color | - +--td-button-light-outline-disabled-color | @brand-color-disabled | - +--td-button-light-text-active-bg-color | @bg-color-container-active | - +--td-button-light-text-color | @brand-color | - +--td-button-medium-font-size | @font-size-m | - +--td-button-medium-height | 80rpx | - +--td-button-medium-icon-size | 40rpx | - +--td-button-medium-padding-horizontal | 32rpx | - +--td-button-primary-active-bg-color | @brand-color-active | - +--td-button-primary-active-border-color | @brand-color-active | - +--td-button-primary-bg-color | @brand-color | - +--td-button-primary-border-color | @brand-color | - +--td-button-primary-color | @text-color-anti | - +--td-button-primary-dashed-border-color | @button-primary-dashed-color | - +--td-button-primary-dashed-color | @brand-color | - +--td-button-primary-dashed-disabled-color | @brand-color-disabled | - +--td-button-primary-disabled-bg | @brand-color-disabled | - +--td-button-primary-disabled-border-color | @brand-color-disabled | - +--td-button-primary-disabled-color | @text-color-anti | - +--td-button-primary-outline-active-bg-color | @bg-color-container-active | - +--td-button-primary-outline-active-border-color | @brand-color-active | - +--td-button-primary-outline-border-color | @button-primary-outline-color | - +--td-button-primary-outline-color | @brand-color | - +--td-button-primary-outline-disabled-color | @brand-color-disabled | - +--td-button-primary-text-active-bg-color | @bg-color-container-active | - +--td-button-primary-text-color | @brand-color | - +--td-button-primary-text-disabled-color | @brand-color-disabled | - +--td-button-small-font-size | @font-size-base | - +--td-button-small-height | 64rpx | - +--td-button-small-icon-size | 36rpx | - +--td-button-small-padding-horizontal | 24rpx | - diff --git a/uni_modules/tdesign-uniapp/components/button/README.md b/uni_modules/tdesign-uniapp/components/button/README.md new file mode 100644 index 0000000..7cabd4f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/button/README.md @@ -0,0 +1,229 @@ +--- +title: Button 按钮 +description: 用于开启一个闭环的操作任务,如“删除”对象、“购买”商品等。 +spline: base +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TButton from '@tdesign/uniapp/button/button.vue'; +``` + +### 01 组件类型 + +#### 基础按钮 + +{{ base }} + +#### 图标按钮 + +{{ icon-btn }} + +#### 幽灵按钮 + +{{ ghost-btn }} + +#### 组合按钮 + +{{ group-btn }} + +#### 通栏按钮 + +{{ block-btn }} + +### 02 组件状态 + +#### 按钮禁用态 + +{{ disabled }} + +### 03 组件样式 + +#### 按钮尺寸 + +{{ size }} + +#### 按钮形状 + +{{ shape }} + +#### 按钮主题 + +{{ theme }} + +## API + +### Button Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +app-parameter | String | - | 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 | N +block | Boolean | false | 是否为块级元素 | N +content | String | - | 按钮内容 | N +custom-dataset | String / Number / Boolean / Object / Array | {} | 自定义 dataset,可通过 event.currentTarget.dataset.custom 获取。TS 类型:`string \| number \| boolean \| object \| Array` | N +disabled | Boolean | undefined | 禁用状态。优先级:Button.disabled > Form.disabled | N +ghost | Boolean | false | 是否为幽灵按钮(镂空按钮) | N +hover-class | String | - | 指定按钮按下去的样式类,按钮不为加载或禁用状态时有效。当 `hover-class="none"` 时,没有点击态效果 | N +hover-start-time | Number | 20 | 按住后多久出现点击态,单位毫秒 | N +hover-stay-time | Number | 70 | 手指松开后点击态保留时间,单位毫秒 | N +hover-stop-propagation | Boolean | false | 指定是否阻止本节点的祖先节点出现点击态 | N +icon | String / Object | - | 图标名称。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` | N +lang | String | - | 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。
具体释义:
`en` 英文;
`zh_CN` 简体中文;
`zh_TW` 繁体中文。
[小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/button.html)。可选项:en/zh_CN/zh_TW | N +loading | Boolean | false | 是否显示为加载状态 | N +loading-props | Object | {} | 透传 Loading 组件全部属性。TS 类型:`LoadingProps`,[Loading API Documents](./loading?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/button/type.ts) | N +open-type | String | - | 微信开放能力。
具体释义:
`contact` 打开客服会话,如果用户在会话中点击消息卡片后返回小程序,可以从 bindcontact 回调中获得具体信息,具体说明 (*鸿蒙 OS 暂不支持*);
`liveActivity` 通过前端获取新的一次性订阅消息下发机制使用的 code;
`share` 触发用户转发,使用前建议先阅读使用指引
`getPhoneNumber` 获取用户手机号,可以从 bindgetphonenumber 回调中获取到用户信息,具体说明 (*小程序插件中不能使用*);
`getUserInfo` 获取用户信息,可以从 bindgetuserinfo 回调中获取到用户信息 (*小程序插件中不能使用*);
`launchApp` 打开APP,可以通过 app-parameter 属性设定向 APP 传的参数具体说明
`openSetting` 打开授权设置页;
`feedback` 打开“意见反馈”页面,用户可提交反馈内容并上传日志,开发者可以登录小程序管理后台后进入左侧菜单“客服反馈”页面获取到反馈内容;
`chooseAvatar` 获取用户头像,可以从 bindchooseavatar 回调中获取到头像信息;
`agreePrivacyAuthorization`用户同意隐私协议按钮。用户点击一次此按钮后,所有隐私接口可以正常调用。可通过`bindagreeprivacyauthorization`监听用户同意隐私协议事件。隐私合规开发指南详情可见《小程序隐私协议开发指南》。
[小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/button.html)。可选项:contact/share/getPhoneNumber/getUserInfo/launchApp/openSetting/feedback/chooseAvatar/agreePrivacyAuthorization | N +phone-number-no-quota-toast | Boolean | true | 原生按钮属性,当手机号快速验证或手机号实时验证额度用尽时,是否对用户展示“申请获取你的手机号,但该功能使用次数已达当前小程序上限,暂时无法使用”的提示,默认展示,open-type="getPhoneNumber" 或 open-type="getRealtimePhoneNumber" 时有效 | N +send-message-img | String | 截图 | 会话内消息卡片图片,open-type="contact"时有效 | N +send-message-path | String | 当前分享路径 | 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 | N +send-message-title | String | 当前标题 | 会话内消息卡片标题,open-type="contact"时有效 | N +session-from | String | - | 会话来源,open-type="contact"时有效 | N +shape | String | rectangle | 按钮形状,有 4 种:长方形、正方形、圆角长方形、圆形。可选项:rectangle/square/round/circle | N +show-message-card | Boolean | false | 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,open-type="contact"时有效 | N +size | String | medium | 组件尺寸。可选项:extra-small/small/medium/large | N +t-id | String | - | 按钮标签id | N +theme | String | default | 组件风格,依次为品牌色、危险色。可选项:default/primary/danger/light | N +type | String | - | 同小程序的 formType。可选项:submit/reset | N +variant | String | base | 按钮形式,基础、线框、虚线、文字。可选项:base/outline/dashed/text | N + +### Button Events + +名称 | 参数 | 描述 +-- | -- | -- +agreeprivacyauthorization | \- | 原生按钮属性,用户同意隐私协议事件回调,open-type=agreePrivacyAuthorization时有效 (Tips: 如果使用 onNeedPrivacyAuthorization 接口,需要在 bindagreeprivacyauthorization 触发后再调用 resolve({ event: "agree", buttonId })) +chooseavatar | \- | 原生按钮属性,获取用户头像回调,`open-type=chooseAvatar` 时有效。返回 `e.detail.avatarUrl` 为头像临时文件链接 +click | `(e: MouseEvent)` | 点击时触发 +contact | \- | 原生按钮属性,客服消息回调,`open-type="contact"` 时有效 +createliveactivity | \- | 新的一次性订阅消息下发机制回调,`open-type=liveActivity` 时有效 +error | \- | 原生按钮属性,当使用开放能力时,发生错误的回调,`open-type=launchApp` 时有效 +getphonenumber | \- | 原生按钮属性,手机号快速验证回调,open-type=getPhoneNumber时有效。Tips:在触发 bindgetphonenumber 回调后应立即隐藏手机号按钮组件,或置为 disabled 状态,避免用户重复授权手机号产生额外费用 +getrealtimephonenumber | \- | 原生按钮属性,手机号实时验证回调,open-type=getRealtimePhoneNumber 时有效。Tips:在触发 bindgetrealtimephonenumber 回调后应立即隐藏手机号按钮组件,或置为 disabled 状态,避免用户重复授权手机号产生额外费用 +getuserinfo | \- | 原生按钮属性,用户点击该按钮时,会返回获取到的用户信息,回调的detail数据与wx.getUserInfo返回的一致,open-type="getUserInfo"时有效 +launchapp | \- | 打开 APP 成功的回调,`open-type=launchApp` 时有效 +opensetting | \- | 原生按钮属性,在打开授权设置页后回调,open-type=openSetting时有效 + +### Button Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,作用同 `content` 插槽 +content | 自定义 `content` 显示内容 +suffix | 右侧内容,可用于定义右侧图标 + +### Button External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-icon | 图标样式类 +t-class-loading | 加载样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-button-border-radius | @radius-default | - +--td-button-border-width | 4rpx | - +--td-button-danger-active-bg-color | @error-color-active | - +--td-button-danger-active-border-color | @error-color-active | - +--td-button-danger-bg-color | @error-color | - +--td-button-danger-border-color | @error-color | - +--td-button-danger-color | @text-color-anti | - +--td-button-danger-dashed-border-color | @button-danger-dashed-color | - +--td-button-danger-dashed-color | @error-color | - +--td-button-danger-dashed-disabled-color | @button-danger-disabled-color | - +--td-button-danger-disabled-bg | @error-color-3 | - +--td-button-danger-disabled-border-color | @error-color-3 | - +--td-button-danger-disabled-color | @font-white-1 | - +--td-button-danger-outline-active-bg-color | @bg-color-container-active | - +--td-button-danger-outline-active-border-color | @error-color-active | - +--td-button-danger-outline-border-color | @button-danger-outline-color | - +--td-button-danger-outline-color | @error-color | - +--td-button-danger-outline-disabled-color | @error-color-3 | - +--td-button-danger-text-active-bg-color | @bg-color-container-active | - +--td-button-danger-text-color | @error-color | - +--td-button-danger-text-disabled-color | @button-danger-disabled-color | - +--td-button-default-active-bg-color | @bg-color-component-active | - +--td-button-default-active-border-color | @bg-color-component-active | - +--td-button-default-bg-color | @bg-color-component | - +--td-button-default-border-color | @bg-color-component | - +--td-button-default-color | @text-color-primary | - +--td-button-default-disabled-bg | @bg-color-component-disabled | - +--td-button-default-disabled-border-color | @bg-color-component-disabled | - +--td-button-default-disabled-color | @text-color-disabled | - +--td-button-default-outline-active-bg-color | @bg-color-container-active | - +--td-button-default-outline-active-border-color | @component-border | - +--td-button-default-outline-border-color | @component-border | - +--td-button-default-outline-color | @text-color-primary | - +--td-button-default-outline-disabled-color | @component-border | - +--td-button-default-text-active-bg-color | @bg-color-container-active | - +--td-button-extra-small-font-size | @font-size-base | - +--td-button-extra-small-height | 56rpx | - +--td-button-extra-small-icon-size | 36rpx | - +--td-button-extra-small-padding-horizontal | 16rpx | - +--td-button-font-weight | 600 | - +--td-button-ghost-border-color | @button-ghost-color | - +--td-button-ghost-color | @text-color-anti | - +--td-button-ghost-danger-border-color | @error-color | - +--td-button-ghost-danger-color | @error-color | - +--td-button-ghost-danger-hover-color | @error-color-active | - +--td-button-ghost-disabled-color | @font-white-4 | - +--td-button-ghost-hover-color | @font-white-2 | - +--td-button-ghost-primary-border-color | @brand-color | - +--td-button-ghost-primary-color | @brand-color | - +--td-button-ghost-primary-hover-color | @brand-color-active | - +--td-button-icon-border-radius | 8rpx | - +--td-button-icon-spacer | @spacer | - +--td-button-large-font-size | @font-size-m | - +--td-button-large-height | 96rpx | - +--td-button-large-icon-size | 48rpx | - +--td-button-large-padding-horizontal | 40rpx | - +--td-button-light-active-bg-color | @brand-color-light-active | - +--td-button-light-active-border-color | @brand-color-light-active | - +--td-button-light-bg-color | @brand-color-light | - +--td-button-light-border-color | @brand-color-light | - +--td-button-light-color | @brand-color | - +--td-button-light-disabled-bg | @brand-color-light | - +--td-button-light-disabled-border-color | @brand-color-light | - +--td-button-light-disabled-color | @brand-color-disabled | - +--td-button-light-outline-active-bg-color | @brand-color-light-active | - +--td-button-light-outline-active-border-color | @brand-color-active | - +--td-button-light-outline-bg-color | @brand-color-light | - +--td-button-light-outline-border-color | @button-light-outline-color | - +--td-button-light-outline-color | @brand-color | - +--td-button-light-outline-disabled-color | @brand-color-disabled | - +--td-button-light-text-active-bg-color | @bg-color-container-active | - +--td-button-light-text-color | @brand-color | - +--td-button-medium-font-size | @font-size-m | - +--td-button-medium-height | 80rpx | - +--td-button-medium-icon-size | 40rpx | - +--td-button-medium-padding-horizontal | 32rpx | - +--td-button-primary-active-bg-color | @brand-color-active | - +--td-button-primary-active-border-color | @brand-color-active | - +--td-button-primary-bg-color | @brand-color | - +--td-button-primary-border-color | @brand-color | - +--td-button-primary-color | @text-color-anti | - +--td-button-primary-dashed-border-color | @button-primary-dashed-color | - +--td-button-primary-dashed-color | @brand-color | - +--td-button-primary-dashed-disabled-color | @brand-color-disabled | - +--td-button-primary-disabled-bg | @brand-color-disabled | - +--td-button-primary-disabled-border-color | @brand-color-disabled | - +--td-button-primary-disabled-color | @text-color-anti | - +--td-button-primary-outline-active-bg-color | @bg-color-container-active | - +--td-button-primary-outline-active-border-color | @brand-color-active | - +--td-button-primary-outline-border-color | @button-primary-outline-color | - +--td-button-primary-outline-color | @brand-color | - +--td-button-primary-outline-disabled-color | @brand-color-disabled | - +--td-button-primary-text-active-bg-color | @bg-color-container-active | - +--td-button-primary-text-color | @brand-color | - +--td-button-primary-text-disabled-color | @brand-color-disabled | - +--td-button-small-font-size | @font-size-base | - +--td-button-small-height | 64rpx | - +--td-button-small-icon-size | 36rpx | - +--td-button-small-padding-horizontal | 24rpx | - diff --git a/uni_modules/tdesign-uniapp/components/button/button.css b/uni_modules/tdesign-uniapp/components/button/button.css new file mode 100644 index 0000000..7c566fb --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/button/button.css @@ -0,0 +1,514 @@ +.t-button--size-extra-small { + font-size: var(--td-button-extra-small-font-size, var(--td-font-size-base, 28rpx)); + padding-left: var(--td-button-extra-small-padding-horizontal, 16rpx); + padding-right: var(--td-button-extra-small-padding-horizontal, 16rpx); + height: var(--td-button-extra-small-height, 56rpx); + line-height: var(--td-button-extra-small-height, 56rpx); +} +.t-button--size-extra-small .t-button__icon { + font-size: var(--td-button-extra-small-icon-size, 36rpx); +} +.t-button--size-small { + font-size: var(--td-button-small-font-size, var(--td-font-size-base, 28rpx)); + padding-left: var(--td-button-small-padding-horizontal, 24rpx); + padding-right: var(--td-button-small-padding-horizontal, 24rpx); + height: var(--td-button-small-height, 64rpx); + line-height: var(--td-button-small-height, 64rpx); +} +.t-button--size-small .t-button__icon { + font-size: var(--td-button-small-icon-size, 36rpx); +} +.t-button--size-medium { + font-size: var(--td-button-medium-font-size, var(--td-font-size-m, 32rpx)); + padding-left: var(--td-button-medium-padding-horizontal, 32rpx); + padding-right: var(--td-button-medium-padding-horizontal, 32rpx); + height: var(--td-button-medium-height, 80rpx); + line-height: var(--td-button-medium-height, 80rpx); +} +.t-button--size-medium .t-button__icon { + font-size: var(--td-button-medium-icon-size, 40rpx); +} +.t-button--size-large { + font-size: var(--td-button-large-font-size, var(--td-font-size-m, 32rpx)); + padding-left: var(--td-button-large-padding-horizontal, 40rpx); + padding-right: var(--td-button-large-padding-horizontal, 40rpx); + height: var(--td-button-large-height, 96rpx); + line-height: var(--td-button-large-height, 96rpx); +} +.t-button--size-large .t-button__icon { + font-size: var(--td-button-large-icon-size, 48rpx); +} +.t-button--default { + color: var(--td-button-default-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + background-color: var(--td-button-default-bg-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-button-default-border-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); +} +.t-button--default::after { + border-width: var(--td-button-border-width, 4rpx); + border-color: var(--td-button-default-border-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); +} +.t-button--default.t-button--hover { + z-index: 0; +} +.t-button--default.t-button--hover, +.t-button--default.t-button--hover::after { + background-color: var(--td-button-default-active-bg-color, var(--td-bg-color-component-active, var(--td-gray-color-6, #a6a6a6))); + border-color: var(--td-button-default-active-border-color, var(--td-bg-color-component-active, var(--td-gray-color-6, #a6a6a6))); +} +.t-button--default.t-button--disabled { + color: var(--td-button-default-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); + background-color: var(--td-button-default-disabled-bg, var(--td-bg-color-component-disabled, var(--td-gray-color-2, #eeeeee))); +} +.t-button--default.t-button--disabled, +.t-button--default.t-button--disabled::after { + border-color: var(--td-button-default-disabled-border-color, var(--td-bg-color-component-disabled, var(--td-gray-color-2, #eeeeee))); +} +.t-button--primary { + color: var(--td-button-primary-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); + background-color: var(--td-button-primary-bg-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + border-color: var(--td-button-primary-border-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-button--primary::after { + border-width: var(--td-button-border-width, 4rpx); + border-color: var(--td-button-primary-border-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-button--primary.t-button--hover { + z-index: 0; +} +.t-button--primary.t-button--hover, +.t-button--primary.t-button--hover::after { + background-color: var(--td-button-primary-active-bg-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); + border-color: var(--td-button-primary-active-border-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--primary.t-button--disabled { + color: var(--td-button-primary-disabled-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); + background-color: var(--td-button-primary-disabled-bg, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--primary.t-button--disabled, +.t-button--primary.t-button--disabled::after { + border-color: var(--td-button-primary-disabled-border-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--light { + color: var(--td-button-light-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: var(--td-button-light-bg-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); + border-color: var(--td-button-light-border-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-button--light::after { + border-width: var(--td-button-border-width, 4rpx); + border-color: var(--td-button-light-border-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-button--light.t-button--hover { + z-index: 0; +} +.t-button--light.t-button--hover, +.t-button--light.t-button--hover::after { + background-color: var(--td-button-light-active-bg-color, var(--td-brand-color-light-active, var(--td-primary-color-2, #d9e1ff))); + border-color: var(--td-button-light-active-border-color, var(--td-brand-color-light-active, var(--td-primary-color-2, #d9e1ff))); +} +.t-button--light.t-button--disabled { + color: var(--td-button-light-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); + background-color: var(--td-button-light-disabled-bg, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-button--light.t-button--disabled, +.t-button--light.t-button--disabled::after { + border-color: var(--td-button-light-disabled-border-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-button--danger { + color: var(--td-button-danger-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); + background-color: var(--td-button-danger-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + border-color: var(--td-button-danger-border-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-button--danger::after { + border-width: var(--td-button-border-width, 4rpx); + border-color: var(--td-button-danger-border-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-button--danger.t-button--hover { + z-index: 0; +} +.t-button--danger.t-button--hover, +.t-button--danger.t-button--hover::after { + background-color: var(--td-button-danger-active-bg-color, var(--td-error-color-active, var(--td-error-color-7, #ad352f))); + border-color: var(--td-button-danger-active-border-color, var(--td-error-color-active, var(--td-error-color-7, #ad352f))); +} +.t-button--danger.t-button--disabled { + color: var(--td-button-danger-disabled-color, var(--td-font-white-1, #ffffff)); + background-color: var(--td-button-danger-disabled-bg, var(--td-error-color-3, #ffb9b0)); +} +.t-button--danger.t-button--disabled, +.t-button--danger.t-button--disabled::after { + border-color: var(--td-button-danger-disabled-border-color, var(--td-error-color-3, #ffb9b0)); +} +.t-button { + display: inline-flex; + align-items: center; + justify-content: center; + position: relative; + white-space: nowrap; + text-align: center; + background-image: none; + transition: all 0.3s; + touch-action: manipulation; + border-radius: var(--td-button-border-radius, var(--td-radius-default, 12rpx)); + outline: none; + font-family: PingFang SC, Microsoft YaHei, Arial Regular; + font-weight: var(--td-button-font-weight, 600); + vertical-align: top; + box-sizing: border-box; + cursor: pointer; + -webkit-tap-highlight-color: transparent; + -webkit-user-select: none; + user-select: none; + /* stylelint-disable-next-line */ + -webkit-appearance: none; +} +.t-button::after { + border-radius: calc(var(--td-button-border-radius, var(--td-radius-default, 12rpx)) * 2); +} +.t-button--text { + color: var(--td-button-default-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + background-color: transparent; +} +.t-button--text, +.t-button--text::after { + border: 0; +} +.t-button--text.t-button--hover, +.t-button--text.t-button--hover::after { + background-color: var(--td-button-default-text-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); +} +.t-button--text.t-button--primary { + color: var(--td-button-primary-text-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: transparent; +} +.t-button--text.t-button--primary.t-button--hover, +.t-button--text.t-button--primary.t-button--hover::after { + background-color: var(--td-button-primary-text-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); +} +.t-button--text.t-button--primary.t-button--disabled { + color: var(--td-button-primary-text-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); +} +.t-button--text.t-button--danger { + color: var(--td-button-danger-text-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + background-color: transparent; +} +.t-button--text.t-button--danger.t-button--hover, +.t-button--text.t-button--danger.t-button--hover::after { + background-color: var(--td-button-danger-text-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); +} +.t-button--text.t-button--danger.t-button--disabled { + color: var(--td-button-danger-text-disabled-color, var(--td-button-danger-disabled-color, var(--td-font-white-1, #ffffff))); + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); +} +.t-button--text.t-button--light { + color: var(--td-button-light-text-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: transparent; +} +.t-button--text.t-button--light.t-button--hover, +.t-button--text.t-button--light.t-button--hover::after { + background-color: var(--td-button-light-text-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); +} +.t-button--text.t-button--disabled { + color: var(--td-button-default-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-button--outline { + color: var(--td-button-default-outline-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); +} +.t-button--outline, +.t-button--outline::after { + border-color: var(--td-button-default-outline-border-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); +} +.t-button--outline.t-button--hover, +.t-button--outline.t-button--hover::after { + background-color: var(--td-button-default-outline-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-button-default-outline-active-border-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); +} +.t-button--outline.t-button--disabled { + color: var(--td-button-default-outline-disabled-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); +} +.t-button--outline.t-button--disabled, +.t-button--outline.t-button--disabled::after { + border-color: var(--td-button-default-outline-disabled-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); +} +.t-button--outline.t-button--primary { + color: var(--td-button-primary-outline-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-button--outline.t-button--primary, +.t-button--outline.t-button--primary::after { + border-color: var(--td-button-primary-outline-border-color, var(--td-button-primary-outline-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)))); +} +.t-button--outline.t-button--primary.t-button--hover { + color: var(--td-button-primary-outline-active-border-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--outline.t-button--primary.t-button--hover::after { + background-color: var(--td-button-primary-outline-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-button-primary-outline-active-border-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--outline.t-button--primary.t-button--disabled { + background-color: transparent; + color: var(--td-button-primary-outline-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--outline.t-button--primary.t-button--disabled, +.t-button--outline.t-button--primary.t-button--disabled::after { + border-color: var(--td-button-primary-outline-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--outline.t-button--danger { + color: var(--td-button-danger-outline-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-button--outline.t-button--danger, +.t-button--outline.t-button--danger::after { + border-color: var(--td-button-danger-outline-border-color, var(--td-button-danger-outline-color, var(--td-error-color, var(--td-error-color-6, #d54941)))); +} +.t-button--outline.t-button--danger.t-button--hover { + color: var(--td-button-danger-outline-active-border-color, var(--td-error-color-active, var(--td-error-color-7, #ad352f))); +} +.t-button--outline.t-button--danger.t-button--hover::after { + background-color: var(--td-button-danger-outline-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-button-danger-outline-active-border-color, var(--td-error-color-active, var(--td-error-color-7, #ad352f))); +} +.t-button--outline.t-button--danger.t-button--disabled { + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); + color: var(--td-button-danger-outline-disabled-color, var(--td-error-color-3, #ffb9b0)); +} +.t-button--outline.t-button--danger.t-button--disabled, +.t-button--outline.t-button--danger.t-button--disabled::after { + border-color: var(--td-button-danger-outline-disabled-color, var(--td-error-color-3, #ffb9b0)); +} +.t-button--outline.t-button--light { + color: var(--td-button-light-outline-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: var(--td-button-light-outline-bg-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-button--outline.t-button--light, +.t-button--outline.t-button--light::after { + border-color: var(--td-button-light-outline-border-color, var(--td-button-light-outline-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)))); +} +.t-button--outline.t-button--light.t-button--hover { + color: var(--td-button-light-outline-active-border-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--outline.t-button--light.t-button--hover, +.t-button--outline.t-button--light.t-button--hover::after { + background-color: var(--td-button-light-outline-active-bg-color, var(--td-brand-color-light-active, var(--td-primary-color-2, #d9e1ff))); + border-color: var(--td-button-light-outline-active-border-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--outline.t-button--light.t-button--disabled { + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); + color: var(--td-button-light-outline-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--outline.t-button--light.t-button--disabled, +.t-button--outline.t-button--light.t-button--disabled::after { + border-color: var(--td-button-light-outline-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--dashed { + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); + border-style: dashed; + border-width: 2rpx; +} +.t-button--dashed::after { + border: 0; +} +.t-button--dashed.t-button--hover, +.t-button--dashed.t-button--hover::after { + background-color: var(--td-button-default-outline-active-bg-color, var(--td-bg-color-container-active, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-button-default-outline-active-border-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); +} +.t-button--dashed.t-button--primary { + color: var(--td-button-primary-dashed-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-button--dashed.t-button--primary, +.t-button--dashed.t-button--primary::after { + border-color: var(--td-button-primary-dashed-border-color, var(--td-button-primary-dashed-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)))); +} +.t-button--dashed.t-button--primary.t-button--disabled { + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); + color: var(--td-button-primary-dashed-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--dashed.t-button--primary.t-button--disabled, +.t-button--dashed.t-button--primary.t-button--disabled::after { + border-color: var(--td-button-primary-dashed-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-button--dashed.t-button--danger { + color: var(--td-button-danger-dashed-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-button--dashed.t-button--danger, +.t-button--dashed.t-button--danger::after { + border-color: var(--td-button-danger-dashed-border-color, var(--td-button-danger-dashed-color, var(--td-error-color, var(--td-error-color-6, #d54941)))); +} +.t-button--dashed.t-button--danger.t-button--disabled { + background-color: transparent; + color: var(--td-button-danger-dashed-disabled-color, var(--td-button-danger-disabled-color, var(--td-font-white-1, #ffffff))); +} +.t-button--dashed.t-button--danger.t-button--disabled::after { + border-color: var(--td-button-danger-dashed-disabled-color, var(--td-button-danger-disabled-color, var(--td-font-white-1, #ffffff))); +} +.t-button--ghost { + background-color: transparent; + color: var(--td-button-ghost-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); +} +.t-button--ghost, +.t-button--ghost::after { + border-color: var(--td-button-ghost-border-color, var(--td-button-ghost-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff)))); +} +.t-button--ghost.t-button--default.t-button--hover { + color: var(--td-button-ghost-hover-color, var(--td-font-white-2, rgba(255, 255, 255, 0.55))); +} +.t-button--ghost.t-button--default.t-button--hover, +.t-button--ghost.t-button--default.t-button--hover::after { + background-color: transparent; + border-color: var(--td-button-ghost-hover-color, var(--td-font-white-2, rgba(255, 255, 255, 0.55))); +} +.t-button--ghost.t-button--primary { + color: var(--td-button-ghost-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-button--ghost.t-button--primary, +.t-button--ghost.t-button--primary::after { + border-color: var(--td-button-ghost-primary-border-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-button--ghost.t-button--primary.t-button--hover { + color: var(--td-button-ghost-primary-hover-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--ghost.t-button--primary.t-button--hover, +.t-button--ghost.t-button--primary.t-button--hover::after { + background-color: transparent; + border-color: var(--td-button-ghost-primary-hover-color, var(--td-brand-color-active, var(--td-primary-color-8, #003cab))); +} +.t-button--ghost.t-button--primary.t-button--text.t-button--hover, +.t-button--ghost.t-button--primary.t-button--text.t-button--hover::after { + background-color: var(--td-gray-color-10, #4b4b4b); +} +.t-button--ghost.t-button--primary.t-button--disabled { + background-color: transparent; + color: var(--td-button-ghost-disabled-color, var(--td-font-white-4, rgba(255, 255, 255, 0.22))); +} +.t-button--ghost.t-button--primary.t-button--disabled, +.t-button--ghost.t-button--primary.t-button--disabled::after { + border-color: var(--td-button-ghost-disabled-color, var(--td-font-white-4, rgba(255, 255, 255, 0.22))); +} +.t-button--ghost.t-button--danger { + color: var(--td-button-ghost-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-button--ghost.t-button--danger, +.t-button--ghost.t-button--danger::after { + border-color: var(--td-button-ghost-danger-border-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-button--ghost.t-button--danger.t-button--hover { + color: var(--td-button-ghost-danger-hover-color, var(--td-error-color-active, var(--td-error-color-7, #ad352f))); +} +.t-button--ghost.t-button--danger.t-button--hover, +.t-button--ghost.t-button--danger.t-button--hover::after { + background-color: transparent; + border-color: var(--td-button-ghost-danger-hover-color, var(--td-error-color-active, var(--td-error-color-7, #ad352f))); +} +.t-button--ghost.t-button--danger.t-button--text.t-button--hover, +.t-button--ghost.t-button--danger.t-button--text.t-button--hover::after { + background-color: var(--td-gray-color-10, #4b4b4b); +} +.t-button--ghost.t-button--danger.t-button--disabled { + background-color: transparent; + color: var(--td-button-ghost-disabled-color, var(--td-font-white-4, rgba(255, 255, 255, 0.22))); +} +.t-button--ghost.t-button--danger.t-button--disabled, +.t-button--ghost.t-button--danger.t-button--disabled::after { + border-color: var(--td-button-ghost-disabled-color, var(--td-font-white-4, rgba(255, 255, 255, 0.22))); +} +.t-button--ghost.t-button--default.t-button--text.t-button--hover, +.t-button--ghost.t-button--default.t-button--text.t-button--hover::after { + background-color: var(--td-gray-color-10, #4b4b4b); +} +.t-button--ghost.t-button--default.t-button--disabled { + background-color: transparent; + color: var(--td-button-ghost-disabled-color, var(--td-font-white-4, rgba(255, 255, 255, 0.22))); +} +.t-button--ghost.t-button--default.t-button--disabled, +.t-button--ghost.t-button--default.t-button--disabled::after { + border-color: var(--td-button-ghost-disabled-color, var(--td-font-white-4, rgba(255, 255, 255, 0.22))); +} +.t-button__loading + .t-button__content:not(:empty), +.t-button__icon + .t-button__content:not(:empty) { + margin-left: 8rpx; +} +.t-button__icon { + border-radius: var(--td-button-icon-border-radius, 8rpx); +} +.t-button--round.t-button--size-large { + border-radius: calc(var(--td-button-large-height, 96rpx) / 2); +} +.t-button--round.t-button--size-large::after { + border-radius: var(--td-button-large-height, 96rpx); +} +.t-button--round.t-button--size-medium { + border-radius: calc(var(--td-button-medium-height, 80rpx) / 2); +} +.t-button--round.t-button--size-medium::after { + border-radius: var(--td-button-medium-height, 80rpx); +} +.t-button--round.t-button--size-small { + border-radius: calc(var(--td-button-small-height, 64rpx) / 2); +} +.t-button--round.t-button--size-small::after { + border-radius: var(--td-button-small-height, 64rpx); +} +.t-button--round.t-button--size-extra-small { + border-radius: calc(var(--td-button-extra-small-height, 56rpx) / 2); +} +.t-button--round.t-button--size-extra-small::after { + border-radius: var(--td-button-extra-small-height, 56rpx); +} +.t-button--square { + padding: 0; +} +.t-button--square.t-button--size-large { + width: var(--td-button-large-height, 96rpx); +} +.t-button--square.t-button--size-medium { + width: var(--td-button-medium-height, 80rpx); +} +.t-button--square.t-button--size-small { + width: var(--td-button-small-height, 64rpx); +} +.t-button--square.t-button--size-extra-small { + width: var(--td-button-extra-small-height, 56rpx); +} +.t-button--circle { + padding: 0; + border-radius: 50%; +} +.t-button--circle.t-button--size-large { + width: var(--td-button-large-height, 96rpx); +} +.t-button--circle.t-button--size-large::after { + border-radius: 50%; +} +.t-button--circle.t-button--size-medium { + width: var(--td-button-medium-height, 80rpx); +} +.t-button--circle.t-button--size-medium::after { + border-radius: 50%; +} +.t-button--circle.t-button--size-small { + width: var(--td-button-small-height, 64rpx); +} +.t-button--circle.t-button--size-small::after { + border-radius: 50%; +} +.t-button--circle.t-button--size-extra-small { + width: var(--td-button-extra-small-height, 56rpx); +} +.t-button--circle.t-button--size-extra-small::after { + border-radius: 50%; +} +.t-button--block { + display: flex; + width: 100%; +} +.t-button--disabled { + cursor: not-allowed; +} +.t-button__loading--wrapper { + display: flex; + align-items: center; + justify-content: center; +} +.t-button.t-button--hover::after { + z-index: -1; +} diff --git a/uni_modules/tdesign-uniapp/components/button/button.vue b/uni_modules/tdesign-uniapp/components/button/button.vue new file mode 100644 index 0000000..94a951a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/button/button.vue @@ -0,0 +1,244 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/button/props.ts b/uni_modules/tdesign-uniapp/components/button/props.ts new file mode 100644 index 0000000..c16b053 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/button/props.ts @@ -0,0 +1,207 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdButtonProps } from './type'; +export default { + /** 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 */ + appParameter: { + type: String, + default: '', + }, + /** 是否为块级元素 */ + block: Boolean, + /** 按钮内容 */ + content: { + type: String, + }, + /** 自定义 dataset,可通过 event.currentTarget.dataset.custom 获取 */ + customDataset: { + type: [String, Number, Boolean, Object, Array], + default: () => ({}) as TdButtonProps['customDataset'], + }, + /** 禁用状态。优先级:Button.disabled > Form.disabled */ + disabled: { + type: Boolean, + default: undefined, + }, + /** 是否为幽灵按钮(镂空按钮) */ + ghost: Boolean, + /** 指定按钮按下去的样式类,按钮不为加载或禁用状态时有效。当 `hover-class="none"` 时,没有点击态效果 */ + hoverClass: { + type: String, + default: '', + }, + /** 按住后多久出现点击态,单位毫秒 */ + hoverStartTime: { + type: Number, + default: 20, + }, + /** 手指松开后点击态保留时间,单位毫秒 */ + hoverStayTime: { + type: Number, + default: 70, + }, + /** 指定是否阻止本节点的祖先节点出现点击态 */ + hoverStopPropagation: Boolean, + /** 图标名称。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` */ + icon: { + type: [String, Object], + }, + /** 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。
具体释义:
`en` 英文;
`zh_CN` 简体中文;
`zh_TW` 繁体中文。
[小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) */ + lang: { + type: String, + validator(val: TdButtonProps['lang']): boolean { + if (!val) return true; + return ['en', 'zh_CN', 'zh_TW'].includes(val); + }, + }, + /** 是否显示为加载状态 */ + loading: Boolean, + /** 透传 Loading 组件全部属性 */ + loadingProps: { + type: Object, + default: () => ({}), + }, + /** 微信开放能力。
具体释义:
`contact` 打开客服会话,如果用户在会话中点击消息卡片后返回小程序,可以从 bindcontact 回调中获得具体信息,具体说明 (*鸿蒙 OS 暂不支持*);
`liveActivity` 通过前端获取新的一次性订阅消息下发机制使用的 code;
`share` 触发用户转发,使用前建议先阅读使用指引
`getPhoneNumber` 获取用户手机号,可以从 bindgetphonenumber 回调中获取到用户信息,具体说明 (*小程序插件中不能使用*);
`getUserInfo` 获取用户信息,可以从 bindgetuserinfo 回调中获取到用户信息 (*小程序插件中不能使用*);
`launchApp` 打开APP,可以通过 app-parameter 属性设定向 APP 传的参数具体说明
`openSetting` 打开授权设置页;
`feedback` 打开“意见反馈”页面,用户可提交反馈内容并上传日志,开发者可以登录小程序管理后台后进入左侧菜单“客服反馈”页面获取到反馈内容;
`chooseAvatar` 获取用户头像,可以从 bindchooseavatar 回调中获取到头像信息;
`agreePrivacyAuthorization`用户同意隐私协议按钮。用户点击一次此按钮后,所有隐私接口可以正常调用。可通过`bindagreeprivacyauthorization`监听用户同意隐私协议事件。隐私合规开发指南详情可见《小程序隐私协议开发指南》。
[小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) */ + openType: { + type: String, + validator(val: TdButtonProps['openType']): boolean { + if (!val) return true; + return ['contact', 'share', 'getPhoneNumber', 'getUserInfo', 'launchApp', 'openSetting', 'feedback', 'chooseAvatar', 'agreePrivacyAuthorization'].includes(val); + }, + }, + /** 原生按钮属性,当手机号快速验证或手机号实时验证额度用尽时,是否对用户展示“申请获取你的手机号,但该功能使用次数已达当前小程序上限,暂时无法使用”的提示,默认展示,open-type="getPhoneNumber" 或 open-type="getRealtimePhoneNumber" 时有效 */ + phoneNumberNoQuotaToast: { + type: Boolean, + default: true, + }, + /** 会话内消息卡片图片,open-type="contact"时有效 */ + sendMessageImg: { + type: String, + default: '截图', + }, + /** 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 */ + sendMessagePath: { + type: String, + default: '当前分享路径', + }, + /** 会话内消息卡片标题,open-type="contact"时有效 */ + sendMessageTitle: { + type: String, + default: '当前标题', + }, + /** 会话来源,open-type="contact"时有效 */ + sessionFrom: { + type: String, + default: '', + }, + /** 按钮形状,有 4 种:长方形、正方形、圆角长方形、圆形 */ + shape: { + type: String, + default: 'rectangle' as TdButtonProps['shape'], + validator(val: TdButtonProps['shape']): boolean { + if (!val) return true; + return ['rectangle', 'square', 'round', 'circle'].includes(val); + }, + }, + /** 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,open-type="contact"时有效 */ + showMessageCard: Boolean, + /** 组件尺寸 */ + size: { + type: String, + default: 'medium' as TdButtonProps['size'], + validator(val: TdButtonProps['size']): boolean { + if (!val) return true; + return ['extra-small', 'small', 'medium', 'large'].includes(val); + }, + }, + /** 按钮标签id */ + tId: { + type: String, + default: '', + }, + /** 组件风格,依次为品牌色、危险色 */ + theme: { + type: String, + default: 'default' as TdButtonProps['theme'], + validator(val: TdButtonProps['theme']): boolean { + if (!val) return true; + return ['default', 'primary', 'danger', 'light'].includes(val); + }, + }, + /** 同小程序的 formType */ + type: { + type: String, + validator(val: TdButtonProps['type']): boolean { + if (!val) return true; + return ['submit', 'reset'].includes(val); + }, + }, + /** 按钮形式,基础、线框、虚线、文字 */ + variant: { + type: String, + default: 'base' as TdButtonProps['variant'], + validator(val: TdButtonProps['variant']): boolean { + if (!val) return true; + return ['base', 'outline', 'dashed', 'text'].includes(val); + }, + }, + /** 原生按钮属性,用户同意隐私协议事件回调,open-type=agreePrivacyAuthorization时有效 (Tips: 如果使用 onNeedPrivacyAuthorization 接口,需要在 bindagreeprivacyauthorization 触发后再调用 resolve({ event: "agree", buttonId })) */ + onAgreeprivacyauthorization: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,获取用户头像回调,`open-type=chooseAvatar` 时有效。返回 `e.detail.avatarUrl` 为头像临时文件链接 */ + onChooseavatar: { + type: Function, + default: () => ({}), + }, + /** 点击时触发 */ + onClick: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,客服消息回调,`open-type="contact"` 时有效 */ + onContact: { + type: Function, + default: () => ({}), + }, + /** 新的一次性订阅消息下发机制回调,`open-type=liveActivity` 时有效 */ + onCreateliveactivity: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,当使用开放能力时,发生错误的回调,`open-type=launchApp` 时有效 */ + onError: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,手机号快速验证回调,open-type=getPhoneNumber时有效。Tips:在触发 bindgetphonenumber 回调后应立即隐藏手机号按钮组件,或置为 disabled 状态,避免用户重复授权手机号产生额外费用 */ + onGetphonenumber: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,手机号实时验证回调,open-type=getRealtimePhoneNumber 时有效。Tips:在触发 bindgetrealtimephonenumber 回调后应立即隐藏手机号按钮组件,或置为 disabled 状态,避免用户重复授权手机号产生额外费用 */ + onGetrealtimephonenumber: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,用户点击该按钮时,会返回获取到的用户信息,回调的detail数据与wx.getUserInfo返回的一致,open-type="getUserInfo"时有效 */ + onGetuserinfo: { + type: Function, + default: () => ({}), + }, + /** 打开 APP 成功的回调,`open-type=launchApp` 时有效 */ + onLaunchapp: { + type: Function, + default: () => ({}), + }, + /** 原生按钮属性,在打开授权设置页后回调,open-type=openSetting时有效 */ + onOpensetting: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/button/type.ts b/uni_modules/tdesign-uniapp/components/button/type.ts new file mode 100644 index 0000000..5d62b14 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/button/type.ts @@ -0,0 +1,192 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdLoadingProps as LoadingProps } from '../loading/type'; + +export interface TdButtonProps { + /** + * 打开 APP 时,向 APP 传递的参数,open-type=launchApp时有效 + * @default '' + */ + appParameter?: string; + /** + * 是否为块级元素 + * @default false + */ + block?: boolean; + /** + * 按钮内容 + */ + content?: string; + /** + * 自定义 dataset,可通过 event.currentTarget.dataset.custom 获取 + * @default {} + */ + customDataset?: string | number | boolean | object | Array; + /** + * 禁用状态。优先级:Button.disabled > Form.disabled + */ + disabled?: boolean; + /** + * 是否为幽灵按钮(镂空按钮) + * @default false + */ + ghost?: boolean; + /** + * 指定按钮按下去的样式类,按钮不为加载或禁用状态时有效。当 `hover-class="none"` 时,没有点击态效果 + * @default '' + */ + hoverClass?: string; + /** + * 按住后多久出现点击态,单位毫秒 + * @default 20 + */ + hoverStartTime?: number; + /** + * 手指松开后点击态保留时间,单位毫秒 + * @default 70 + */ + hoverStayTime?: number; + /** + * 指定是否阻止本节点的祖先节点出现点击态 + * @default false + */ + hoverStopPropagation?: boolean; + /** + * 图标名称。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` + */ + icon?: string | object; + /** + * 指定返回用户信息的语言,zh_CN 简体中文,zh_TW 繁体中文,en 英文。
具体释义:
`en` 英文;
`zh_CN` 简体中文;
`zh_TW` 繁体中文。
[小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) + */ + lang?: 'en' | 'zh_CN' | 'zh_TW'; + /** + * 是否显示为加载状态 + * @default false + */ + loading?: boolean; + /** + * 透传 Loading 组件全部属性 + * @default {} + */ + loadingProps?: LoadingProps; + /** + * 微信开放能力。
具体释义:
`contact` 打开客服会话,如果用户在会话中点击消息卡片后返回小程序,可以从 bindcontact 回调中获得具体信息,具体说明 (*鸿蒙 OS 暂不支持*);
`liveActivity` 通过前端获取新的一次性订阅消息下发机制使用的 code;
`share` 触发用户转发,使用前建议先阅读使用指引
`getPhoneNumber` 获取用户手机号,可以从 bindgetphonenumber 回调中获取到用户信息,具体说明 (*小程序插件中不能使用*);
`getUserInfo` 获取用户信息,可以从 bindgetuserinfo 回调中获取到用户信息 (*小程序插件中不能使用*);
`launchApp` 打开APP,可以通过 app-parameter 属性设定向 APP 传的参数具体说明
`openSetting` 打开授权设置页;
`feedback` 打开“意见反馈”页面,用户可提交反馈内容并上传日志,开发者可以登录小程序管理后台后进入左侧菜单“客服反馈”页面获取到反馈内容;
`chooseAvatar` 获取用户头像,可以从 bindchooseavatar 回调中获取到头像信息;
`agreePrivacyAuthorization`用户同意隐私协议按钮。用户点击一次此按钮后,所有隐私接口可以正常调用。可通过`bindagreeprivacyauthorization`监听用户同意隐私协议事件。隐私合规开发指南详情可见《小程序隐私协议开发指南》。
[小程序官方文档](https://developers.weixin.qq.com/miniprogram/dev/component/button.html) + */ + openType?: + | 'contact' + | 'share' + | 'getPhoneNumber' + | 'getUserInfo' + | 'launchApp' + | 'openSetting' + | 'feedback' + | 'chooseAvatar' + | 'agreePrivacyAuthorization'; + /** + * 原生按钮属性,当手机号快速验证或手机号实时验证额度用尽时,是否对用户展示“申请获取你的手机号,但该功能使用次数已达当前小程序上限,暂时无法使用”的提示,默认展示,open-type="getPhoneNumber" 或 open-type="getRealtimePhoneNumber" 时有效 + * @default true + */ + phoneNumberNoQuotaToast?: boolean; + /** + * 会话内消息卡片图片,open-type="contact"时有效 + * @default 截图 + */ + sendMessageImg?: string; + /** + * 会话内消息卡片点击跳转小程序路径,open-type="contact"时有效 + * @default 当前分享路径 + */ + sendMessagePath?: string; + /** + * 会话内消息卡片标题,open-type="contact"时有效 + * @default 当前标题 + */ + sendMessageTitle?: string; + /** + * 会话来源,open-type="contact"时有效 + * @default '' + */ + sessionFrom?: string; + /** + * 按钮形状,有 4 种:长方形、正方形、圆角长方形、圆形 + * @default rectangle + */ + shape?: 'rectangle' | 'square' | 'round' | 'circle'; + /** + * 是否显示会话内消息卡片,设置此参数为 true,用户进入客服会话会在右下角显示"可能要发送的小程序"提示,用户点击后可以快速发送小程序消息,open-type="contact"时有效 + * @default false + */ + showMessageCard?: boolean; + /** + * 组件尺寸 + * @default medium + */ + size?: 'extra-small' | 'small' | 'medium' | 'large'; + /** + * 按钮标签id + * @default '' + */ + tId?: string; + /** + * 组件风格,依次为品牌色、危险色 + * @default default + */ + theme?: 'default' | 'primary' | 'danger' | 'light'; + /** + * 同小程序的 formType + */ + type?: 'submit' | 'reset'; + /** + * 按钮形式,基础、线框、虚线、文字 + * @default base + */ + variant?: 'base' | 'outline' | 'dashed' | 'text'; + /** + * 原生按钮属性,用户同意隐私协议事件回调,open-type=agreePrivacyAuthorization时有效 (Tips: 如果使用 onNeedPrivacyAuthorization 接口,需要在 bindagreeprivacyauthorization 触发后再调用 resolve({ event: "agree", buttonId })) + */ + onAgreeprivacyauthorization?: () => void; + /** + * 原生按钮属性,获取用户头像回调,`open-type=chooseAvatar` 时有效。返回 `e.detail.avatarUrl` 为头像临时文件链接 + */ + onChooseavatar?: () => void; + /** + * 点击时触发 + */ + onClick?: (e: MouseEvent) => void; + /** + * 原生按钮属性,客服消息回调,`open-type="contact"` 时有效 + */ + onContact?: () => void; + /** + * 新的一次性订阅消息下发机制回调,`open-type=liveActivity` 时有效 + */ + onCreateliveactivity?: () => void; + /** + * 原生按钮属性,当使用开放能力时,发生错误的回调,`open-type=launchApp` 时有效 + */ + onError?: () => void; + /** + * 原生按钮属性,手机号快速验证回调,open-type=getPhoneNumber时有效。Tips:在触发 bindgetphonenumber 回调后应立即隐藏手机号按钮组件,或置为 disabled 状态,避免用户重复授权手机号产生额外费用 + */ + onGetphonenumber?: () => void; + /** + * 原生按钮属性,手机号实时验证回调,open-type=getRealtimePhoneNumber 时有效。Tips:在触发 bindgetrealtimephonenumber 回调后应立即隐藏手机号按钮组件,或置为 disabled 状态,避免用户重复授权手机号产生额外费用 + */ + onGetrealtimephonenumber?: () => void; + /** + * 原生按钮属性,用户点击该按钮时,会返回获取到的用户信息,回调的detail数据与wx.getUserInfo返回的一致,open-type="getUserInfo"时有效 + */ + onGetuserinfo?: () => void; + /** + * 打开 APP 成功的回调,`open-type=launchApp` 时有效 + */ + onLaunchapp?: () => void; + /** + * 原生按钮属性,在打开授权设置页后回调,open-type=openSetting时有效 + */ + onOpensetting?: () => void; +} diff --git a/uni_modules/tdesign-uniapp/components/calendar/README.en-US.md b/uni_modules/tdesign-uniapp/components/calendar/README.en-US.md new file mode 100644 index 0000000..64669c9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/README.en-US.md @@ -0,0 +1,63 @@ +:: BASE_DOC :: + +## API + +### Calendar Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +allow-same-day | Boolean | false | \- | N +auto-close | Boolean | true | \- | N +confirm-btn | String / Object | '' | [see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts) | N +first-day-of-week | Number | 0 | \- | N +format | Function | - | Typescript: `CalendarFormatType ` `type CalendarFormatType = (day: TDate) => TDate` `type TDateType = 'selected' \| 'disabled' \| 'start' \| 'start-end' \|'centre' \| 'end' \| ''` `interface TDate { date: Date; day: number; type: TDateType; className?: string; prefix?: string; suffix?: string;}`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts) | N +locale-text | Object | - | Typescript: `CalendarLocaleText` `interface CalendarLocaleText {title?: string; weekdays?: string[]; monthTitle?: string; months?: string[]; confirm?: string;}`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts) | N +max-date | Number | - | \- | N +min-date | Number | - | \- | N +readonly | Boolean | - | \- | N +switch-mode | String | none | options: none/month/year-month | N +title | String | - | \- | N +type | String | single | options: single/multiple/range | N +use-popup | Boolean | true | \- | N +using-custom-navbar | Boolean | false | \- | N +value | Number / Array | - | `v-model:value` is supported。Typescript: `number \| number[]` | N +default-value | Number / Array | - | uncontrolled property。Typescript: `number \| number[]` | N +visible | Boolean | false | \- | N + +### Calendar Events + +name | params | description +-- | -- | -- +change | `(context: { value: number \| number[] })` | \- +close | `(context: { trigger: CalendarTrigger })` | [see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts)。
`type CalendarTrigger = 'close-btn' \| 'confirm-btn' \| 'overlay' \| 'auto-close'`
+confirm | `(context: { value: number \| number[] })` | \- +panel-change | `(context: { year: number, month: number })` | \- +scroll | `(context: {scrollLeft: number, scrollTop: number, scrollHeight: number, scrollWidth: number, deltaX: number, deltaY: number})` | triggered when scrolling +select | `(context: { value: number \| number[] })` | \- + +### Calendar Slots + +name | Description +-- | -- +confirm-btn | \- +title | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-calendar-active-color | @brand-color | - +--td-calendar-bg-color | @bg-color-container | - +--td-calendar-days-color | @text-color-secondary | - +--td-calendar-item-centre-color | @brand-color-light | - +--td-calendar-item-disabled-color | @text-color-disabled | - +--td-calendar-item-suffix-color | @text-color-placeholder | - +--td-calendar-radius | 24rpx | - +--td-calendar-selected-border-radius | @radius-default | - +--td-calendar-selected-color | @text-color-anti | - +--td-calendar-switch-mode-icon-color | @text-color-secondary | - +--td-calendar-switch-mode-icon-disabled-color | @text-color-disabled | - +--td-calendar-title-color | @text-color-primary | - +--td-calendar-title-font | @font-title-large | - diff --git a/uni_modules/tdesign-uniapp/components/calendar/README.md b/uni_modules/tdesign-uniapp/components/calendar/README.md new file mode 100644 index 0000000..80dd052 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/README.md @@ -0,0 +1,113 @@ +--- +title: Calendar 日历 +description: 按照日历形式展示数据或日期的容器。 +spline: form +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TCalendar from '@tdesign/uniapp/calendar/calendar.vue'; +``` + +### 组件类型 + +#### 单个选择日历 + +{{ base }} + +#### 多个选择日历 + +{{ multiple }} + +#### 带单行/双行描述的日历 + +{{ custom-text }} + +#### 带翻页功能的日历 + +{{ switch-mode }} + +#### 可选择区间日期的日历 + +{{ range }} + +### 组件样式 + +#### 国际化 + +{{ local-text }} + +#### 含不可选的日历 + +{{ custom-range }} + +#### 不使用 Popup + +{{ without-popup }} + +## API + +### Calendar Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +allow-same-day | Boolean | false | 是否允许区间选择日历的起止时间相同,仅当 `type='range'` 时有效 | N +auto-close | Boolean | true | 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible | N +confirm-btn | String / Object | '' | 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts) | N +first-day-of-week | Number | 0 | 第一天从星期几开始,默认 0 = 周日 | N +format | Function | - | 用于格式化日期的函数。TS 类型:`CalendarFormatType ` `type CalendarFormatType = (day: TDate) => TDate` `type TDateType = 'selected' \| 'disabled' \| 'start' \| 'start-end' \|'centre' \| 'end' \| ''` `interface TDate { date: Date; day: number; type: TDateType; className?: string; prefix?: string; suffix?: string;}`。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts) | N +locale-text | Object | - | 国际化文案。TS 类型:`CalendarLocaleText` `interface CalendarLocaleText {title?: string; weekdays?: string[]; monthTitle?: string; months?: string[]; confirm?: string;}`。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts) | N +max-date | Number | - | 最大可选的日期,不传则默认半年后 | N +min-date | Number | - | 最小可选的日期,不传则默认今天 | N +readonly | Boolean | - | 是否只读,只读状态下不能选择日期 | N +switch-mode | String | none | 切换模式。 `none` 表示平铺展示所有月份; `month` 表示支持按月切换, `year-month` 表示既按年切换,也支持按月切换。可选项:none/month/year-month | N +title | String | - | 标题,不传默认为“请选择日期” | N +type | String | single | 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择。可选项:single/multiple/range | N +use-popup | Boolean | true | 是否使用弹出层包裹日历 | N +using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N +value | Number / Array | - | 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组。支持语法糖 `v-model:value`。TS 类型:`number \| number[]` | N +default-value | Number / Array | - | 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组。非受控属性。TS 类型:`number \| number[]` | N +visible | Boolean | false | 是否显示日历;`usePopup` 为 true 时有效 | N + +### Calendar Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { value: number \| number[] })` | 不显示 confirm-btn 时,完成选择时触发(暂不支持 type = multiple) +close | `(context: { trigger: CalendarTrigger })` | 关闭按钮时触发。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/calendar/type.ts)。
`type CalendarTrigger = 'close-btn' \| 'confirm-btn' \| 'overlay' \| 'auto-close'`
+confirm | `(context: { value: number \| number[] })` | 点击确认按钮时触发 +panel-change | `(context: { year: number, month: number })` | 切换月或年时触发(switch-mode 不为 none 时有效) +scroll | `(context: {scrollLeft: number, scrollTop: number, scrollHeight: number, scrollWidth: number, deltaX: number, deltaY: number})` | 滚动时触发 +select | `(context: { value: number \| number[] })` | 点击日期时触发 + +### Calendar Slots + +名称 | 描述 +-- | -- +confirm-btn | 确认按钮 +title | 标题 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-calendar-active-color | @brand-color | - +--td-calendar-bg-color | @bg-color-container | - +--td-calendar-days-color | @text-color-secondary | - +--td-calendar-item-centre-color | @brand-color-light | - +--td-calendar-item-disabled-color | @text-color-disabled | - +--td-calendar-item-suffix-color | @text-color-placeholder | - +--td-calendar-radius | 24rpx | - +--td-calendar-selected-border-radius | @radius-default | - +--td-calendar-selected-color | @text-color-anti | - +--td-calendar-switch-mode-icon-color | @text-color-secondary | - +--td-calendar-switch-mode-icon-disabled-color | @text-color-disabled | - +--td-calendar-title-color | @text-color-primary | - +--td-calendar-title-font | @font-title-large | - diff --git a/uni_modules/tdesign-uniapp/components/calendar/calendar-header.props.js b/uni_modules/tdesign-uniapp/components/calendar/calendar-header.props.js new file mode 100644 index 0000000..e30be21 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/calendar-header.props.js @@ -0,0 +1,42 @@ +export default { + tClass: { + type: String, + default: '', + }, + classPrefix: { + type: String, + default: '', + }, + switchMode: { + type: String, + default: '', + }, + title: { + type: String, + default: '', + }, + preYearBtnDisable: { + type: Boolean, + }, + prevMonthBtnDisable: { + type: Boolean, + }, + nextYearBtnDisable: { + type: Boolean, + }, + nextMonthBtnDisable: { + type: Boolean, + }, + tId: { + type: String, + default: '', + }, + // realLocalText: { + // type: Object, + // default: () => ({}), + // }, + // currentMonth: { + // type: Array, + // default: () => ([]), + // }, +}; diff --git a/uni_modules/tdesign-uniapp/components/calendar/calendar-header.vue b/uni_modules/tdesign-uniapp/components/calendar/calendar-header.vue new file mode 100644 index 0000000..633ffcf --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/calendar-header.vue @@ -0,0 +1,98 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/calendar/calendar.css b/uni_modules/tdesign-uniapp/components/calendar/calendar.css new file mode 100644 index 0000000..e735cac --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/calendar.css @@ -0,0 +1,205 @@ +.t-calendar { + width: inherit; + position: relative; + z-index: 9999; + background: var(--td-calendar-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + overflow-x: hidden; +} +.t-calendar--popup { + border-top-left-radius: var(--td-calendar-radius, 24rpx); + border-top-right-radius: var(--td-calendar-radius, 24rpx); +} +.t-calendar__title { + display: flex; + align-items: center; + justify-content: center; + font: var(--td-calendar-title-font, var(--td-font-title-large, 600 36rpx / 52rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-calendar-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + padding: var(--td-spacer-2, 32rpx); +} +.t-calendar__title:focus { + outline: 0; +} +.t-calendar__close-btn { + position: absolute; + top: var(--td-spacer-2, 32rpx); + right: var(--td-spacer-2, 32rpx); + margin: calc(-1 * var(--td-spacer-1, 24rpx)); + padding: var(--td-spacer-1, 24rpx); + color: var(--td-calendar-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-calendar__days { + display: grid; + grid-template-columns: repeat(7, 1fr); + grid-column-gap: 8rpx; + padding: 0 32rpx; + text-align: center; + line-height: 92rpx; +} +.t-calendar__days-item { + height: 92rpx; + font-size: 28rpx; + color: var(--td-calendar-days-color, var(--td-text-color-secondary, var(--td-font-gray-2, rgba(0, 0, 0, 0.6)))); +} +.t-calendar__content { + min-height: 400rpx; + display: flex; + flex-direction: column; +} +.t-calendar__month { + font: var(--td-font-title-small, 600 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + color: var(--td-calendar-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + padding: 32rpx 0 0; +} +.t-calendar__months { + height: 712rpx; + padding: 0 32rpx 32rpx; + box-sizing: border-box; +} +.t-calendar__months::-webkit-scrollbar { + display: none; +} +.t-calendar__dates { + flex: 1; + display: grid; + grid-template-columns: repeat(7, 1fr); + grid-column-gap: 8rpx; +} +.t-calendar__dates-item { + position: relative; + display: flex; + align-items: center; + justify-content: center; + font: var(--td-font-title-medium, 600 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + border-radius: var(--td-calendar-selected-border-radius, var(--td-radius-default, 12rpx)); + height: 120rpx; + margin-top: 16rpx; + color: var(--td-calendar-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + cursor: pointer; + -webkit-tap-highlight-color: transparent; + -webkit-user-select: none; + user-select: none; +} +.t-calendar__dates-item-prefix, +.t-calendar__dates-item-suffix { + position: absolute; + font: var(--td-font-body-extraSmall, 20rpx / 32rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + width: 100%; + text-align: center; +} +.t-calendar__dates-item-prefix { + top: 8rpx; +} +.t-calendar__dates-item-suffix { + bottom: 8rpx; + color: var(--td-calendar-item-suffix-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); +} +.t-calendar__dates-item-suffix--selected, +.t-calendar__dates-item-suffix--start, +.t-calendar__dates-item-suffix--end { + color: var(--td-calendar-selected-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); +} +.t-calendar__dates-item-suffix--disabled { + color: var(--td-calendar-item-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-calendar__dates-item--selected, +.t-calendar__dates-item--start-end, +.t-calendar__dates-item--start, +.t-calendar__dates-item--end { + background: var(--td-calendar-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + color: var(--td-calendar-selected-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); + border-radius: var(--td-calendar-selected-border-radius, var(--td-radius-default, 12rpx)); +} +.t-calendar__dates-item--start { + border-radius: var(--td-calendar-selected-border-radius, var(--td-radius-default, 12rpx)) 0 0 var(--td-calendar-selected-border-radius, var(--td-radius-default, 12rpx)); +} +.t-calendar__dates-item--end { + border-radius: 0 var(--td-calendar-selected-border-radius, var(--td-radius-default, 12rpx)) var(--td-calendar-selected-border-radius, var(--td-radius-default, 12rpx)) 0; +} +.t-calendar__dates-item--start + .t-calendar__dates-item--end::before { + content: ''; + display: block; + position: absolute; + top: 0; + width: 8rpx; + height: 100%; + background: var(--td-calendar-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-calendar__dates-item--start + .t-calendar__dates-item--end:before { + left: -8rpx; +} +.t-calendar__dates-item--centre { + border-radius: 0; + background-color: var(--td-calendar-item-centre-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-calendar__dates-item--centre::before, +.t-calendar__dates-item--centre::after { + content: ''; + display: block; + position: absolute; + top: 0; + width: 8rpx; + height: 100%; + background-color: var(--td-calendar-item-centre-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-calendar__dates-item--centre:before { + left: -8rpx; +} +.t-calendar__dates-item--centre:after { + right: -8rpx; +} +.t-calendar__dates-item--disabled { + color: var(--td-calendar-item-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); + cursor: default; +} +.t-calendar__footer { + padding: 32rpx; +} +.t-calendar-switch-mode--none > .t-calendar__months { + height: 60vh; +} +.t-calendar-header { + display: flex; + justify-content: space-between; + align-items: center; +} +.t-calendar-header__with-action { + padding: 0rpx 32rpx 16rpx 32rpx; + box-sizing: border-box; + position: relative; +} +.t-calendar-header__with-action::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-border-color, var(--td-gray-color-3, #e7e7e7)); +} +.t-calendar-header__with-action::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-calendar-header__with-action .t-calendar-header__title { + flex: 1; + text-align: center; + font: var(--td-font-title-small, 600 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); +} +.t-calendar-header__action { + display: flex; + font-size: 40rpx; + color: var(--td-calendar-switch-mode-icon-color, var(--td-text-color-secondary, var(--td-font-gray-2, rgba(0, 0, 0, 0.6)))); +} +.t-calendar-header__icon { + padding: 16rpx; +} +.t-calendar-header__icon--disabled { + color: var(--td-calendar-switch-mode-icon-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-calendar-header__title { + text-align: left; +} diff --git a/uni_modules/tdesign-uniapp/components/calendar/calendar.vue b/uni_modules/tdesign-uniapp/components/calendar/calendar.vue new file mode 100644 index 0000000..063cbcf --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/calendar.vue @@ -0,0 +1,454 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/calendar/computed.js b/uni_modules/tdesign-uniapp/components/calendar/computed.js new file mode 100644 index 0000000..14f0245 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/computed.js @@ -0,0 +1,42 @@ +import { getRegExp } from '../common/runtime/wxs-polyfill'; + + +export function isDateSelected(dateItem) { + return ['start', 'end', 'selected', 'centre'].indexOf(dateItem.type) >= 0; +} + +export function getMonthTitle(year, month, pattern = '') { + // prettier-ignore + const REGEXP = getRegExp('\\{year\\}|\\{month\\}', 'g'); + + return pattern.replace(REGEXP, (match) => { + const replacements = { + '{year}': year, + '{month}': month < 10 ? `0${month}` : month, + }; + return replacements[match] || match; + }); +} + + +export function getDateLabel(monthItem, dateItem) { + const weekdayText = ['日', '一', '二', '三', '四', '五', '六']; + const weekday = (monthItem.weekdayOfFirstDay + dateItem.day - 1) % 7; + let label = `${monthItem.month + 1}月${dateItem.day}日, 星期${weekdayText[weekday]}`; + if (dateItem.type === 'start') { + label = `开始日期:${label}`; + } + if (dateItem.type === 'end') { + label = `结束日期:${label}`; + } + if (isDateSelected(dateItem)) { + label = `已选中, ${label}`; + } + if (dateItem.prefix) { + label += `, ${dateItem.prefix}`; + } + if (dateItem.suffix) { + label += `, ${dateItem.suffix}`; + } + return label; +} diff --git a/uni_modules/tdesign-uniapp/components/calendar/props.ts b/uni_modules/tdesign-uniapp/components/calendar/props.ts new file mode 100644 index 0000000..379ca85 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/props.ts @@ -0,0 +1,113 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCalendarProps } from './type'; +export default { + /** 是否允许区间选择日历的起止时间相同,仅当 `type='range'` 时有效 */ + allowSameDay: Boolean, + /** 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible */ + autoClose: { + type: Boolean, + default: true, + }, + /** 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性 */ + confirmBtn: { + type: [String, Object], + default: '' as TdCalendarProps['confirmBtn'], + }, + /** 第一天从星期几开始,默认 0 = 周日 */ + firstDayOfWeek: { + type: Number, + default: 0, + }, + /** 用于格式化日期的函数 */ + format: { + type: Function, + }, + /** 国际化文案 */ + localeText: { + type: Object, + }, + /** 最大可选的日期,不传则默认半年后 */ + maxDate: { + type: Number, + }, + /** 最小可选的日期,不传则默认今天 */ + minDate: { + type: Number, + }, + /** 是否只读,只读状态下不能选择日期 */ + readonly: Boolean, + /** 切换模式。 `none` 表示平铺展示所有月份; `month` 表示支持按月切换, `year-month` 表示既按年切换,也支持按月切换 */ + switchMode: { + type: String, + default: 'none' as TdCalendarProps['switchMode'], + validator(val: TdCalendarProps['switchMode']): boolean { + if (!val) return true; + return ['none', 'month', 'year-month'].includes(val); + }, + }, + /** 标题,不传默认为“请选择日期” */ + title: { + type: String, + }, + /** 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择 */ + type: { + type: String, + default: 'single' as TdCalendarProps['type'], + validator(val: TdCalendarProps['type']): boolean { + if (!val) return true; + return ['single', 'multiple', 'range'].includes(val); + }, + }, + /** 是否使用弹出层包裹日历 */ + usePopup: { + type: Boolean, + default: true, + }, + /** 是否使用了自定义导航栏 */ + usingCustomNavbar: Boolean, + /** 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组 */ + value: { + type: [Number, Array], + }, + /** 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组,非受控属性 */ + defaultValue: { + type: [Number, Array], + }, + /** 是否显示日历;`usePopup` 为 true 时有效 */ + visible: Boolean, + /** 不显示 confirm-btn 时,完成选择时触发(暂不支持 type = multiple) */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 关闭按钮时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 点击确认按钮时触发 */ + onConfirm: { + type: Function, + default: () => ({}), + }, + /** 切换月或年时触发(switch-mode 不为 none 时有效) */ + onPanelChange: { + type: Function, + default: () => ({}), + }, + /** 滚动时触发 */ + onScroll: { + type: Function, + default: () => ({}), + }, + /** 点击日期时触发 */ + onSelect: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/calendar/template.props.js b/uni_modules/tdesign-uniapp/components/calendar/template.props.js new file mode 100644 index 0000000..c421f33 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/template.props.js @@ -0,0 +1,57 @@ +export default { + classPrefix: { + type: String, + default: '', + }, + usePopup: { + type: Boolean, + }, + switchMode: { + type: String, + default: '', + }, + tClass: { + type: String, + default: '', + }, + customStyle: { + type: [String, Object], + default: '', + }, + title: { + type: String, + default: '', + }, + realLocalText: { + type: Object, + default: () => ({}), + }, + months: { + type: Array, + default: () => ([]), + }, + currentMonth: { + type: [Array, Object], + default: () => ([]), + }, + actionButtons: { + type: Object, + default: () => ({}), + }, + days: { + type: Array, + default: () => ([]), + }, + scrollIntoView: { + type: String, + default: '', + }, + firstDayOfWeek: { + type: Number, + default: 0, + }, + innerConfirmBtn: { + type: [Object, String], + default: '', + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/calendar/template.vue b/uni_modules/tdesign-uniapp/components/calendar/template.vue new file mode 100644 index 0000000..52e767d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/template.vue @@ -0,0 +1,261 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/calendar/type.ts b/uni_modules/tdesign-uniapp/components/calendar/type.ts new file mode 100644 index 0000000..888089d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/type.ts @@ -0,0 +1,141 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdButtonProps as ButtonProps } from '../button/type'; + +export interface TdCalendarProps { + /** + * 是否允许区间选择日历的起止时间相同,仅当 `type='range'` 时有效 + * @default false + */ + allowSameDay?: boolean; + /** + * 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible + * @default true + */ + autoClose?: boolean; + /** + * 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性 + * @default '' + */ + confirmBtn?: string | ButtonProps | null; + /** + * 第一天从星期几开始,默认 0 = 周日 + * @default 0 + */ + firstDayOfWeek?: number; + /** + * 用于格式化日期的函数 + */ + format?: CalendarFormatType; + /** + * 国际化文案 + */ + localeText?: CalendarLocaleText; + /** + * 最大可选的日期,不传则默认半年后 + */ + maxDate?: number; + /** + * 最小可选的日期,不传则默认今天 + */ + minDate?: number; + /** + * 是否只读,只读状态下不能选择日期 + */ + readonly?: boolean; + /** + * 切换模式。 `none` 表示平铺展示所有月份; `month` 表示支持按月切换, `year-month` 表示既按年切换,也支持按月切换 + * @default none + */ + switchMode?: 'none' | 'month' | 'year-month'; + /** + * 标题,不传默认为“请选择日期” + */ + title?: string; + /** + * 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择 + * @default single + */ + type?: 'single' | 'multiple' | 'range'; + /** + * 是否使用弹出层包裹日历 + * @default true + */ + usePopup?: boolean; + /** + * 是否使用了自定义导航栏 + * @default false + */ + usingCustomNavbar?: boolean; + /** + * 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组 + */ + value?: number | number[]; + /** + * 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组,非受控属性 + */ + defaultValue?: number | number[]; + /** + * 是否显示日历;`usePopup` 为 true 时有效 + * @default false + */ + visible?: boolean; + /** + * 不显示 confirm-btn 时,完成选择时触发(暂不支持 type = multiple) + */ + onChange?: (context: { value: number | number[] }) => void; + /** + * 关闭按钮时触发 + */ + onClose?: (context: { trigger: CalendarTrigger }) => void; + /** + * 点击确认按钮时触发 + */ + onConfirm?: (context: { value: number | number[] }) => void; + /** + * 切换月或年时触发(switch-mode 不为 none 时有效) + */ + onPanelChange?: (context: { year: number; month: number }) => void; + /** + * 滚动时触发 + */ + onScroll?: (context: { + scrollLeft: number; + scrollTop: number; + scrollHeight: number; + scrollWidth: number; + deltaX: number; + deltaY: number; + }) => void; + /** + * 点击日期时触发 + */ + onSelect?: (context: { value: number | number[] }) => void; +} + +export type CalendarFormatType = (day: TDate) => TDate; + +export type TDateType = 'selected' | 'disabled' | 'start' | 'start-end' | 'centre' | 'end' | ''; + +export interface TDate { + date: Date; + day: number; + type: TDateType; + className?: string; + prefix?: string; + suffix?: string; +} + +export interface CalendarLocaleText { + title?: string; + weekdays?: string[]; + monthTitle?: string; + months?: string[]; + confirm?: string; +} + +export type CalendarTrigger = 'close-btn' | 'confirm-btn' | 'overlay' | 'auto-close'; diff --git a/uni_modules/tdesign-uniapp/components/calendar/utils.js b/uni_modules/tdesign-uniapp/components/calendar/utils.js new file mode 100644 index 0000000..aa2c2d8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/calendar/utils.js @@ -0,0 +1,16 @@ +export function getMonthByOffset(date, offset) { + const _date = new Date(date); + _date.setMonth(_date.getMonth() + offset); + return _date; +} + +export function getYearByOffset(date, offset) { + const _date = new Date(date); + _date.setFullYear(_date.getFullYear() + offset); + return _date; +} + +export const getPrevMonth = date => getMonthByOffset(date, -1); +export const getNextMonth = date => getMonthByOffset(date, 1); +export const getPrevYear = date => getYearByOffset(date, -1); +export const getNextYear = date => getYearByOffset(date, 1); diff --git a/uni_modules/tdesign-uniapp/components/cascader/README.en-US.md b/uni_modules/tdesign-uniapp/components/cascader/README.en-US.md new file mode 100644 index 0000000..694e0a8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cascader/README.en-US.md @@ -0,0 +1,56 @@ +:: BASE_DOC :: + +## API + +### Cascader Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +check-strictly | Boolean | false | \- | N +close-btn | Boolean | true | \- | N +keys | Object | - | Typescript: `CascaderKeysType` `type CascaderKeysType = TreeKeysType`。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/common/common.ts)。[see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/cascader/type.ts) | N +options | Array | [] | Typescript: `Array` | N +placeholder | String | 选择选项 | \- | N +sub-titles | Array | [] | Typescript: `Array` | N +theme | String | step | options: step/tab | N +title | String | - | \- | N +value | String / Number | - | `v-model:value` is supported | N +default-value | String / Number | - | uncontrolled property | N +visible | Boolean | false | \- | N + +### Cascader Events + +name | params | description +-- | -- | -- +change | `(context: { value: string \| number, selectedOptions: string[] })` | \- +close | `(context: { trigger: CascaderTriggerSource })` | [see more ts definition](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/cascader/type.ts)。
`type CascaderTriggerSource = 'overlay' \| 'close-btn' \| 'finish'`
+pick | `(context: { value: string \| number, label: string, index: number, level: number })` | \- + +### Cascader Slots + +name | Description +-- | -- +close-btn | \- +header | \- +middle-content | \- +title | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-cascader-active-color | @brand-color | - +--td-cascader-bg-color | @bg-color-container | - +--td-cascader-border-color | @component-stroke | - +--td-cascader-content-height | 78vh | - +--td-cascader-disabled-color | @text-color-disabled | - +--td-cascader-options-height | calc(100% - @cascader-step-height) | - +--td-cascader-options-title-color | @text-color-placeholder | - +--td-cascader-step-arrow-color | @text-color-placeholder | - +--td-cascader-step-dot-size | 16rpx | - +--td-cascader-step-height | 88rpx | - +--td-cascader-title-color | @text-color-primary | - +--td-cascader-title-font | @font-title-large | - +--td-cascader-title-padding | @spacer-2 | - diff --git a/uni_modules/tdesign-uniapp/components/cascader/README.md b/uni_modules/tdesign-uniapp/components/cascader/README.md new file mode 100644 index 0000000..51472aa --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cascader/README.md @@ -0,0 +1,96 @@ +--- +title: Cascader 级联选择器 +description: 级联选择器适用于有清晰层级结构的数据集合,用户可以通过逐级查看并选择。 +spline: form +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TCascader from '@tdesign/uniapp/cascader/cascader.vue'; +``` + +### 基础用法 + +{{ base }} + +### 选项卡风格 + +{{ theme-tab }} + +### 进阶 + +#### 带初始值 + +{{ with-value }} + +#### 自定义 keys + +{{ keys }} + +#### 使用次级标题 + +{{ with-title }} + +#### 选择任意一项 + +{{ check-strictly }} + +## API + +### Cascader Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +check-strictly | Boolean | false | 父子节点选中状态不再关联,可各自选中或取消 | N +close-btn | Boolean | true | 关闭按钮 | N +keys | Object | - | 用来定义 value / label / children / disabled 在 `options` 中对应的字段别名。TS 类型:`CascaderKeysType` `type CascaderKeysType = TreeKeysType`。[通用类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/common/common.ts)。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/cascader/type.ts) | N +options | Array | [] | 可选项数据源。TS 类型:`Array` | N +placeholder | String | 选择选项 | 未选中时的提示文案 | N +sub-titles | Array | [] | 每级展示的次标题。TS 类型:`Array` | N +theme | String | step | 展示风格。可选项:step/tab | N +title | String | - | 标题 | N +value | String / Number | - | 选项值。支持语法糖 `v-model:value` | N +default-value | String / Number | - | 选项值。非受控属性 | N +visible | Boolean | false | 是否展示 | N + +### Cascader Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { value: string \| number, selectedOptions: string[] })` | 值发生变更时触发 +close | `(context: { trigger: CascaderTriggerSource })` | 关闭时触发。[详细类型定义](https://github.com/tencent/tdesign-miniprogram/blob/develop/packages/uniapp-components/cascader/type.ts)。
`type CascaderTriggerSource = 'overlay' \| 'close-btn' \| 'finish'`
+pick | `(context: { value: string \| number, label: string, index: number, level: number })` | 选择后触发 + +### Cascader Slots + +名称 | 描述 +-- | -- +close-btn | 自定义 `close-btn` 显示内容 +header | 头部 +middle-content | 中间内容 +title | 自定义 `title` 显示内容 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-cascader-active-color | @brand-color | - +--td-cascader-bg-color | @bg-color-container | - +--td-cascader-border-color | @component-stroke | - +--td-cascader-content-height | 78vh | - +--td-cascader-disabled-color | @text-color-disabled | - +--td-cascader-options-height | calc(100% - @cascader-step-height) | - +--td-cascader-options-title-color | @text-color-placeholder | - +--td-cascader-step-arrow-color | @text-color-placeholder | - +--td-cascader-step-dot-size | 16rpx | - +--td-cascader-step-height | 88rpx | - +--td-cascader-title-color | @text-color-primary | - +--td-cascader-title-font | @font-title-large | - +--td-cascader-title-padding | @spacer-2 | - diff --git a/uni_modules/tdesign-uniapp/components/cascader/cascader.css b/uni_modules/tdesign-uniapp/components/cascader/cascader.css new file mode 100644 index 0000000..54dea53 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cascader/cascader.css @@ -0,0 +1,102 @@ +.t-cascader { + display: flex; + flex-direction: column; + background-color: var(--td-cascader-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + color: var(--td-cascader-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + border-radius: var(--td-radius-extraLarge, 24rpx) var(--td-radius-extraLarge, 24rpx) 0 0; + --td-radio-icon-checked-color: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + --td-tab-item-active-color: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + --td-tab-track-color: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-cascader__close-btn { + right: var(--td-spacer-2, 32rpx); + top: var(--td-spacer-2, 32rpx); + position: absolute; +} +.t-cascader__title { + position: relative; + font: var(--td-cascader-title-font, var(--td-font-title-large, 600 36rpx / 52rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + text-align: center; + padding: var(--td-cascader-title-padding, var(--td-spacer-2, 32rpx)); +} +.t-cascader__content { + width: 100%; + height: var(--td-cascader-content-height, 78vh); + display: flex; + flex-direction: column; +} +.t-cascader__options { + width: 100vw; +} +.t-cascader__options-title { + color: var(--td-cascader-options-title-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + font: var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); + padding-top: 40rpx; + padding-left: var(--td-spacer-2, 32rpx); + box-sizing: border-box; +} +.t-cascader__options-container { + flex: 1; + display: flex; + transition: all ease 0.3s; +} +.t-cascader__step { + display: flex; + align-items: center; + height: var(--td-cascader-step-height, 88rpx); +} +.t-cascader__steps { + padding: 0 32rpx 10rpx; + position: relative; +} +.t-cascader__steps::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-cascader-border-color, var(--td-component-stroke, var(--td-gray-color-3, #e7e7e7))); +} +.t-cascader__steps::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-cascader__step-dot { + position: relative; + width: var(--td-cascader-step-dot-size, 16rpx); + height: var(--td-cascader-step-dot-size, 16rpx); + border-radius: 50%; + border: 2rpx solid var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + box-sizing: border-box; +} +.t-cascader__step-dot:not(.t-cascader__step-dot--last)::after { + content: ''; + display: block; + position: absolute; + left: 50%; + top: calc(var(--td-cascader-step-dot-size, 16rpx) + 14rpx); + height: 36rpx; + width: 2rpx; + background: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + transform: translateX(-50%); +} +.t-cascader__step-dot--active { + background: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + border-color: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-cascader__step-label { + padding-left: var(--td-spacer-2, 32rpx); + font: var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); +} +.t-cascader__step-label--active { + color: var(--td-cascader-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + font-weight: 600; +} +.t-cascader__step-arrow { + color: var(--td-cascader-step-arrow-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + margin-left: auto; +} diff --git a/uni_modules/tdesign-uniapp/components/cascader/cascader.vue b/uni_modules/tdesign-uniapp/components/cascader/cascader.vue new file mode 100644 index 0000000..a0c4eb6 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cascader/cascader.vue @@ -0,0 +1,519 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/cascader/props.ts b/uni_modules/tdesign-uniapp/components/cascader/props.ts new file mode 100644 index 0000000..4de54aa --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cascader/props.ts @@ -0,0 +1,73 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCascaderProps } from './type'; +export default { + /** 父子节点选中状态不再关联,可各自选中或取消 */ + checkStrictly: Boolean, + /** 关闭按钮 */ + closeBtn: { + type: Boolean, + default: true as TdCascaderProps['closeBtn'], + }, + /** 用来定义 value / label / children / disabled 在 `options` 中对应的字段别名 */ + keys: { + type: Object, + }, + /** 可选项数据源 */ + options: { + type: Array, + default: (): TdCascaderProps['options'] => [], + }, + /** 未选中时的提示文案 */ + placeholder: { + type: String, + default: '选择选项', + }, + /** 每级展示的次标题 */ + subTitles: { + type: Array, + default: (): TdCascaderProps['subTitles'] => [], + }, + /** 展示风格 */ + theme: { + type: String, + default: 'step' as TdCascaderProps['theme'], + validator(val: TdCascaderProps['theme']): boolean { + if (!val) return true; + return ['step', 'tab'].includes(val); + }, + }, + /** 标题 */ + title: { + type: String, + }, + /** 选项值 */ + value: { + type: [String, Number], + }, + /** 选项值,非受控属性 */ + defaultValue: { + type: [String, Number], + }, + /** 是否展示 */ + visible: Boolean, + /** 值发生变更时触发 */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 关闭时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 选择后触发 */ + onPick: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/cascader/type.ts b/uni_modules/tdesign-uniapp/components/cascader/type.ts new file mode 100644 index 0000000..cfd9e36 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cascader/type.ts @@ -0,0 +1,77 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TreeOptionData, TreeKeysType } from '../common/common'; + +export interface TdCascaderProps { + /** + * 父子节点选中状态不再关联,可各自选中或取消 + * @default false + */ + checkStrictly?: boolean; + /** + * 关闭按钮 + * @default true + */ + closeBtn?: boolean; + /** + * 用来定义 value / label / children / disabled 在 `options` 中对应的字段别名 + */ + keys?: CascaderKeysType; + /** + * 可选项数据源 + * @default [] + */ + options?: Array; + /** + * 未选中时的提示文案 + * @default 选择选项 + */ + placeholder?: string; + /** + * 每级展示的次标题 + * @default [] + */ + subTitles?: Array; + /** + * 展示风格 + * @default step + */ + theme?: 'step' | 'tab'; + /** + * 标题 + */ + title?: string; + /** + * 选项值 + */ + value?: string | number; + /** + * 选项值,非受控属性 + */ + defaultValue?: string | number; + /** + * 是否展示 + * @default false + */ + visible?: boolean; + /** + * 值发生变更时触发 + */ + onChange?: (context: { value: string | number; selectedOptions: string[] }) => void; + /** + * 关闭时触发 + */ + onClose?: (context: { trigger: CascaderTriggerSource }) => void; + /** + * 选择后触发 + */ + onPick?: (context: { value: string | number; label: string; index: number; level: number }) => void; +} + +export type CascaderKeysType = TreeKeysType; + +export type CascaderTriggerSource = 'overlay' | 'close-btn' | 'finish'; diff --git a/uni_modules/tdesign-uniapp/components/cell-group/cell-group.css b/uni_modules/tdesign-uniapp/components/cell-group/cell-group.css new file mode 100644 index 0000000..a031b8a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell-group/cell-group.css @@ -0,0 +1,44 @@ +.t-cell-group { + position: relative; +} +.t-cell-group__title { + font-family: PingFangSC-Regular; + font-size: var(--td-cell-group-title-font-size, 28rpx); + color: var(--td-cell-group-title-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + text-align: left; + line-height: var(--td-cell-group-title-line-height, 90rpx); + background-color: var(--td-cell-group-title-bg-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); + padding-left: var(--td-cell-group-title-padding-left, 32rpx); +} +.t-cell-group--bordered::before { + position: absolute; + box-sizing: border-box; + content: ' '; + pointer-events: none; + right: 0; + left: 0; + top: 0; + border-top: 1px solid var(--td-cell-group-border-color, var(--td-component-stroke, var(--td-gray-color-3, #e7e7e7))); + transform: scaleY(0.5); + transform-origin: 0 0; + transform-origin: top; + z-index: 1; +} +.t-cell-group--bordered::after { + position: absolute; + box-sizing: border-box; + content: ' '; + pointer-events: none; + right: 0; + left: 0; + bottom: 0; + border-bottom: 1px solid var(--td-cell-group-border-color, var(--td-component-stroke, var(--td-gray-color-3, #e7e7e7))); + transform: scaleY(0.5); + transform-origin: bottom; + z-index: 1; +} +.t-cell-group--card { + margin: 0 32rpx; + border-radius: var(--td-radius-large, 18rpx); + overflow: hidden; +} diff --git a/uni_modules/tdesign-uniapp/components/cell-group/cell-group.vue b/uni_modules/tdesign-uniapp/components/cell-group/cell-group.vue new file mode 100644 index 0000000..2c38b72 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell-group/cell-group.vue @@ -0,0 +1,69 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/cell-group/props.ts b/uni_modules/tdesign-uniapp/components/cell-group/props.ts new file mode 100644 index 0000000..490c8a1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell-group/props.ts @@ -0,0 +1,25 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCellGroupProps } from './type'; +export default { + /** 是否显示组边框 */ + bordered: Boolean, + /** 单元格组风格 */ + theme: { + type: String, + default: 'default' as TdCellGroupProps['theme'], + validator(val: TdCellGroupProps['theme']): boolean { + if (!val) return true; + return ['default', 'card'].includes(val); + }, + }, + /** 单元格组标题 */ + title: { + type: String, + default: '', + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/cell-group/type.ts b/uni_modules/tdesign-uniapp/components/cell-group/type.ts new file mode 100644 index 0000000..76ed34b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell-group/type.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdCellGroupProps { + /** + * 是否显示组边框 + * @default false + */ + bordered?: boolean; + /** + * 单元格组风格 + * @default default + */ + theme?: 'default' | 'card'; + /** + * 单元格组标题 + * @default '' + */ + title?: string; +} diff --git a/uni_modules/tdesign-uniapp/components/cell/README.en-US.md b/uni_modules/tdesign-uniapp/components/cell/README.en-US.md new file mode 100644 index 0000000..07c2ccd --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell/README.en-US.md @@ -0,0 +1,116 @@ +:: BASE_DOC :: + +## API + +### Cell Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +align | String | middle | options: top/middle/bottom | N +arrow | Boolean / Object | false | \- | N +bordered | Boolean | true | \- | N +description | String | - | \- | N +hover | Boolean | - | \- | N +image | String | - | \- | N +jump-type | String | navigateTo | options: switchTab/reLaunch/redirectTo/navigateTo | N +left-icon | String / Object | - | \- | N +note | String | - | \- | N +note-style | String / Object | - | \- | N +required | Boolean | false | \- | N +right-icon | String / Object | - | \- | N +right-icon-style | String / Object | - | \- | N +title | String | - | \- | N +title-style | String / Object | - | \- | N +url | String | - | \- | N + +### Cell Events + +name | params | description +-- | -- | -- +click | `(e: MouseEvent)` | \- + +### Cell Slots + +name | Description +-- | -- +description | \- +image | \- +left-icon | \- +note | \- +right-icon | \- +title | \- + +### Cell External Classes + +className | Description +-- | -- +t-class | \- +t-class-center | \- +t-class-description | \- +t-class-hover | \- +t-class-image | \- +t-class-left | \- +t-class-left-icon | \- +t-class-note | \- +t-class-right | \- +t-class-right-icon | \- +t-class-title | \- + + +### CellGroup Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +bordered | Boolean | false | \- | N +theme | String | default | options: default/card | N +title | String | - | \- | N + +### CellGroup Slots + +name | Description +-- | -- +\- | \- + +### CellGroup External Classes + +className | Description +-- | -- +t-class | \- +t-class-title | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-cell-group-border-color | @component-stroke | - +--td-cell-group-title-bg-color | @bg-color-secondarycontainer | - +--td-cell-group-title-color | @text-color-placeholder | - +--td-cell-group-title-font-size | 28rpx | - +--td-cell-group-title-line-height | 90rpx | - +--td-cell-group-title-padding-left | 32rpx | - +--td-cell-bg-color | @bg-color-container | - +--td-cell-border-color | @component-stroke | - +--td-cell-border-left-space | @cell-horizontal-padding | - +--td-cell-border-right-space | 0 | - +--td-cell-border-width | 1px | - +--td-cell-description-color | @text-color-secondary | - +--td-cell-description-font | @font-body-medium | - +--td-cell-height | auto | - +--td-cell-horizontal-padding | 32rpx | - +--td-cell-hover-color | @bg-color-secondarycontainer | - +--td-cell-image-height | 96rpx | - +--td-cell-image-width | 96rpx | - +--td-cell-left-icon-color | @brand-color | - +--td-cell-left-icon-size | 48rpx | - +--td-cell-note-color | @text-color-placeholder | - +--td-cell-note-font-size | @font-size-m | - +--td-cell-required-color | @error-color | - +--td-cell-required-font-size | @font-size-m | - +--td-cell-right-icon-color | @text-color-placeholder | - +--td-cell-right-icon-size | 48rpx | - +--td-cell-title-color | @text-color-primary | - +--td-cell-title-font | @font-body-large | - +--td-cell-vertical-padding | 32rpx | - diff --git a/uni_modules/tdesign-uniapp/components/cell/README.md b/uni_modules/tdesign-uniapp/components/cell/README.md new file mode 100644 index 0000000..6f13955 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell/README.md @@ -0,0 +1,147 @@ +--- +title: Cell 单元格 +description: 用于各个类别行的信息展示。 +spline: data +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TCell from '@tdesign/uniapp/cell/cell.vue'; +import TCellGroup from '@tdesign/uniapp/cell-group/cell-group.vue'; +``` + +### 类型 + +单行单元格 + +{{ base }} + +多行单元格 + +{{ multiple }} + +### 样式 + +卡片单元格 + +{{ theme }} + +## API + +### Cell Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +align | String | middle | 右侧内容的对齐方式,默认居中对齐。可选项:top/middle/bottom | N +arrow | Boolean / Object | false | 是否显示右侧箭头 | N +bordered | Boolean | true | 是否显示下边框 | N +description | String | - | 下方内容描述 | N +hover | Boolean | - | 是否开启点击反馈 | N +image | String | - | 主图 | N +jump-type | String | navigateTo | 链接跳转类型。可选项:switchTab/reLaunch/redirectTo/navigateTo | N +left-icon | String / Object | - | 左侧图标,出现在单元格标题的左侧 | N +note | String | - | 和标题同行的说明文字 | N +note-style | String / Object | - | 说明文字自定义样式 | N +required | Boolean | false | 是否显示表单必填星号 | N +right-icon | String / Object | - | 最右侧图标 | N +right-icon-style | String / Object | - | 右侧图标自定义样式 | N +title | String | - | 标题 | N +title-style | String / Object | - | 标题自定义样式 | N +url | String | - | 点击后跳转链接地址。如果值为空,则表示不需要跳转 | N + +### Cell Events + +名称 | 参数 | 描述 +-- | -- | -- +click | `(e: MouseEvent)` | 右侧内容 + +### Cell Slots + +名称 | 描述 +-- | -- +description | 自定义 `description` 显示内容 +image | 自定义 `image` 显示内容 +left-icon | 自定义 `left-icon` 显示内容 +note | 自定义 `note` 显示内容 +right-icon | 自定义 `right-icon` 显示内容 +title | 自定义 `title` 显示内容 + +### Cell External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-center | 中间(`title`, `description`)内容样式类 +t-class-description | 下方描述内容样式类 +t-class-hover | 悬停样式类 +t-class-image | 图片样式类 +t-class-left | 左侧内容样式类 +t-class-left-icon | 左侧图标样式类 +t-class-note | 右侧说明文字样式类 +t-class-right | 右侧内容样式类 +t-class-right-icon | 右侧图标样式类 +t-class-title | 标题样式类 + + +### CellGroup Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +bordered | Boolean | false | 是否显示组边框 | N +theme | String | default | 单元格组风格。可选项:default/card | N +title | String | - | 单元格组标题 | N + +### CellGroup Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 + +### CellGroup External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-title | 标题样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-cell-group-border-color | @component-stroke | - +--td-cell-group-title-bg-color | @bg-color-secondarycontainer | - +--td-cell-group-title-color | @text-color-placeholder | - +--td-cell-group-title-font-size | 28rpx | - +--td-cell-group-title-line-height | 90rpx | - +--td-cell-group-title-padding-left | 32rpx | - +--td-cell-bg-color | @bg-color-container | - +--td-cell-border-color | @component-stroke | - +--td-cell-border-left-space | @cell-horizontal-padding | - +--td-cell-border-right-space | 0 | - +--td-cell-border-width | 1px | - +--td-cell-description-color | @text-color-secondary | - +--td-cell-description-font | @font-body-medium | - +--td-cell-height | auto | - +--td-cell-horizontal-padding | 32rpx | - +--td-cell-hover-color | @bg-color-secondarycontainer | - +--td-cell-image-height | 96rpx | - +--td-cell-image-width | 96rpx | - +--td-cell-left-icon-color | @brand-color | - +--td-cell-left-icon-size | 48rpx | - +--td-cell-note-color | @text-color-placeholder | - +--td-cell-note-font-size | @font-size-m | - +--td-cell-required-color | @error-color | - +--td-cell-required-font-size | @font-size-m | - +--td-cell-right-icon-color | @text-color-placeholder | - +--td-cell-right-icon-size | 48rpx | - +--td-cell-title-color | @text-color-primary | - +--td-cell-title-font | @font-body-large | - +--td-cell-vertical-padding | 32rpx | - diff --git a/uni_modules/tdesign-uniapp/components/cell/cell.css b/uni_modules/tdesign-uniapp/components/cell/cell.css new file mode 100644 index 0000000..cfa66a0 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell/cell.css @@ -0,0 +1,94 @@ +.t-cell { + position: relative; + display: flex; + box-sizing: border-box; + width: 100%; + padding: var(--td-cell-vertical-padding, 32rpx) var(--td-cell-horizontal-padding, 32rpx); + height: var(--td-cell-height, auto); + background-color: var(--td-cell-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-cell--bordered::after { + position: absolute; + box-sizing: border-box; + content: ' '; + pointer-events: none; + right: 0; + left: 0; + bottom: 0; + border-bottom: var(--td-cell-border-width, 1px) solid var(--td-cell-border-color, var(--td-component-stroke, var(--td-gray-color-3, #e7e7e7))); + transform: scaleY(0.5); + transform-origin: bottom; + left: var(--td-cell-border-left-space, var(--td-cell-horizontal-padding, 32rpx)); + right: var(--td-cell-border-right-space, 0); +} +.t-cell__description { + font: var(--td-cell-description-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-cell-description-color, var(--td-text-color-secondary, var(--td-font-gray-2, rgba(0, 0, 0, 0.6)))); +} +.t-cell__description-text { + margin-top: calc(var(--td-spacer, 16rpx) / 2); +} +.t-cell__note { + display: flex; + align-items: center; + justify-content: flex-end; + color: var(--td-cell-note-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + font-size: var(--td-cell-note-font-size, var(--td-font-size-m, 32rpx)); +} +.t-cell__title { + margin-right: var(--td-spacer-2, 32rpx); +} +.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; + font: var(--td-cell-title-font, var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-cell-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-cell__left, +.t-cell__right { + align-self: stretch; +} +.t-cell__left:not(:empty) { + margin-right: var(--td-spacer-1, 24rpx); +} +.t-cell__left-icon { + color: var(--td-cell-left-icon-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + font-size: var(--td-cell-left-icon-size, 48rpx); +} +.t-cell__left-image { + height: var(--td-cell-image-height, 96rpx); + width: var(--td-cell-image-width, 96rpx); +} +.t-cell__note:not(:empty) + .t-cell__right { + margin-left: calc(var(--td-spacer, 16rpx) / 2); +} +.t-cell__right { + display: flex; +} +.t-cell__right-icon { + color: var(--td-cell-right-icon-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + font-size: var(--td-cell-right-icon-size, 48rpx); +} +.t-cell__right--middle { + align-items: center; +} +.t-cell__right--top { + align-items: flex-start; +} +.t-cell__right--bottom { + align-items: flex-end; +} +.t-cell--hover { + background-color: var(--td-cell-hover-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); +} +.t-cell--required { + font-size: var(--td-cell-required-font-size, var(--td-font-size-m, 32rpx)); + color: var(--td-cell-required-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} diff --git a/uni_modules/tdesign-uniapp/components/cell/cell.vue b/uni_modules/tdesign-uniapp/components/cell/cell.vue new file mode 100644 index 0000000..3dc43ff --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell/cell.vue @@ -0,0 +1,260 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/cell/props.ts b/uni_modules/tdesign-uniapp/components/cell/props.ts new file mode 100644 index 0000000..b137491 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell/props.ts @@ -0,0 +1,87 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCellProps } from './type'; +export default { + /** 右侧内容的对齐方式,默认居中对齐 */ + align: { + type: String, + default: 'middle' as TdCellProps['align'], + validator(val: TdCellProps['align']): boolean { + if (!val) return true; + return ['top', 'middle', 'bottom'].includes(val); + }, + }, + /** 是否显示右侧箭头 */ + arrow: { + type: [Boolean, Object], + default: false as TdCellProps['arrow'], + }, + /** 是否显示下边框 */ + bordered: { + type: Boolean, + default: true, + }, + /** 下方内容描述 */ + description: { + type: String, + }, + /** 是否开启点击反馈 */ + hover: Boolean, + /** 主图 */ + image: { + type: String, + }, + /** 链接跳转类型 */ + jumpType: { + type: String, + default: 'navigateTo' as TdCellProps['jumpType'], + validator(val: TdCellProps['jumpType']): boolean { + if (!val) return true; + return ['switchTab', 'reLaunch', 'redirectTo', 'navigateTo'].includes(val); + }, + }, + /** 左侧图标,出现在单元格标题的左侧 */ + leftIcon: { + type: [String, Object], + }, + /** 和标题同行的说明文字 */ + note: { + type: String, + }, + /** 说明文字自定义样式 */ + noteStyle: { + type: [String, Object], + }, + /** 是否显示表单必填星号 */ + required: Boolean, + /** 最右侧图标 */ + rightIcon: { + type: [String, Object], + }, + /** 右侧图标自定义样式 */ + rightIconStyle: { + type: [String, Object], + }, + /** 标题 */ + title: { + type: String, + }, + /** 标题自定义样式 */ + titleStyle: { + type: [String, Object], + }, + /** 点击后跳转链接地址。如果值为空,则表示不需要跳转 */ + url: { + type: String, + default: '', + }, + /** 右侧内容 */ + onClick: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/cell/type.ts b/uni_modules/tdesign-uniapp/components/cell/type.ts new file mode 100644 index 0000000..915dab7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/cell/type.ts @@ -0,0 +1,82 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdCellProps { + /** + * 右侧内容的对齐方式,默认居中对齐 + * @default middle + */ + align?: 'top' | 'middle' | 'bottom'; + /** + * 是否显示右侧箭头 + * @default false + */ + arrow?: boolean | object; + /** + * 是否显示下边框 + * @default true + */ + bordered?: boolean; + /** + * 下方内容描述 + */ + description?: string; + /** + * 是否开启点击反馈 + */ + hover?: boolean; + /** + * 主图 + */ + image?: string; + /** + * 链接跳转类型 + * @default navigateTo + */ + jumpType?: 'switchTab' | 'reLaunch' | 'redirectTo' | 'navigateTo'; + /** + * 左侧图标,出现在单元格标题的左侧 + */ + leftIcon?: string | object; + /** + * 和标题同行的说明文字 + */ + note?: string; + /** + * 说明文字自定义样式 + */ + noteStyle?: string | object; + /** + * 是否显示表单必填星号 + * @default false + */ + required?: boolean; + /** + * 最右侧图标 + */ + rightIcon?: string | object; + /** + * 右侧图标自定义样式 + */ + rightIconStyle?: string | object; + /** + * 标题 + */ + title?: string; + /** + * 标题自定义样式 + */ + titleStyle?: string | object; + /** + * 点击后跳转链接地址。如果值为空,则表示不需要跳转 + * @default '' + */ + url?: string; + /** + * 右侧内容 + */ + onClick?: (e: MouseEvent) => void; +} diff --git a/uni_modules/tdesign-uniapp/components/check-tag/check-tag.css b/uni_modules/tdesign-uniapp/components/check-tag/check-tag.css new file mode 100644 index 0000000..19f29d9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/check-tag/check-tag.css @@ -0,0 +1,197 @@ +.t-tag { + display: inline-flex; + align-items: center; + border: 2rpx solid transparent; + box-sizing: border-box; + border-radius: var(--td-tag-square-border-radius, 8rpx); + user-select: none; + vertical-align: middle; +} +.t-tag__text { + word-wrap: normal; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.t-tag__icon { + display: flex; + align-items: center; +} +.t-tag__icon:not(:empty) + .t-tag__text:not(:empty) { + margin-left: 8rpx; +} +.t-tag--small { + padding: var(--td-tag-small-padding, 2rpx 10rpx); + font: var(--td-tag-small-font, var(--td-font-body-extraSmall, 20rpx / 32rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); +} +.t-tag--small .t-icon, +.t-tag--small .t-icon-close { + font-size: var(--td-tag-small-icon-size, 24rpx); +} +.t-tag--medium { + padding: var(--td-tag-medium-padding, 2rpx 14rpx); + font: var(--td-tag-medium-font, var(--td-font-body-small, 24rpx / 40rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); +} +.t-tag--medium .t-icon, +.t-tag--medium .t-icon-close { + font-size: var(--td-tag-medium-icon-size, 28rpx); +} +.t-tag--large { + padding: var(--td-tag-large-padding, 4rpx 14rpx); + font: var(--td-tag-large-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); +} +.t-tag--large .t-icon, +.t-tag--large .t-icon-close { + font-size: var(--td-tag-large-icon-size, 32rpx); +} +.t-tag--extra-large { + padding: var(--td-tag-extra-large-padding, 16rpx 30rpx); + font: var(--td-tag-extra-large-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); +} +.t-tag--extra-large .t-icon, +.t-tag--extra-large .t-icon-close { + font-size: var(--td-tag-extra-large-icon-size, 32rpx); +} +.t-tag.t-tag--square { + border-radius: var(--td-tag-square-border-radius, 8rpx); +} +.t-tag.t-tag--round { + border-radius: var(--td-tag-round-border-radius, 999px); +} +.t-tag.t-tag--mark { + border-radius: 0 var(--td-tag-mark-border-radius, var(--td-tag-round-border-radius, 999px)) var(--td-tag-mark-border-radius, var(--td-tag-round-border-radius, 999px)) 0; +} +.t-tag--dark.t-tag--default { + color: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + border-color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + background-color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); +} +.t-tag--dark.t-tag--primary { + color: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + border-color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-tag--dark.t-tag--success { + color: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + border-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); + background-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); +} +.t-tag--dark.t-tag--warning { + color: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + border-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); + background-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); +} +.t-tag--dark.t-tag--danger { + color: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + border-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + background-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-tag--dark.t-tag--default { + color: var(--td-tag-default-font-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-tag--outline.t-tag--default { + color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + background-color: var(--td-tag-default-light-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); +} +.t-tag--outline.t-tag--primary { + color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + border-color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: var(--td-tag-primary-light-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-tag--outline.t-tag--success { + color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); + border-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); + background-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e3f9e9)); +} +.t-tag--outline.t-tag--warning { + color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); + border-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); + background-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fff1e9)); +} +.t-tag--outline.t-tag--danger { + color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + border-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + background-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fff0ed)); +} +.t-tag--outline.t-tag--default { + color: var(--td-tag-default-font-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-tag--outline.t-tag--default { + background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-tag--outline.t-tag--primary { + background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-tag--outline.t-tag--success { + background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-tag--outline.t-tag--warning { + background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-tag--outline.t-tag--danger { + background-color: var(--td-tag-outline-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-tag--light.t-tag--default { + color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-tag-default-light-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); + background-color: var(--td-tag-default-light-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); +} +.t-tag--light.t-tag--primary { + color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + border-color: var(--td-tag-primary-light-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); + background-color: var(--td-tag-primary-light-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-tag--light.t-tag--success { + color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); + border-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e3f9e9)); + background-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e3f9e9)); +} +.t-tag--light.t-tag--warning { + color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); + border-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fff1e9)); + background-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fff1e9)); +} +.t-tag--light.t-tag--danger { + color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + border-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fff0ed)); + background-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fff0ed)); +} +.t-tag--light.t-tag--default { + color: var(--td-tag-default-font-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-tag--light-outline.t-tag--default { + color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-tag-default-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + background-color: var(--td-tag-default-light-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); +} +.t-tag--light-outline.t-tag--primary { + color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + border-color: var(--td-tag-primary-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: var(--td-tag-primary-light-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-tag--light-outline.t-tag--success { + color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); + border-color: var(--td-tag-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471))); + background-color: var(--td-tag-success-light-color, var(--td-success-color-1, #e3f9e9)); +} +.t-tag--light-outline.t-tag--warning { + color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); + border-color: var(--td-tag-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318))); + background-color: var(--td-tag-warning-light-color, var(--td-warning-color-1, #fff1e9)); +} +.t-tag--light-outline.t-tag--danger { + color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + border-color: var(--td-tag-danger-color, var(--td-error-color, var(--td-error-color-6, #d54941))); + background-color: var(--td-tag-danger-light-color, var(--td-error-color-1, #fff0ed)); +} +.t-tag--light-outline.t-tag--default { + color: var(--td-tag-default-font-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + border-color: var(--td-component-border, var(--td-gray-color-4, #dcdcdc)); +} +.t-tag.t-tag--closable.t-tag--disabled { + cursor: not-allowed; + color: var(--td-tag-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); + background-color: var(--td-tag-disabled-background-color, var(--td-bg-color-component-disabled, var(--td-gray-color-2, #eeeeee))); + border-color: var(--td-tag-disabled-border-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); +} diff --git a/uni_modules/tdesign-uniapp/components/check-tag/check-tag.vue b/uni_modules/tdesign-uniapp/components/check-tag/check-tag.vue new file mode 100644 index 0000000..d6a9844 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/check-tag/check-tag.vue @@ -0,0 +1,149 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/check-tag/props.ts b/uni_modules/tdesign-uniapp/components/check-tag/props.ts new file mode 100644 index 0000000..fa4e78a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/check-tag/props.ts @@ -0,0 +1,67 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCheckTagProps } from './type'; +export default { + /** 标签选中的状态,默认风格(theme=default)才有选中态 */ + checked: Boolean, + /** 标签选中的状态,默认风格(theme=default)才有选中态,非受控属性 */ + defaultChecked: Boolean, + /** 标签是否可关闭 */ + closable: Boolean, + /** 组件子元素;传入数组时:[选中内容,非选中内容] */ + content: { + type: [String, Number, Array], + }, + /** 标签禁用态,失效标签不能触发事件。默认风格(theme=default)才有禁用态 */ + disabled: Boolean, + /** 标签图标 */ + icon: { + type: [String, Object], + }, + /** 标签类型,有三种:方形、圆角方形、标记型 */ + shape: { + type: String, + default: 'square' as TdCheckTagProps['shape'], + validator(val: TdCheckTagProps['shape']): boolean { + if (!val) return true; + return ['square', 'round', 'mark'].includes(val); + }, + }, + /** 标签尺寸 */ + size: { + type: String, + default: 'medium' as TdCheckTagProps['size'], + validator(val: TdCheckTagProps['size']): boolean { + if (!val) return true; + return ['small', 'medium', 'large'].includes(val); + }, + }, + /** 标签风格变体 */ + variant: { + type: String, + default: 'dark' as TdCheckTagProps['variant'], + validator(val: TdCheckTagProps['variant']): boolean { + if (!val) return true; + return ['dark', 'light', 'outline', 'light-outline'].includes(val); + }, + }, + /** 状态切换时触发 */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 点击标签时触发 */ + onClick: { + type: Function, + default: () => ({}), + }, + /** 如果关闭按钮存在,点击关闭按钮时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/check-tag/type.ts b/uni_modules/tdesign-uniapp/components/check-tag/type.ts new file mode 100644 index 0000000..7fe35c5 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/check-tag/type.ts @@ -0,0 +1,63 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { SizeEnum } from '../common/common'; + +export interface TdCheckTagProps { + /** + * 标签选中的状态,默认风格(theme=default)才有选中态 + */ + checked?: boolean; + /** + * 标签选中的状态,默认风格(theme=default)才有选中态,非受控属性 + */ + defaultChecked?: boolean; + /** + * 标签是否可关闭 + * @default false + */ + closable?: boolean; + /** + * 组件子元素;传入数组时:[选中内容,非选中内容] + */ + content?: string | number | string[]; + /** + * 标签禁用态,失效标签不能触发事件。默认风格(theme=default)才有禁用态 + * @default false + */ + disabled?: boolean; + /** + * 标签图标 + */ + icon?: string | object; + /** + * 标签类型,有三种:方形、圆角方形、标记型 + * @default square + */ + shape?: 'square' | 'round' | 'mark'; + /** + * 标签尺寸 + * @default medium + */ + size?: SizeEnum; + /** + * 标签风格变体 + * @default dark + */ + variant?: 'dark' | 'light' | 'outline' | 'light-outline'; + /** + * 状态切换时触发 + */ + onChange?: (context: { checked: boolean }) => void; + /** + * 点击标签时触发 + */ + onClick?: () => void; + /** + * 如果关闭按钮存在,点击关闭按钮时触发 + */ + onClose?: () => void; +} diff --git a/uni_modules/tdesign-uniapp/components/checkbox-group/checkbox-group.css b/uni_modules/tdesign-uniapp/components/checkbox-group/checkbox-group.css new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/tdesign-uniapp/components/checkbox-group/checkbox-group.vue b/uni_modules/tdesign-uniapp/components/checkbox-group/checkbox-group.vue new file mode 100644 index 0000000..ce4bffe --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox-group/checkbox-group.vue @@ -0,0 +1,261 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/checkbox-group/props.ts b/uni_modules/tdesign-uniapp/components/checkbox-group/props.ts new file mode 100644 index 0000000..18179ae --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox-group/props.ts @@ -0,0 +1,58 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCheckboxGroupProps } from './type'; +export default { + /** 是否开启无边框模式。优先级低于 Checkbox.borderless */ + borderless: Boolean, + /** 是否禁用组件。优先级:Form.disabled < CheckboxGroup.disabled < Checkbox.disabled */ + disabled: { + type: Boolean, + default: undefined, + }, + /** 用来定义 value / label / disabled 在 `options` 中对应的字段别名 */ + keys: { + type: Object, + }, + /** 支持最多选中的数量 */ + max: { + type: Number, + default: undefined, + }, + /** 统一设置内部复选框 HTML 属性 */ + name: { + type: String, + default: '', + }, + /** 以配置形式设置子元素。示例1:`['北京', '上海']` ,示例2: `[{ label: '全选', checkAll: true }, { label: '上海', value: 'shanghai' }]`。checkAll 值为 true 表示当前选项为「全选选项」 */ + options: { + type: Array, + default: (): TdCheckboxGroupProps['options'] => [], + }, + /** 只读状态 */ + readonly: { + type: Boolean, + default: undefined, + }, + /** -1 时代表独立,不再寻找 parent,用于头条小程序 */ + relationKey: { + type: String, + default: '', + }, + /** 选中值 */ + value: { + type: Array, + }, + /** 选中值,非受控属性 */ + defaultValue: { + type: Array, + }, + /** 值变化时触发。`context` 表示当前点击项内容 */ + onChange: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/checkbox-group/type.ts b/uni_modules/tdesign-uniapp/components/checkbox-group/type.ts new file mode 100644 index 0000000..004c68c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox-group/type.ts @@ -0,0 +1,72 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { KeysType } from '../common/common'; + +export interface TdCheckboxGroupProps { + /** + * 是否开启无边框模式。优先级低于 Checkbox.borderless + * @default false + */ + borderless?: boolean; + /** + * 是否禁用组件。优先级:Form.disabled < CheckboxGroup.disabled < Checkbox.disabled + */ + disabled?: boolean; + /** + * 用来定义 value / label / disabled 在 `options` 中对应的字段别名 + */ + keys?: KeysType; + /** + * 支持最多选中的数量 + */ + max?: number; + /** + * 统一设置内部复选框 HTML 属性 + * @default '' + */ + name?: string; + /** + * 以配置形式设置子元素。示例1:`['北京', '上海']` ,示例2: `[{ label: '全选', checkAll: true }, { label: '上海', value: 'shanghai' }]`。checkAll 值为 true 表示当前选项为「全选选项」 + * @default [] + */ + options?: Array; + /** + * 只读状态 + */ + readonly?: boolean; + /** + * -1 时代表独立,不再寻找 parent,用于头条小程序 + * @default '' + */ + relationKey?: string; + /** + * 选中值 + */ + value?: T; + /** + * 选中值,非受控属性 + */ + defaultValue?: T; + /** + * 值变化时触发。`context` 表示当前点击项内容 + */ + onChange?: (context: { + value: CheckboxGroupValue; + context: { value: boolean | number | string; label: boolean | number | string }; + }) => void; +} + +export type CheckboxOption = string | number | CheckboxOptionObj; + +export interface CheckboxOptionObj { + label?: string; + value?: string | number; + disabled?: boolean; + checkAll?: true; +} + +export type CheckboxGroupValue = Array; diff --git a/uni_modules/tdesign-uniapp/components/checkbox/README.en-US.md b/uni_modules/tdesign-uniapp/components/checkbox/README.en-US.md new file mode 100644 index 0000000..40ae09b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox/README.en-US.md @@ -0,0 +1,103 @@ +:: BASE_DOC :: + +## API + +### Checkbox Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +block | Boolean | true | \- | N +borderless | Boolean | undefined | \- | N +check-all | Boolean | false | \- | N +checked | Boolean | - | `v-model:checked` is supported | N +default-checked | Boolean | - | uncontrolled property | N +content | String | - | \- | N +content-disabled | Boolean | - | \- | N +disabled | Boolean | undefined | \- | N +icon | String / Array | 'circle' | Typescript:`'circle' \| 'line' \| 'rectangle' \| string[]` | N +indeterminate | Boolean | false | \- | N +label | String | - | \- | N +max-content-row | Number | 5 | \- | N +max-label-row | Number | 3 | \- | N +name | String | - | \- | N +placement | String | left | options: left/right | N +readonly | Boolean | undefined | \- | N +relation-key | String | - | \- | N +value | String / Number / Boolean | - | value of checkbox。Typescript:`string \| number \| boolean` | N + +### Checkbox Events + +name | params | description +-- | -- | -- +change | `(context: { checked: boolean, context: { value: boolean\|number\|string, label: boolean\|number\|string }})` | \- + +### Checkbox Slots + +name | Description +-- | -- +\- | \- +content | \- +label | \- + +### Checkbox External Classes + +className | Description +-- | -- +t-class | \- +t-class-border | \- +t-class-content | \- +t-class-icon | \- +t-class-label | \- + + +### CheckboxGroup Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +borderless | Boolean | false | \- | N +disabled | Boolean | undefined | \- | N +keys | Object | - | Typescript:`KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +max | Number | undefined | \- | N +name | String | - | \- | N +options | Array | [] | Typescript:`Array` `type CheckboxOption = string \| number \| CheckboxOptionObj` `interface CheckboxOptionObj { label?: string; value?: string \| number; disabled?: boolean; checkAll?: true }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/checkbox-group/type.ts) | N +readonly | Boolean | undefined | \- | N +relation-key | String | - | \- | N +value | Array | - | `v-model:value` is supported。Typescript:`T` `type CheckboxGroupValue = Array`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/checkbox-group/type.ts) | N +default-value | Array | - | uncontrolled property。Typescript:`T` `type CheckboxGroupValue = Array`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/checkbox-group/type.ts) | N + +### CheckboxGroup Events + +name | params | description +-- | -- | -- +change | `(context: { value: CheckboxGroupValue, context: { value: boolean\|number\|string, label: boolean\|number\|string }})` | \- + +### CheckboxGroup Slots + +name | Description +-- | -- +\- | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-checkbox-bg-color | @bg-color-container | - +--td-checkbox-border-color | @component-stroke | - +--td-checkbox-description-color | @text-color-secondary | - +--td-checkbox-description-disabled-color | @text-color-disabled | - +--td-checkbox-description-font | @font-body-medium | - +--td-checkbox-icon-checked-color | @brand-color | - +--td-checkbox-icon-color | @component-border | - +--td-checkbox-icon-disabled-bg-color | @bg-color-component-disabled | - +--td-checkbox-icon-disabled-color | @brand-color-disabled | - +--td-checkbox-icon-size | 48rpx | - +--td-checkbox-tag-active-bg-color | @brand-color-light | - +--td-checkbox-tag-active-color | @brand-color | - +--td-checkbox-title-color | @text-color-primary | - +--td-checkbox-title-disabled-color | @text-color-disabled | - +--td-checkbox-title-font | @font-body-large | - +--td-checkbox-title-line-height | 48rpx | - +--td-checkbox-vertical-padding | @spacer-2 | - diff --git a/uni_modules/tdesign-uniapp/components/checkbox/README.md b/uni_modules/tdesign-uniapp/components/checkbox/README.md new file mode 100644 index 0000000..f43a1fc --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox/README.md @@ -0,0 +1,158 @@ +--- +title: Checkbox 多选框 +description: 用于预设的一组选项中执行多项选择,并呈现选择结果。 +spline: form +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TCheckbox from '@tdesign/uniapp/checkbox/checkbox.vue'; +import TCheckboxGroup from '@tdesign/uniapp/checkbox-group/checkbox-group.vue'; +``` + +### 组件类型 + +纵向多选框 + +{{ base }} + +横向多选框 + +{{ horizontal }} + +带全选多选框 + +{{ all }} + +### 组件状态 + +多选框状态 + +{{ status }} + +### 组件样式 + +勾选样式 + +{{ type }} + +勾选显示位置 + +{{ right }} + +非通栏多选样式 + +{{ card }} + +### 组件规格 + +多选框尺寸规格 + +{{ special }} + +## API + +### Checkbox Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +block | Boolean | true | 是否为块级元素 | N +borderless | Boolean | undefined | 是否开启无边框模式 | N +check-all | Boolean | false | 用于标识是否为「全选选项」。单独使用无效,需在 CheckboxGroup 中使用 | N +checked | Boolean | - | 是否选中。支持语法糖 `v-model:checked` | N +default-checked | Boolean | - | 是否选中。非受控属性 | N +content | String | - | 多选框内容 | N +content-disabled | Boolean | - | 是否禁用组件内容(content)触发选中 | N +disabled | Boolean | undefined | 是否禁用组件。如果父组件存在 CheckboxGroup,默认值由 CheckboxGroup.disabled 控制。优先级:Checkbox.disabled > CheckboxGroup.disabled > Form.disabled | N +icon | String / Array | 'circle' | 自定义选中图标和非选中图标。使用 Array 时表示:`[选中态图标,非选中态图标,半选中态图标]`。使用 String 时,值为 circle 表示填充圆形图标、值为 line 表示描边型图标、值为 rectangle 表示填充矩形图标。TS 类型:`'circle' \| 'line' \| 'rectangle' \| string[]` | N +indeterminate | Boolean | false | 是否为半选 | N +label | String | - | 主文案 | N +max-content-row | Number | 5 | 内容最大行数限制 | N +max-label-row | Number | 3 | 主文案最大行数限制 | N +name | String | - | HTML 元素原生属性 | N +placement | String | left | 多选框和内容相对位置。可选项:left/right | N +readonly | Boolean | undefined | 只读状态 | N +relation-key | String | - | -1 时代表独立,不再寻找 parent,用于头条小程序 | N +value | String / Number / Boolean | - | 多选框的值。TS 类型:`string \| number \| boolean` | N + +### Checkbox Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { checked: boolean, context: { value: boolean\|number\|string, label: boolean\|number\|string }})` | 值变化时触发。`context` 表示当前点击项内容 + +### Checkbox Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,主文案 +content | 自定义 `content` 显示内容 +label | 自定义 `label` 显示内容 + +### Checkbox External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-border | 边框样式类 +t-class-content | 内容样式类 +t-class-icon | 图标样式类 +t-class-label | 标签样式类 + + +### CheckboxGroup Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +borderless | Boolean | false | 是否开启无边框模式。优先级低于 Checkbox.borderless | N +disabled | Boolean | undefined | 是否禁用组件。优先级:Form.disabled < CheckboxGroup.disabled < Checkbox.disabled | N +keys | Object | - | 用来定义 value / label / disabled 在 `options` 中对应的字段别名。TS 类型:`KeysType`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +max | Number | undefined | 支持最多选中的数量 | N +name | String | - | 统一设置内部复选框 HTML 属性 | N +options | Array | [] | 以配置形式设置子元素。示例1:`['北京', '上海']` ,示例2: `[{ label: '全选', checkAll: true }, { label: '上海', value: 'shanghai' }]`。checkAll 值为 true 表示当前选项为「全选选项」。TS 类型:`Array` `type CheckboxOption = string \| number \| CheckboxOptionObj` `interface CheckboxOptionObj { label?: string; value?: string \| number; disabled?: boolean; checkAll?: true }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/checkbox-group/type.ts) | N +readonly | Boolean | undefined | 只读状态 | N +relation-key | String | - | -1 时代表独立,不再寻找 parent,用于头条小程序 | N +value | Array | - | 选中值。支持语法糖 `v-model:value`。TS 类型:`T` `type CheckboxGroupValue = Array`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/checkbox-group/type.ts) | N +default-value | Array | - | 选中值。非受控属性。TS 类型:`T` `type CheckboxGroupValue = Array`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/checkbox-group/type.ts) | N + +### CheckboxGroup Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { value: CheckboxGroupValue, context: { value: boolean\|number\|string, label: boolean\|number\|string }})` | 值变化时触发。`context` 表示当前点击项内容 + +### CheckboxGroup Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,多选框组内容 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-checkbox-bg-color | @bg-color-container | - +--td-checkbox-border-color | @component-stroke | - +--td-checkbox-description-color | @text-color-secondary | - +--td-checkbox-description-disabled-color | @text-color-disabled | - +--td-checkbox-description-font | @font-body-medium | - +--td-checkbox-icon-checked-color | @brand-color | - +--td-checkbox-icon-color | @component-border | - +--td-checkbox-icon-disabled-bg-color | @bg-color-component-disabled | - +--td-checkbox-icon-disabled-color | @brand-color-disabled | - +--td-checkbox-icon-size | 48rpx | - +--td-checkbox-tag-active-bg-color | @brand-color-light | - +--td-checkbox-tag-active-color | @brand-color | - +--td-checkbox-title-color | @text-color-primary | - +--td-checkbox-title-disabled-color | @text-color-disabled | - +--td-checkbox-title-font | @font-body-large | - +--td-checkbox-title-line-height | 48rpx | - +--td-checkbox-vertical-padding | @spacer-2 | - diff --git a/uni_modules/tdesign-uniapp/components/checkbox/checkbox.css b/uni_modules/tdesign-uniapp/components/checkbox/checkbox.css new file mode 100644 index 0000000..1b8ebe3 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox/checkbox.css @@ -0,0 +1,174 @@ +.t-checkbox { + display: inline-flex; + vertical-align: middle; + position: relative; + background: var(--td-checkbox-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-checkbox:focus { + outline: 0; +} +.t-checkbox--block { + display: flex; + padding: var(--td-checkbox-vertical-padding, var(--td-spacer-2, 32rpx)); +} +.t-checkbox--right { + flex-direction: row-reverse; +} +.t-checkbox .limit-title-row { + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; +} +.t-checkbox .image-center { + position: absolute; + top: 50%; + transform: translateY(-50%); +} +.t-checkbox__icon-left { + margin-right: 20rpx; + width: 40rpx; +} +.t-checkbox__icon-right { + right: 0px; + display: contents; + position: absolute; + top: 50%; + transform: translateY(-50%); +} +.t-checkbox__icon-image { + width: var(--td-checkbox-icon-size, 48rpx); + height: var(--td-checkbox-icon-size, 48rpx); + vertical-align: top; +} +.t-checkbox__icon { + position: relative; + display: block; + width: var(--td-checkbox-icon-size, 48rpx); + height: var(--td-checkbox-icon-size, 48rpx); + color: var(--td-checkbox-icon-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); + font-size: var(--td-checkbox-icon-size, 48rpx); + margin-top: calc((var(--td-checkbox-title-line-height, 48rpx) - var(--td-checkbox-icon-size, 48rpx)) / 2); +} +.t-checkbox__icon:empty { + display: none; +} +.t-checkbox__icon--checked { + color: var(--td-checkbox-icon-checked-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-checkbox__icon--disabled { + cursor: not-allowed; + color: var(--td-checkbox-icon-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-checkbox__icon--left { + margin-right: 16rpx; +} +.t-checkbox__icon-circle { + width: calc((var(--td-checkbox-icon-size, 48rpx) - 4rpx) * 2); + height: calc((var(--td-checkbox-icon-size, 48rpx) - 4rpx) * 2); + border: calc(4rpx * 2) solid var(--td-checkbox-icon-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); + border-radius: 50%; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.5); + box-sizing: border-box; +} +.t-checkbox__icon-circle--disabled { + background: var(--td-checkbox-icon-disabled-bg-color, var(--td-bg-color-component-disabled, var(--td-gray-color-2, #eeeeee))); +} +.t-checkbox__icon-rectangle { + width: calc((var(--td-checkbox-icon-size, 48rpx) - 4rpx * 2) * 2); + height: calc((var(--td-checkbox-icon-size, 48rpx) - 4rpx * 2) * 2); + border: calc(4rpx * 2) solid var(--td-checkbox-icon-color, var(--td-component-border, var(--td-gray-color-4, #dcdcdc))); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%) scale(0.5); + box-sizing: border-box; +} +.t-checkbox__icon-rectangle--disabled { + background: var(--td-checkbox-icon-disabled-bg-color, var(--td-bg-color-component-disabled, var(--td-gray-color-2, #eeeeee))); +} +.t-checkbox__icon-line::before, +.t-checkbox__icon-line::after { + content: ''; + display: block; + position: absolute; + width: 5rpx; + border-radius: 2rpx; + background: var(--td-checkbox-icon-checked-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + transform-origin: top center; +} +.t-checkbox__icon-line::before { + height: 16rpx; + left: 8rpx; + top: 22rpx; + transform: rotate(-45deg); +} +.t-checkbox__icon-line::after { + height: 26rpx; + right: 8rpx; + top: 14rpx; + transform: rotate(45deg); +} +.t-checkbox__icon-line--disabled::before, +.t-checkbox__icon-line--disabled::after { + background: var(--td-checkbox-icon-disabled-color, var(--td-brand-color-disabled, var(--td-primary-color-3, #b5c7ff))); +} +.t-checkbox__content { + flex: 1; +} +.t-checkbox__title { + color: var(--td-checkbox-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + font: var(--td-checkbox-title-font, var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + line-height: var(--td-checkbox-title-line-height, 48rpx); + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; +} +.t-checkbox__title--disabled { + color: var(--td-checkbox-title-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-checkbox__description { + color: var(--td-checkbox-description-color, var(--td-text-color-secondary, var(--td-font-gray-2, rgba(0, 0, 0, 0.6)))); + display: -webkit-box; + -webkit-box-orient: vertical; + overflow: hidden; + font: var(--td-checkbox-description-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); +} +.t-checkbox__description--disabled { + color: var(--td-checkbox-description-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-checkbox__title + .t-checkbox__description:not(:empty) { + margin-top: 8rpx; +} +.t-checkbox__border { + position: absolute; + bottom: 0; + left: 96rpx; + right: 0; + height: 1px; + background: var(--td-checkbox-border-color, var(--td-component-stroke, var(--td-gray-color-3, #e7e7e7))); + transform: scaleY(0.5); +} +.t-checkbox__border--right { + left: 32rpx; +} +.t-checkbox--tag { + font-size: 28rpx; + padding-top: 16rpx; + padding-bottom: 16rpx; + text-align: center; + background-color: var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3)); + border-radius: 12rpx; +} +.t-checkbox--tag.t-checkbox--checked { + color: var(--td-checkbox-tag-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + background-color: var(--td-checkbox-tag-active-bg-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff))); +} +.t-checkbox--tag .t-checkbox__title--checked { + color: var(--td-checkbox-tag-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-checkbox--tag .t-checkbox__content { + margin-right: 0; +} diff --git a/uni_modules/tdesign-uniapp/components/checkbox/checkbox.vue b/uni_modules/tdesign-uniapp/components/checkbox/checkbox.vue new file mode 100644 index 0000000..0f08ebb --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox/checkbox.vue @@ -0,0 +1,238 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/checkbox/props.ts b/uni_modules/tdesign-uniapp/components/checkbox/props.ts new file mode 100644 index 0000000..34410cd --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox/props.ts @@ -0,0 +1,90 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCheckboxProps } from './type'; +export default { + /** 是否为块级元素 */ + block: { + type: Boolean, + default: true, + }, + /** 是否开启无边框模式 */ + borderless: { + type: Boolean, + default: undefined, + }, + /** 用于标识是否为「全选选项」。单独使用无效,需在 CheckboxGroup 中使用 */ + checkAll: Boolean, + /** 是否选中 */ + checked: Boolean, + /** 是否选中,非受控属性 */ + defaultChecked: Boolean, + /** 多选框内容 */ + content: { + type: String, + }, + /** 是否禁用组件内容(content)触发选中 */ + contentDisabled: Boolean, + /** 是否禁用组件。如果父组件存在 CheckboxGroup,默认值由 CheckboxGroup.disabled 控制。优先级:Checkbox.disabled > CheckboxGroup.disabled > Form.disabled */ + disabled: { + type: Boolean, + default: undefined, + }, + /** 自定义选中图标和非选中图标。使用 Array 时表示:`[选中态图标,非选中态图标,半选中态图标]`。使用 String 时,值为 circle 表示填充圆形图标、值为 line 表示描边型图标、值为 rectangle 表示填充矩形图标 */ + icon: { + type: [String, Array], + default: 'circle' as TdCheckboxProps['icon'], + }, + /** 是否为半选 */ + indeterminate: Boolean, + /** 主文案 */ + label: { + type: String, + }, + /** 内容最大行数限制 */ + maxContentRow: { + type: Number, + default: 5, + }, + /** 主文案最大行数限制 */ + maxLabelRow: { + type: Number, + default: 3, + }, + /** HTML 元素原生属性 */ + name: { + type: String, + default: '', + }, + /** 多选框和内容相对位置 */ + placement: { + type: String, + default: 'left' as TdCheckboxProps['placement'], + validator(val: TdCheckboxProps['placement']): boolean { + if (!val) return true; + return ['left', 'right'].includes(val); + }, + }, + /** 只读状态 */ + readonly: { + type: Boolean, + default: undefined, + }, + /** -1 时代表独立,不再寻找 parent,用于头条小程序 */ + relationKey: { + type: String, + default: '', + }, + /** 多选框的值 */ + value: { + type: [String, Number, Boolean], + }, + /** 值变化时触发。`context` 表示当前点击项内容 */ + onChange: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/checkbox/type.ts b/uni_modules/tdesign-uniapp/components/checkbox/type.ts new file mode 100644 index 0000000..d5b9659 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/checkbox/type.ts @@ -0,0 +1,96 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdCheckboxProps { + /** + * 是否为块级元素 + * @default true + */ + block?: boolean; + /** + * 是否开启无边框模式 + */ + borderless?: boolean; + /** + * 用于标识是否为「全选选项」。单独使用无效,需在 CheckboxGroup 中使用 + * @default false + */ + checkAll?: boolean; + /** + * 是否选中 + */ + checked?: boolean; + /** + * 是否选中,非受控属性 + */ + defaultChecked?: boolean; + /** + * 多选框内容 + */ + content?: string; + /** + * 是否禁用组件内容(content)触发选中 + */ + contentDisabled?: boolean; + /** + * 是否禁用组件。如果父组件存在 CheckboxGroup,默认值由 CheckboxGroup.disabled 控制。优先级:Checkbox.disabled > CheckboxGroup.disabled > Form.disabled + */ + disabled?: boolean; + /** + * 自定义选中图标和非选中图标。使用 Array 时表示:`[选中态图标,非选中态图标,半选中态图标]`。使用 String 时,值为 circle 表示填充圆形图标、值为 line 表示描边型图标、值为 rectangle 表示填充矩形图标 + * @default 'circle' + */ + icon?: 'circle' | 'line' | 'rectangle' | string[]; + /** + * 是否为半选 + * @default false + */ + indeterminate?: boolean; + /** + * 主文案 + */ + label?: string; + /** + * 内容最大行数限制 + * @default 5 + */ + maxContentRow?: number; + /** + * 主文案最大行数限制 + * @default 3 + */ + maxLabelRow?: number; + /** + * HTML 元素原生属性 + * @default '' + */ + name?: string; + /** + * 多选框和内容相对位置 + * @default left + */ + placement?: 'left' | 'right'; + /** + * 只读状态 + */ + readonly?: boolean; + /** + * -1 时代表独立,不再寻找 parent,用于头条小程序 + * @default '' + */ + relationKey?: string; + /** + * 多选框的值 + */ + value?: string | number | boolean; + /** + * 值变化时触发。`context` 表示当前点击项内容 + */ + onChange?: (context: { + checked: boolean; + context: { value: boolean | number | string; label: boolean | number | string }; + }) => void; +} diff --git a/uni_modules/tdesign-uniapp/components/col/README.en-US.md b/uni_modules/tdesign-uniapp/components/col/README.en-US.md new file mode 100644 index 0000000..2fd9278 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/README.en-US.md @@ -0,0 +1,31 @@ +:: BASE_DOC :: + +## API + +### Col Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +offset | String / Number | - | \- | N +span | String / Number | - | \- | N + +### Col Slots + +name | Description +-- | -- +\- | \- + + +### Row Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +gutter | String / Number | - | \- | N + +### Row Slots + +name | Description +-- | -- +\- | \- diff --git a/uni_modules/tdesign-uniapp/components/col/README.md b/uni_modules/tdesign-uniapp/components/col/README.md new file mode 100644 index 0000000..eb6dd93 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/README.md @@ -0,0 +1,60 @@ +--- +title: Layout 布局 +description: 以规则的网格阵列来指导和规范页面中的版面布局以及信息分布,提高界面内布局的一致性,节约成本。 +spline: base +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + + +```js +import TRow from '@tdesign/uniapp/row/row.vue'; +import TCol from '@tdesign/uniapp/col/col.vue'; +``` + +### 组件类型 + +基础 + +{{ base }} + + +增加间距 + +{{ offset }} + + + +## API + +### Col Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +offset | String / Number | - | 列的偏移量(默认单位px) | N +span | String / Number | - | 列的宽度(默认单位px) | N + +### Col Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,列内容 + + +### Row Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +gutter | String / Number | - | 列之间的间距(默认单位px) | N + +### Row Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,行内容 diff --git a/uni_modules/tdesign-uniapp/components/col/col.css b/uni_modules/tdesign-uniapp/components/col/col.css new file mode 100644 index 0000000..1eb610c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/col.css @@ -0,0 +1,149 @@ +.t-col { + display: block; + box-sizing: border-box; + min-height: 1px; +} +.t-col--1 { + width: 4.16666667%; +} +.t-col--offset-1 { + margin-left: 4.16666667%; +} +.t-col--2 { + width: 8.33333333%; +} +.t-col--offset-2 { + margin-left: 8.33333333%; +} +.t-col--3 { + width: 12.5%; +} +.t-col--offset-3 { + margin-left: 12.5%; +} +.t-col--4 { + width: 16.66666667%; +} +.t-col--offset-4 { + margin-left: 16.66666667%; +} +.t-col--5 { + width: 20.83333333%; +} +.t-col--offset-5 { + margin-left: 20.83333333%; +} +.t-col--6 { + width: 25%; +} +.t-col--offset-6 { + margin-left: 25%; +} +.t-col--7 { + width: 29.16666667%; +} +.t-col--offset-7 { + margin-left: 29.16666667%; +} +.t-col--8 { + width: 33.33333333%; +} +.t-col--offset-8 { + margin-left: 33.33333333%; +} +.t-col--9 { + width: 37.5%; +} +.t-col--offset-9 { + margin-left: 37.5%; +} +.t-col--10 { + width: 41.66666667%; +} +.t-col--offset-10 { + margin-left: 41.66666667%; +} +.t-col--11 { + width: 45.83333333%; +} +.t-col--offset-11 { + margin-left: 45.83333333%; +} +.t-col--12 { + width: 50%; +} +.t-col--offset-12 { + margin-left: 50%; +} +.t-col--13 { + width: 54.16666667%; +} +.t-col--offset-13 { + margin-left: 54.16666667%; +} +.t-col--14 { + width: 58.33333333%; +} +.t-col--offset-14 { + margin-left: 58.33333333%; +} +.t-col--15 { + width: 62.5%; +} +.t-col--offset-15 { + margin-left: 62.5%; +} +.t-col--16 { + width: 66.66666667%; +} +.t-col--offset-16 { + margin-left: 66.66666667%; +} +.t-col--17 { + width: 70.83333333%; +} +.t-col--offset-17 { + margin-left: 70.83333333%; +} +.t-col--18 { + width: 75%; +} +.t-col--offset-18 { + margin-left: 75%; +} +.t-col--19 { + width: 79.16666667%; +} +.t-col--offset-19 { + margin-left: 79.16666667%; +} +.t-col--20 { + width: 83.33333333%; +} +.t-col--offset-20 { + margin-left: 83.33333333%; +} +.t-col--21 { + width: 87.5%; +} +.t-col--offset-21 { + margin-left: 87.5%; +} +.t-col--22 { + width: 91.66666667%; +} +.t-col--offset-22 { + margin-left: 91.66666667%; +} +.t-col--23 { + width: 95.83333333%; +} +.t-col--offset-23 { + margin-left: 95.83333333%; +} +.t-col--24 { + width: 100%; +} +.t-col--offset-24 { + margin-left: 100%; +} diff --git a/uni_modules/tdesign-uniapp/components/col/col.vue b/uni_modules/tdesign-uniapp/components/col/col.vue new file mode 100644 index 0000000..d7ba015 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/col.vue @@ -0,0 +1,58 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/col/computed.js b/uni_modules/tdesign-uniapp/components/col/computed.js new file mode 100644 index 0000000..1aa29e8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/computed.js @@ -0,0 +1,14 @@ +import utils from '../common/utils.wxs'; + +export function getColStyles(gutter, customStyle) { + let _style = ''; + if (gutter) { + _style = utils._style({ + 'padding-right': utils.addUnit(gutter / 2), + 'padding-left': utils.addUnit(gutter / 2), + }); + } + + return utils._style([customStyle]) + _style; +} + diff --git a/uni_modules/tdesign-uniapp/components/col/props.ts b/uni_modules/tdesign-uniapp/components/col/props.ts new file mode 100644 index 0000000..eacc90d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/props.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export default { + /** 列的偏移量(默认单位px) */ + offset: { + type: [String, Number], + }, + /** 列的宽度(默认单位px) */ + span: { + type: [String, Number], + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/col/type.ts b/uni_modules/tdesign-uniapp/components/col/type.ts new file mode 100644 index 0000000..b8f348f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/col/type.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdColProps { + /** + * 列的偏移量(默认单位px) + */ + offset?: string | number; + /** + * 列的宽度(默认单位px) + */ + span?: string | number; +} diff --git a/uni_modules/tdesign-uniapp/components/collapse-panel/collapse-panel.css b/uni_modules/tdesign-uniapp/components/collapse-panel/collapse-panel.css new file mode 100644 index 0000000..9bdd3cc --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse-panel/collapse-panel.css @@ -0,0 +1,154 @@ +.t-collapse-panel { + background-color: var(--td-collapse-panel-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); +} +.t-collapse-panel--disabled { + pointer-events: none; +} +.t-collapse-panel--disabled .t-collapse-panel__content, +.t-collapse-panel--disabled .t-collapse-panel__header { + opacity: 0.3; +} +.t-collapse-panel--top { + display: flex; + flex-direction: column-reverse; +} +.t-collapse-panel__header { + position: relative; + display: flex; + justify-content: space-between; + align-items: center; + padding-left: var(--td-collapse-horizontal-padding, 32rpx); + height: var(--td-collapse-header-height, auto); +} +.t-collapse-panel__header--top { + position: relative; +} +.t-collapse-panel__header--top::after { + content: ''; + display: block; + position: absolute; + top: 0; + bottom: unset; + left: unset; + right: unset; + background-color: var(--td-collapse-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7))); +} +.t-collapse-panel__header--top::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-collapse-panel__header--bottom { + position: relative; +} +.t-collapse-panel__header--bottom::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-collapse-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7))); +} +.t-collapse-panel__header--bottom::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-collapse-panel__header::after { + left: var(--td-spacer-2, 32rpx); +} +.t-collapse-panel__extra { + font: var(--td-collapse-extra-font, var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); +} +.t-collapse-panel__body { + position: relative; +} +.t-collapse-panel__body::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-collapse-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7))); +} +.t-collapse-panel__body::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-collapse-panel__wrapper { + height: 0; + overflow: hidden; +} +.t-collapse-panel__content { + color: var(--td-collapse-content-text-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); + font: var(--td-collapse-content-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + padding: var(--td-collapse-content-padding, 32rpx); +} +.t-collapse-panel__content--disabled { + color: var(--td-collapse-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +.t-collapse-panel__content--expanded.t-collapse-panel__content--bottom { + position: relative; +} +.t-collapse-panel__content--expanded.t-collapse-panel__content--bottom::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-collapse-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7))); +} +.t-collapse-panel__content--expanded.t-collapse-panel__content--bottom::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-collapse-panel__content--expanded.t-collapse-panel__content--top { + position: relative; +} +.t-collapse-panel__content--expanded.t-collapse-panel__content--top::after { + content: ''; + display: block; + position: absolute; + top: 0; + bottom: unset; + left: unset; + right: unset; + background-color: var(--td-collapse-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7))); +} +.t-collapse-panel__content--expanded.t-collapse-panel__content--top::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-collapse-panel__arrow--top { + transform: rotate(180deg); +} +.class-title { + font: var(--td-collapse-title-font, var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-collapse-header-text-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.class-left-icon { + color: var(--td-collapse-left-icon-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.class-right-icon { + color: var(--td-collapse-icon-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); +} +.class-title--disabled, +.class-note--disabled, +.class-left-icon--disabled, +.class-right-icon--disabled { + color: var(--td-collapse-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} diff --git a/uni_modules/tdesign-uniapp/components/collapse-panel/collapse-panel.vue b/uni_modules/tdesign-uniapp/components/collapse-panel/collapse-panel.vue new file mode 100644 index 0000000..27eaef7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse-panel/collapse-panel.vue @@ -0,0 +1,238 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/collapse-panel/props.ts b/uni_modules/tdesign-uniapp/components/collapse-panel/props.ts new file mode 100644 index 0000000..fc62766 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse-panel/props.ts @@ -0,0 +1,48 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCollapsePanelProps } from './type'; +export default { + /** 折叠面板内容 */ + content: { + type: String, + }, + /** 禁止当前面板展开,优先级大于 Collapse 的同名属性 */ + disabled: { + type: Boolean, + default: undefined, + }, + /** 当前折叠面板展开图标,优先级大于 Collapse 的同名属性 */ + expandIcon: { + type: Boolean, + default: undefined as TdCollapsePanelProps['expandIcon'], + }, + /** 面板头内容 */ + header: { + type: String, + }, + /** 面板头左侧图标 */ + headerLeftIcon: { + type: String, + }, + /** 面板头的右侧区域,一般用于呈现面板操作 */ + headerRightContent: { + type: String, + }, + /** 选项卡内容的位置 */ + placement: { + type: String, + default: 'bottom' as TdCollapsePanelProps['placement'], + validator(val: TdCollapsePanelProps['placement']): boolean { + if (!val) return true; + return ['bottom', 'top'].includes(val); + }, + }, + /** 当前面板唯一标识,如果值为空则取当前面下标兜底作为唯一标识 */ + value: { + type: [String, Number], + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/collapse-panel/type.ts b/uni_modules/tdesign-uniapp/components/collapse-panel/type.ts new file mode 100644 index 0000000..636ea81 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse-panel/type.ts @@ -0,0 +1,41 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdCollapsePanelProps { + /** + * 折叠面板内容 + */ + content?: string; + /** + * 禁止当前面板展开,优先级大于 Collapse 的同名属性 + */ + disabled?: boolean; + /** + * 当前折叠面板展开图标,优先级大于 Collapse 的同名属性 + */ + expandIcon?: boolean; + /** + * 面板头内容 + */ + header?: string; + /** + * 面板头左侧图标 + */ + headerLeftIcon?: string; + /** + * 面板头的右侧区域,一般用于呈现面板操作 + */ + headerRightContent?: string; + /** + * 选项卡内容的位置 + * @default bottom + */ + placement?: 'bottom' | 'top'; + /** + * 当前面板唯一标识,如果值为空则取当前面下标兜底作为唯一标识 + */ + value?: string | number; +} diff --git a/uni_modules/tdesign-uniapp/components/collapse/README.en-US.md b/uni_modules/tdesign-uniapp/components/collapse/README.en-US.md new file mode 100644 index 0000000..a43fefc --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse/README.en-US.md @@ -0,0 +1,82 @@ +:: BASE_DOC :: + +## API + +### Collapse Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +default-expand-all | Boolean | false | \- | N +disabled | Boolean | - | \- | N +expand-icon | Boolean | true | \- | N +expand-mutex | Boolean | false | \- | N +theme | String | default | options: default/card | N +value | Array | - | `v-model:value` is supported。Typescript:`CollapseValue` `type CollapseValue = Array`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/collapse/type.ts) | N +default-value | Array | - | uncontrolled property。Typescript:`CollapseValue` `type CollapseValue = Array`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/collapse/type.ts) | N + +### Collapse Events + +name | params | description +-- | -- | -- +change | `(context: { value: CollapseValue, context: { e: MouseEvent }})` | \- + +### Collapse Slots + +name | Description +-- | -- +\- | \- + + +### CollapsePanel Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +content | String | - | \- | N +disabled | Boolean | undefined | \- | N +expand-icon | Boolean | undefined | \- | N +header | String | - | \- | N +header-left-icon | String | - | \- | N +header-right-content | String | - | \- | N +placement | String | bottom | options: bottom/top | N +value | String / Number | - | \- | N + +### CollapsePanel Slots + +name | Description +-- | -- +\- | \- +content | \- +expand-icon | \- +header | \- +header-left-icon | \- +header-right-content | \- + +### CollapsePanel External Classes + +className | Description +-- | -- +t-class | \- +t-class-content | \- +t-class-header | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-collapse-border-color | @border-level-1-color | - +--td-collapse-content-font | @font-body-medium | - +--td-collapse-content-padding | 32rpx | - +--td-collapse-content-text-color | @text-color-primary | - +--td-collapse-disabled-color | @text-color-disabled | - +--td-collapse-extra-font | @font-body-large | - +--td-collapse-header-height | auto | - +--td-collapse-header-text-color | @text-color-primary | - +--td-collapse-header-text-disabled-color | @collapse-disabled-color | - +--td-collapse-horizontal-padding | 32rpx | - +--td-collapse-icon-color | @text-color-placeholder | - +--td-collapse-left-icon-color | @brand-color | - +--td-collapse-panel-bg-color | @bg-color-container | - +--td-collapse-title-font | @font-body-large | - diff --git a/uni_modules/tdesign-uniapp/components/collapse/README.md b/uni_modules/tdesign-uniapp/components/collapse/README.md new file mode 100644 index 0000000..0643e01 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse/README.md @@ -0,0 +1,119 @@ +--- +title: Collapse 折叠面板 +description: 用于对复杂区域进行分组和隐藏 常用于订单信息展示等 +spline: data +isComponent: true +--- + + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TCollapse from '@tdesign/uniapp/collapse/collapse.vue'; +import TCollapsePanel from '@tdesign/uniapp/collapse-panel/collapse-panel.vue'; +``` + +### 类型 + +基础折叠面板 + +{{ base }} + + +带操作说明 + +{{ action }} + +手风琴模式 + +{{ accordion }} + +### 样式 + +卡片折叠面板 + +{{ theme }} + +## API + +### Collapse Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +default-expand-all | Boolean | false | 默认是否展开全部 | N +disabled | Boolean | - | 是否禁用面板展开/收起操作 | N +expand-icon | Boolean | true | 展开图标 | N +expand-mutex | Boolean | false | 每个面板互斥展开,每次只展开一个面板 | N +theme | String | default | 折叠面板风格。可选项:default/card | N +value | Array | - | 展开的面板集合。支持语法糖 `v-model:value`。TS 类型:`CollapseValue` `type CollapseValue = Array`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/collapse/type.ts) | N +default-value | Array | - | 展开的面板集合。非受控属性。TS 类型:`CollapseValue` `type CollapseValue = Array`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/collapse/type.ts) | N + +### Collapse Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { value: CollapseValue, context: { e: MouseEvent }})` | 切换面板时触发,返回变化的值 + +### Collapse Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 + + +### CollapsePanel Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +content | String | - | 折叠面板内容 | N +disabled | Boolean | undefined | 禁止当前面板展开,优先级大于 Collapse 的同名属性 | N +expand-icon | Boolean | undefined | 当前折叠面板展开图标,优先级大于 Collapse 的同名属性 | N +header | String | - | 面板头内容 | N +header-left-icon | String | - | 面板头左侧图标 | N +header-right-content | String | - | 面板头的右侧区域,一般用于呈现面板操作 | N +placement | String | bottom | 选项卡内容的位置。可选项:bottom/top | N +value | String / Number | - | 当前面板唯一标识,如果值为空则取当前面下标兜底作为唯一标识 | N + +### CollapsePanel Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,作用同 `content` 插槽 +content | 自定义 `content` 显示内容 +expand-icon | 自定义 `expand-icon` 显示内容 +header | 自定义 `header` 显示内容 +header-left-icon | 自定义 `header-left-icon` 显示内容 +header-right-content | 自定义 `header-right-content` 显示内容 + +### CollapsePanel External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-content | 内容样式类 +t-class-header | 头部样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-collapse-border-color | @border-level-1-color | - +--td-collapse-content-font | @font-body-medium | - +--td-collapse-content-padding | 32rpx | - +--td-collapse-content-text-color | @text-color-primary | - +--td-collapse-disabled-color | @text-color-disabled | - +--td-collapse-extra-font | @font-body-large | - +--td-collapse-header-height | auto | - +--td-collapse-header-text-color | @text-color-primary | - +--td-collapse-header-text-disabled-color | @collapse-disabled-color | - +--td-collapse-horizontal-padding | 32rpx | - +--td-collapse-icon-color | @text-color-placeholder | - +--td-collapse-left-icon-color | @brand-color | - +--td-collapse-panel-bg-color | @bg-color-container | - +--td-collapse-title-font | @font-body-large | - diff --git a/uni_modules/tdesign-uniapp/components/collapse/collapse.css b/uni_modules/tdesign-uniapp/components/collapse/collapse.css new file mode 100644 index 0000000..75a9bd0 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse/collapse.css @@ -0,0 +1,5 @@ +.t-collapse--card { + margin: 0 32rpx; + border-radius: var(--td-radius-large, 18rpx); + overflow: hidden; +} diff --git a/uni_modules/tdesign-uniapp/components/collapse/collapse.vue b/uni_modules/tdesign-uniapp/components/collapse/collapse.vue new file mode 100644 index 0000000..232f92a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse/collapse.vue @@ -0,0 +1,119 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/collapse/props.ts b/uni_modules/tdesign-uniapp/components/collapse/props.ts new file mode 100644 index 0000000..0182195 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse/props.ts @@ -0,0 +1,42 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCollapseProps } from './type'; +export default { + /** 默认是否展开全部 */ + defaultExpandAll: Boolean, + /** 是否禁用面板展开/收起操作 */ + disabled: Boolean, + /** 展开图标 */ + expandIcon: { + type: Boolean, + default: true, + }, + /** 每个面板互斥展开,每次只展开一个面板 */ + expandMutex: Boolean, + /** 折叠面板风格 */ + theme: { + type: String, + default: 'default' as TdCollapseProps['theme'], + validator(val: TdCollapseProps['theme']): boolean { + if (!val) return true; + return ['default', 'card'].includes(val); + }, + }, + /** 展开的面板集合 */ + value: { + type: Array, + }, + /** 展开的面板集合,非受控属性 */ + defaultValue: { + type: Array, + }, + /** 切换面板时触发,返回变化的值 */ + onChange: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/collapse/type.ts b/uni_modules/tdesign-uniapp/components/collapse/type.ts new file mode 100644 index 0000000..0600170 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/collapse/type.ts @@ -0,0 +1,46 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdCollapseProps { + /** + * 默认是否展开全部 + * @default false + */ + defaultExpandAll?: boolean; + /** + * 是否禁用面板展开/收起操作 + */ + disabled?: boolean; + /** + * 展开图标 + * @default true + */ + expandIcon?: boolean; + /** + * 每个面板互斥展开,每次只展开一个面板 + * @default false + */ + expandMutex?: boolean; + /** + * 折叠面板风格 + * @default default + */ + theme?: 'default' | 'card'; + /** + * 展开的面板集合 + */ + value?: CollapseValue; + /** + * 展开的面板集合,非受控属性 + */ + defaultValue?: CollapseValue; + /** + * 切换面板时触发,返回变化的值 + */ + onChange?: (context: { value: CollapseValue; context: { e: MouseEvent } }) => void; +} + +export type CollapseValue = Array; diff --git a/uni_modules/tdesign-uniapp/components/color-picker/README.en-US.md b/uni_modules/tdesign-uniapp/components/color-picker/README.en-US.md new file mode 100644 index 0000000..d147a8b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/README.en-US.md @@ -0,0 +1,62 @@ +:: BASE_DOC :: + +## API + +### ColorPicker Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +auto-close | Boolean | true | \- | N +enable-alpha | Boolean | false | \- | N +fixed | Boolean | false | \- | N +format | String | RGB | When `enableAlpha` is true, `HEX8/RGBA/HSLA/HSVA` are valid。options: HEX/HEX8/RGB/RGBA/HSL/HSLA/HSV/HSVA/CMYK/CSS | N +popup-props | Object | {} | Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts) | N +swatch-colors | Array | undefined | swatch colors。Typescript:`Array \| null \| undefined` | N +type | String | base | options: base/multiple。Typescript:`TypeEnum ` `type TypeEnum = 'base' \| 'multiple'`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts) | N +use-popup | Boolean | false | \- | N +value | String | - | color value。`v-model:value` is supported | N +default-value | String | - | color value。uncontrolled property | N +visible | Boolean | false | \- | N + +### ColorPicker Events + +name | params | description +-- | -- | -- +change | `(context: { value: string, context: { color: ColorObject; trigger: ColorPickerChangeTrigger }})` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts)。
`type ColorPickerChangeTrigger = 'palette-hue-bar' \| 'palette-alpha-bar' \| 'preset' `
+close | `(context: { trigger: ColorPickerTrigger })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts)。
`type ColorPickerTrigger = 'overlay'`
+palette-bar-change | `(context: { color: ColorObject })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts)。
`interface ColorObject { alpha: number; css: string; hex: string; hex8: string; hsl: string; hsla: string; hsv: string; hsva: string; rgb: string; rgba: string; value: number;}`
+ +### ColorPicker Slots + +name | Description +-- | -- +footer | \- +header | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-color-picker-gradient-preview-height | 56rpx | - +--td-color-picker-gradient-preview-radius | 6rpx | - +--td-color-picker-gradient-preview-width | 56rpx | - +--td-color-picker-input-format-margin-left | 48rpx | - +--td-color-picker-panel-background | @bg-color-container | - +--td-color-picker-panel-padding | 32rpx | - +--td-color-picker-panel-radius | 24rpx | - +--td-color-picker-panel-width | 750rpx | - +--td-color-picker-saturation-height | 288rpx | - +--td-color-picker-saturation-radius | 12rpx | - +--td-color-picker-saturation-thumb-size | 48rpx | - +--td-color-picker-slider-height | 16rpx | - +--td-color-picker-slider-thumb-padding | 6rpx | - +--td-color-picker-slider-thumb-size | 48rpx | - +--td-color-picker-slider-thumb-transform-x | -18rpx | - +--td-color-picker-slider-wrapper-padding | 0 18rpx | - +--td-color-picker-swatch-border-radius | @radius-small | - +--td-color-picker-swatch-height | 48rpx | - +--td-color-picker-swatch-padding | 0 | - +--td-color-picker-swatch-width | 48rpx | - +--td-color-picker-swatches-title-font | @font-title-medium | - diff --git a/uni_modules/tdesign-uniapp/components/color-picker/README.md b/uni_modules/tdesign-uniapp/components/color-picker/README.md new file mode 100644 index 0000000..25ed41c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/README.md @@ -0,0 +1,110 @@ +--- +title: ColorPicker 颜色选择器 +description: 用于颜色选择,支持多种格式。 +spline: data +isComponent: true +--- + + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TColorPicker from '@tdesign/uniapp/color-picker/color-picker.vue'; +``` + +## 代码演示 + +### 组件类型 + +#### 基础颜色选择器 + +{{ base }} + +#### 带色板的颜色选择器 + +{{ multiple }} + +### 组件状态 + +{{ format }} + +## FAQ + +如果使用场景为 `scroll-view`,除了需要显示指定 `fixed` 属性为 `true`,还需要手动调用组件的 debouncedUpdateEleRect() 事件。 + +```html + + + +``` + +```js +onScroll(e) { + if (!this.colorPicker) this.colorPicker = this.selectComponent('#ColorPicker'); + this.colorPicker.debouncedUpdateEleRect(e); +} +``` + +## API + +### ColorPicker Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +auto-close | Boolean | true | 自动关闭。在点击遮罩层时自动关闭,不需要手动设置 visible | N +enable-alpha | Boolean | false | 是否开启透明通道 | N +fixed | Boolean | false | 如果 color-picker 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true | N +format | String | RGB | 格式化色值。`enableAlpha` 为真时,`HEX8/RGBA/HSLA/HSVA` 有效。可选项:HEX/HEX8/RGB/RGBA/HSL/HSLA/HSV/HSVA/CMYK/CSS | N +popup-props | Object | {} | 透传 Popup 组件全部属性。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts) | N +swatch-colors | Array | undefined | 系统预设的颜色样例,值为 `null` 或 `[]` 则不显示系统色,值为 `undefined` 会显示组件内置的系统默认色。TS 类型:`Array \| null \| undefined` | N +type | String | base | 颜色选择器类型。(base 表示仅展示系统预设内容; multiple 表示展示色板和系统预设内容。可选项:base/multiple。TS 类型:`TypeEnum ` `type TypeEnum = 'base' \| 'multiple'`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts) | N +use-popup | Boolean | false | 是否使用弹出层包裹颜色选择器 | N +value | String | - | 色值。支持语法糖 `v-model:value` | N +default-value | String | - | 色值。非受控属性 | N +visible | Boolean | false | 是否显示颜色选择器。`usePopup` 为 true 时有效 | N + +### ColorPicker Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { value: string, context: { color: ColorObject; trigger: ColorPickerChangeTrigger }})` | 选中的色值发生变化时触发,第一个参数 `value` 表示新色值,`context.color` 表示当前调色板控制器的色值,`context.trigger` 表示触发颜色变化的来源。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts)。
`type ColorPickerChangeTrigger = 'palette-hue-bar' \| 'palette-alpha-bar' \| 'preset' `
+close | `(context: { trigger: ColorPickerTrigger })` | 关闭按钮时触发。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts)。
`type ColorPickerTrigger = 'overlay'`
+palette-bar-change | `(context: { color: ColorObject })` | 调色板控制器的值变化时触发,`context.color` 指调色板控制器的值。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/color-picker/type.ts)。
`interface ColorObject { alpha: number; css: string; hex: string; hex8: string; hsl: string; hsla: string; hsv: string; hsva: string; rgb: string; rgba: string; value: number;}`
+ +### ColorPicker Slots + +名称 | 描述 +-- | -- +footer | 底部插槽,仅在 `usePopup` 为 `true` 时有效 +header | 顶部插槽,仅在 `usePopup` 为 `true` 时有效 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-color-picker-gradient-preview-height | 56rpx | - +--td-color-picker-gradient-preview-radius | 6rpx | - +--td-color-picker-gradient-preview-width | 56rpx | - +--td-color-picker-input-format-margin-left | 48rpx | - +--td-color-picker-panel-background | @bg-color-container | - +--td-color-picker-panel-padding | 32rpx | - +--td-color-picker-panel-radius | 24rpx | - +--td-color-picker-panel-width | 750rpx | - +--td-color-picker-saturation-height | 288rpx | - +--td-color-picker-saturation-radius | 12rpx | - +--td-color-picker-saturation-thumb-size | 48rpx | - +--td-color-picker-slider-height | 16rpx | - +--td-color-picker-slider-thumb-padding | 6rpx | - +--td-color-picker-slider-thumb-size | 48rpx | - +--td-color-picker-slider-thumb-transform-x | -18rpx | - +--td-color-picker-slider-wrapper-padding | 0 18rpx | - +--td-color-picker-swatch-border-radius | @radius-small | - +--td-color-picker-swatch-height | 48rpx | - +--td-color-picker-swatch-padding | 0 | - +--td-color-picker-swatch-width | 48rpx | - +--td-color-picker-swatches-title-font | @font-title-medium | - diff --git a/uni_modules/tdesign-uniapp/components/color-picker/color-picker.css b/uni_modules/tdesign-uniapp/components/color-picker/color-picker.css new file mode 100644 index 0000000..c209d5e --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/color-picker.css @@ -0,0 +1,271 @@ +.t-color-picker__panel { + padding: 0; + width: var(--td-color-picker-panel-width, 750rpx); + background: var(--td-color-picker-panel-background, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + border-top-left-radius: var(--td-color-picker-panel-radius, 24rpx); + border-top-right-radius: var(--td-color-picker-panel-radius, 24rpx); + user-select: none; +} +.t-color-picker__body { + padding: var(--td-color-picker-panel-padding, 32rpx); + padding-bottom: 56rpx; +} +.t-color-picker__thumb { + position: absolute; + z-index: 1; + outline: none; + width: var(--td-color-picker-slider-thumb-size, 48rpx); + height: var(--td-color-picker-slider-thumb-size, 48rpx); + border-radius: var(--td-radius-circle, 50%); + box-shadow: var(--td-shadow-1, 0 1px 10px rgba(0, 0, 0, 0.05), 0 4px 5px rgba(0, 0, 0, 0.08), 0 2px 4px -1px rgba(0, 0, 0, 0.12)); + color: var(--td-text-color-brand, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + box-sizing: border-box; +} +.t-color-picker__thumb::before, +.t-color-picker__thumb::after { + content: ''; + position: absolute; + border-radius: var(--td-radius-circle, 50%); + box-sizing: border-box; + display: block; + border: 1px solid var(--td-component-border, var(--td-gray-color-4, #dcdcdc)); +} +.t-color-picker__thumb::before { + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: #fff; +} +.t-color-picker__thumb::after { + left: 3px; + top: 3px; + width: calc(100% - 6px); + height: calc(100% - 6px); + padding: var(--td-color-picker-slider-thumb-padding, 6rpx); + background: currentcolor; +} +.t-color-picker__saturation { + height: var(--td-color-picker-saturation-height, 288rpx); + border-radius: var(--td-color-picker-saturation-radius, 12rpx); + position: relative; + overflow: hidden; + background: transparent; +} +.t-color-picker__saturation::before, +.t-color-picker__saturation::after { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.t-color-picker__saturation::before { + /* stylelint-disable-next-line color-no-hex */ + background: linear-gradient(90deg, #fff, transparent); +} +.t-color-picker__saturation::after { + /* stylelint-disable-next-line color-no-hex */ + background: linear-gradient(0deg, #000, transparent); +} +.t-color-picker__saturation .t-color-picker__thumb { + width: var(--td-color-picker-saturation-thumb-size, 48rpx); + height: var(--td-color-picker-saturation-thumb-size, 48rpx); + border-radius: var(--td-radius-circle, 50%); + transform: translate(-50%, -50%); +} +.t-color-picker__slider-wrapper { + border-radius: calc(var(--td-color-picker-slider-height, 16rpx) / 2); + padding: var(--td-color-picker-slider-wrapper-padding, 0 18rpx); + position: relative; +} +.t-color-picker__slider-wrapper--hue-type { + /* stylelint-disable-next-line color-named */ + background: linear-gradient(90deg, red, #ff0 17%, #0f0 33%, #0ff 50%, #00f 67%, #f0f 83%, red); + margin: 16rpx 0; +} +.t-color-picker__slider-wrapper--alpha-type { + background: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + margin: 40rpx 0 16rpx 0; + /* stylelint-disable-next-line color-no-hex */ + background-image: linear-gradient(45deg, #c5c5c5 25%, transparent 0, transparent 75%, #c5c5c5 0, #c5c5c5), linear-gradient(45deg, #c5c5c5 25%, transparent 0, transparent 75%, #c5c5c5 0, #c5c5c5); + background-size: 6px 6px; + background-position: 0 0, 3px 3px; +} +.t-color-picker__slider-wrapper--alpha-type .t-color-picker__rail { + background: linear-gradient(to right, transparent, currentcolor); +} +.t-color-picker__slider-padding { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: var(--td-color-picker-slider-height, 16rpx); + border-radius: calc(var(--td-color-picker-slider-height, 16rpx) / 2); +} +.t-color-picker__slider { + height: var(--td-color-picker-slider-height, 16rpx); + position: relative; + border-radius: calc(var(--td-color-picker-slider-height, 16rpx) / 2); + color: transparent; + outline: none; + z-index: 1; +} +.t-color-picker__slider .t-color-picker__thumb { + transform: translate(var(--td-color-picker-slider-thumb-transform-x, -18rpx), -50%); + top: 50%; +} +.t-color-picker__slider .t-color-picker__rail { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + overflow: hidden; + border-radius: inherit; +} +.t-color-picker__sliders-wrapper { + display: flex; + align-items: center; + margin: 32rpx 0 40rpx; +} +.t-color-picker__sliders { + width: 100%; +} +.t-color-picker__sliders-preview { + flex-shrink: 0; + margin-left: var(--td-spacer-1, 24rpx); + width: var(--td-color-picker-gradient-preview-width, 56rpx); + height: var(--td-color-picker-gradient-preview-height, 56rpx); + border-radius: var(--td-color-picker-gradient-preview-radius, 6rpx); + overflow: hidden; + background: var(--td-text-color-anti, var(--td-font-white-1, #ffffff)); + /* stylelint-disable-next-line color-no-hex */ + background-image: linear-gradient(45deg, #c5c5c5 25%, transparent 0, transparent 75%, #c5c5c5 0, #c5c5c5), linear-gradient(45deg, #c5c5c5 25%, transparent 0, transparent 75%, #c5c5c5 0, #c5c5c5); + background-size: 6px 6px; + background-position: 0 0, 3px 3px; +} +.t-color-picker__sliders-preview-inner { + display: block; + width: 100%; + height: 100%; +} +.t-color-picker__format { + display: flex; + align-items: center; + justify-content: space-between; + color: var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4))); + text-align: center; +} +.t-color-picker__format:not(:empty) + .t-color-picker__swatches-wrap:not(:empty) { + margin-top: 56rpx; +} +.t-color-picker__format-item { + font: var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); +} +.t-color-picker__format-item--first { + flex-shrink: 0; + width: 136rpx; + border: 2rpx solid var(--td-component-border, var(--td-gray-color-4, #dcdcdc)); + border-radius: var(--td-radius-default, 12rpx); + padding: 6rpx 0; + margin-right: 24rpx; +} +.t-color-picker__format-item--second { + flex: 1; +} +.t-color-picker__format-inputs { + display: flex; + align-items: center; + justify-content: space-around; +} +.t-color-picker__format-input { + flex: 1; + width: 0; + margin-left: -2rpx; + padding: 6rpx 0; + border: 1px solid var(--td-component-border, var(--td-gray-color-4, #dcdcdc)); + border-radius: var(--td-radius-default, 12rpx); +} +.t-color-picker__format-input:not(:first-child):not(:last-child) { + border-radius: 0; +} +.t-color-picker__format-input:first-child:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.t-color-picker__format-input:last-child:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.t-color-picker__format-input--fixed { + flex-shrink: 0; + flex-grow: 0; + flex-basis: 133rpx; +} +.t-color-picker__swatches-wrap { + position: relative; +} +.t-color-picker__swatches + .t-color-picker__swatches { + margin-top: var(--td-spacer-1, 24rpx); +} +.t-color-picker__swatches-title { + font: var(--td-color-picker-swatches-title-font, var(--td-font-title-medium, 600 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + padding: 0; + color: var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))); + display: flex; + align-items: center; + justify-content: space-between; +} +.t-color-picker__swatches-items { + margin-top: 24rpx; + width: 100%; + list-style: none; + display: flex; + overflow-x: auto; + overflow-y: auto; +} +.t-color-picker__swatches-items::-webkit-scrollbar { + display: none; + width: 0; + height: 0; + color: transparent; +} +.t-color-picker__swatches-item { + width: var(--td-color-picker-swatch-width, 48rpx); + height: var(--td-color-picker-swatch-height, 48rpx); + border-radius: var(--td-radius-small, 6rpx); + padding: var(--td-color-picker-swatch-padding, 0); + overflow: hidden; + display: flex; + align-items: center; + justify-content: center; + position: relative; + transform-origin: center; + transition: all var(--td-anim-duration-base, 0.2s) var(--td-anim-time-fn-easing, cubic-bezier(0.38, 0, 0.24, 1)); + box-sizing: border-box; + flex-shrink: 0; + margin-right: 24rpx; + border-radius: var(--td-color-picker-swatch-border-radius, var(--td-radius-small, 6rpx)); +} +.t-color-picker__swatches-item::after { + content: ''; + width: 100%; + height: 100%; + position: absolute; + left: 0; + top: 0; + opacity: 0; + background: rgba(0, 0, 0, 0.2); +} +.t-color-picker__swatches-item:active::after { + opacity: 1; +} +.t-color-picker__swatches-inner { + width: 100%; + height: 100%; + display: block; + border-radius: var(--td-color-picker-swatch-border-radius, var(--td-radius-small, 6rpx)); + position: relative; +} diff --git a/uni_modules/tdesign-uniapp/components/color-picker/color-picker.vue b/uni_modules/tdesign-uniapp/components/color-picker/color-picker.vue new file mode 100644 index 0000000..a774c3b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/color-picker.vue @@ -0,0 +1,501 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/color-picker/constants.ts b/uni_modules/tdesign-uniapp/components/color-picker/constants.ts new file mode 100644 index 0000000..72f64af --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/constants.ts @@ -0,0 +1,26 @@ +/** 常量 */ + +// 默认颜色 +export const DEFAULT_COLOR = '#001F97'; + +// 默认系统色彩 +export const DEFAULT_SYSTEM_SWATCH_COLORS = [ + '#F2F3FF', + '#D9E1FF', + '#B5C7FF', + '#8EABFF', + '#618DFF', + '#366EF4', + '#0052D9', + '#003CAB', + '#002A7C', + '#001A57', +]; + +// saturation-panel default rect +export const SATURATION_PANEL_DEFAULT_WIDTH = 343; +export const SATURATION_PANEL_DEFAULT_HEIGHT = 144; +export const SLIDER_DEFAULT_WIDTH = 303; + +export const HUE_MAX = 360; +export const ALPHA_MAX = 100; diff --git a/uni_modules/tdesign-uniapp/components/color-picker/props.ts b/uni_modules/tdesign-uniapp/components/color-picker/props.ts new file mode 100644 index 0000000..c0c35dd --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/props.ts @@ -0,0 +1,75 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdColorPickerProps } from './type'; +export default { + /** 自动关闭。在点击遮罩层时自动关闭,不需要手动设置 visible */ + autoClose: { + type: Boolean, + default: true, + }, + /** 是否开启透明通道 */ + enableAlpha: Boolean, + /** 如果 color-picker 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true */ + fixed: Boolean, + /** 格式化色值。`enableAlpha` 为真时,`HEX8/RGBA/HSLA/HSVA` 有效 */ + format: { + type: String, + default: 'RGB' as TdColorPickerProps['format'], + validator(val: TdColorPickerProps['format']): boolean { + if (!val) return true; + return ['HEX', 'HEX8', 'RGB', 'RGBA', 'HSL', 'HSLA', 'HSV', 'HSVA', 'CMYK', 'CSS'].includes(val); + }, + }, + /** 透传 Popup 组件全部属性 */ + popupProps: { + type: Object, + default: () => ({}), + }, + /** 系统预设的颜色样例,值为 `null` 或 `[]` 则不显示系统色,值为 `undefined` 会显示组件内置的系统默认色 */ + swatchColors: { + type: Array, + default: undefined as TdColorPickerProps['swatchColors'], + }, + /** 颜色选择器类型。(base 表示仅展示系统预设内容; multiple 表示展示色板和系统预设内容 */ + type: { + type: String, + default: 'base' as TdColorPickerProps['type'], + validator(val: TdColorPickerProps['type']): boolean { + if (!val) return true; + return ['base', 'multiple'].includes(val); + }, + }, + /** 是否使用弹出层包裹颜色选择器 */ + usePopup: Boolean, + /** 色值 */ + value: { + type: String, + default: '', + }, + /** 色值,非受控属性 */ + defaultValue: { + type: String, + default: '', + }, + /** 是否显示颜色选择器。`usePopup` 为 true 时有效 */ + visible: Boolean, + /** 选中的色值发生变化时触发,第一个参数 `value` 表示新色值,`context.color` 表示当前调色板控制器的色值,`context.trigger` 表示触发颜色变化的来源 */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 关闭按钮时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 调色板控制器的值变化时触发,`context.color` 指调色板控制器的值 */ + onPaletteBarChange: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/color-picker/template.props.js b/uni_modules/tdesign-uniapp/components/color-picker/template.props.js new file mode 100644 index 0000000..971fa24 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/template.props.js @@ -0,0 +1,58 @@ +export default { + classPrefix: { + type: String, + default: '', + }, + customStyle: { + type: [String, Object], + default: '', + }, + isMultiple: { + type: Boolean, + default: false, + }, + type: { + type: String, + default: '', + }, + sliderInfo: { + type: Object, + default: () => ({}), + }, + saturationThumbStyle: { + type: [String, Object], + default: '', + }, + hueSliderStyle: { + type: [String, Object], + default: '', + }, + enableAlpha: { + type: Boolean, + default: false, + }, + alphaSliderStyle: { + type: [String, Object], + default: '', + }, + showPrimaryColorPreview: { + type: Boolean, + default: false, + }, + previewColor: { + type: String, + default: '', + }, + formatList: { + type: Array, + default: () => ([]), + }, + innerSwatchList: { + type: Array, + default: () => ([]), + }, + format: { + type: String, + default: 'RGB', + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/color-picker/template.vue b/uni_modules/tdesign-uniapp/components/color-picker/template.vue new file mode 100644 index 0000000..0cb51f1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/template.vue @@ -0,0 +1,198 @@ + + + + + diff --git a/uni_modules/tdesign-uniapp/components/color-picker/type.ts b/uni_modules/tdesign-uniapp/components/color-picker/type.ts new file mode 100644 index 0000000..2583507 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/type.ts @@ -0,0 +1,96 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdPopupProps as PopupProps } from '../popup/type'; + +export interface TdColorPickerProps { + /** + * 自动关闭。在点击遮罩层时自动关闭,不需要手动设置 visible + * @default true + */ + autoClose?: boolean; + /** + * 是否开启透明通道 + * @default false + */ + enableAlpha?: boolean; + /** + * 如果 color-picker 是在一个 `position:fixed` 的区域,需要显式指定属性 fixed 为 true + * @default false + */ + fixed?: boolean; + /** + * 格式化色值。`enableAlpha` 为真时,`HEX8/RGBA/HSLA/HSVA` 有效 + * @default RGB + */ + format?: 'HEX' | 'HEX8' | 'RGB' | 'RGBA' | 'HSL' | 'HSLA' | 'HSV' | 'HSVA' | 'CMYK' | 'CSS'; + /** + * 透传 Popup 组件全部属性 + * @default {} + */ + popupProps?: PopupProps; + /** + * 系统预设的颜色样例,值为 `null` 或 `[]` 则不显示系统色,值为 `undefined` 会显示组件内置的系统默认色 + */ + swatchColors?: Array | null | undefined; + /** + * 颜色选择器类型。(base 表示仅展示系统预设内容; multiple 表示展示色板和系统预设内容 + * @default base + */ + type?: TypeEnum; + /** + * 是否使用弹出层包裹颜色选择器 + * @default false + */ + usePopup?: boolean; + /** + * 色值 + * @default '' + */ + value?: string; + /** + * 色值,非受控属性 + * @default '' + */ + defaultValue?: string; + /** + * 是否显示颜色选择器。`usePopup` 为 true 时有效 + * @default false + */ + visible?: boolean; + /** + * 选中的色值发生变化时触发,第一个参数 `value` 表示新色值,`context.color` 表示当前调色板控制器的色值,`context.trigger` 表示触发颜色变化的来源 + */ + onChange?: (context: { value: string; context: { color: ColorObject; trigger: ColorPickerChangeTrigger } }) => void; + /** + * 关闭按钮时触发 + */ + onClose?: (context: { trigger: ColorPickerTrigger }) => void; + /** + * 调色板控制器的值变化时触发,`context.color` 指调色板控制器的值 + */ + onPaletteBarChange?: (context: { color: ColorObject }) => void; +} + +export type TypeEnum = 'base' | 'multiple'; + +export type ColorPickerChangeTrigger = 'palette-hue-bar' | 'palette-alpha-bar' | 'preset'; + +export type ColorPickerTrigger = 'overlay'; + +export interface ColorObject { + alpha: number; + css: string; + hex: string; + hex8: string; + hsl: string; + hsla: string; + hsv: string; + hsva: string; + rgb: string; + rgba: string; + value: number; +} diff --git a/uni_modules/tdesign-uniapp/components/color-picker/utils.js b/uni_modules/tdesign-uniapp/components/color-picker/utils.js new file mode 100644 index 0000000..c985ed7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/color-picker/utils.js @@ -0,0 +1 @@ +export * from '../common/shared/color-picker/index'; diff --git a/uni_modules/tdesign-uniapp/components/common/bus.js b/uni_modules/tdesign-uniapp/components/common/bus.js new file mode 100644 index 0000000..8535007 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/bus.js @@ -0,0 +1,84 @@ +export default class Bus { + constructor() { + this.listeners = new Map(); + this.emitted = new Map(); // 改为Map存储事件参数 + this.onceListeners = new Map(); + } + + on(evtName, listener) { + const target = this.listeners.get(evtName) || []; + target.push(listener); + this.listeners.set(evtName, target); + + // 如果事件已触发过,用保存的参数立即执行 + if (this.emitted.has(evtName)) { + listener(...this.emitted.get(evtName)); + } + } + + once(evtName, listener) { + // 如果事件已经触发过,立即执行并返回 + if (this.emitted.has(evtName)) { + listener(...this.emitted.get(evtName)); + return; + } + + // 封装自移除函数 + const onceWrapper = (...args) => { + listener(...args); + this.off(evtName, onceWrapper); + }; + + // 存储原始监听器用于精准移除 + const onceMap = this.onceListeners.get(evtName) || new Map(); + onceMap.set(listener, onceWrapper); + this.onceListeners.set(evtName, onceMap); + + // 注册封装函数 + this.on(evtName, onceWrapper); + } + + emit(evtName, ...args) { + // 保存事件参数 + this.emitted.set(evtName, args); + + const listeners = this.listeners.get(evtName) || []; + // 使用副本避免执行时修改数组 + [...listeners].forEach(func => func(...args)); + } + + off(evtName, listener) { + if (listener) { + // 处理一次性监听器的原始引用 + const onceMap = this.onceListeners.get(evtName); + if (onceMap && onceMap.has(listener)) { + const wrapper = onceMap.get(listener); + this._removeListener(evtName, wrapper); + onceMap.delete(listener); + return; + } + + // 处理普通监听器 + this._removeListener(evtName, listener); + } else { + // 移除所有监听器 + this.listeners.delete(evtName); + this.onceListeners.delete(evtName); + this.emitted.delete(evtName); + } + } + + // 私有方法:从指定事件中移除单个监听器 + _removeListener(evtName, listener) { + const listeners = this.listeners.get(evtName); + if (!listeners) return; + + const index = listeners.indexOf(listener); + if (index > -1) { + listeners.splice(index, 1); + if (listeners.length === 0) { + this.listeners.delete(evtName); + } + } + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/canvas/index.js b/uni_modules/tdesign-uniapp/components/common/canvas/index.js new file mode 100644 index 0000000..1299b62 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/canvas/index.js @@ -0,0 +1,53 @@ +export async function loadImage({ + canvas, + src, +}) { + let result = null; + if (!src || !canvas) { + return result; + } + + // #ifdef MP + // 小程序环境:使用 canvas.createImage + if (canvas.createImage) { + result = new Promise((resolve, reject) => { + const img = canvas.createImage(); + // 必须先设置 onload 和 onerror,再设置 src + img.onload = () => resolve(img); + img.onerror = (err) => { + console.error('创建图片对象失败:', err); + reject(err); + }; + img.src = src; + }); + } + // #endif + + // #ifdef APP-PLUS + result = new Promise((resolve) => { + uni.getImageInfo({ + src, + success: (res) => { + const imgPath = res.path; // 本地临时路径 + resolve(imgPath); + }, + }); + }); + // #endif + + // #ifdef H5 + // H5 环境:创建 Image 对象(参考 TSX 实现) + result = new Promise((resolve, reject) => { + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.onload = () => resolve(img); + img.onerror = (err) => { + console.error('图标加载失败:', err); + reject(err); + }; + img.src = src; + }); + // #endif + + return result; +} diff --git a/uni_modules/tdesign-uniapp/components/common/common.ts b/uni_modules/tdesign-uniapp/components/common/common.ts new file mode 100644 index 0000000..11387f0 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/common.ts @@ -0,0 +1,173 @@ +export type Classes = Array; + +export interface Styles { + [css: string]: string | number; +} + +export type ImageEvent = any; + +export type PlainObject = { [key: string]: any }; + +export type OptionData = { + label?: string; + value?: string | number; +} & PlainObject; + +export type TreeOptionData = { + children?: Array> | boolean; + /** option label content */ + label?: string; + /** option search text */ + text?: string; + /** option value */ + value?: T; + /** option node content */ + content?: string; +} & PlainObject; + +export type FormResetEvent = Event; + +export type FormSubmitEvent = Event; + +/** + * 移除 on 前缀并可选地去掉可选修饰符 + * @param T - 原始类型 + * @param MakeRequired - 是否将属性变为必需(默认 false) + */ +export type TransformEventHandlers< + T, + MakeRequired extends boolean = false +> = MakeRequired extends true + ? { + [K in keyof T as K extends `on${infer Event}` + ? Uncapitalize + : never + ]-?: T[K] + } + : { + [K in keyof T as K extends `on${infer Event}` + ? Uncapitalize + : never + ]: T[K] + }; + + +type WrapWithContext any> = + T extends (...args: infer P) => infer R + ? (context: { [K in keyof P]: P[K] }) => R + : never; + + +export type WrapParamsWithContext = { + [K in keyof T]: T[K] extends (...args: any[]) => any + ? WrapWithContext + : T[K]; +}; + + +// 提取非 on 开头的属性 +export type ExtractNonOnProps = { + [K in keyof T as K extends `on${string}` ? never : K]: T[K] +}; + +export interface IsEmailOptions { + allow_display_name?: boolean; + require_display_name?: boolean; + allow_utf8_local_part?: boolean; + require_tld?: boolean; + allow_ip_dot?: boolean; + domain_specific_validation?: boolean; + host_blacklist?: string[]; + ignore_max_length?: boolean; +} + +export interface IsURLOptions { + protocols?: string[]; + require_tld?: boolean; + require_protocol?: boolean; + require_host?: boolean; + require_port?: boolean; + require_valid_protocol?: boolean; + allow_underscores?: boolean; + host_whitelist?: (string | RegExp)[]; + host_blacklist?: (string | RegExp)[]; + allow_trailing_dot?: boolean; + allow_protocol_relative_urls?: boolean; + disallow_auth?: boolean; + validate_length?: boolean; +} +/** + * 通用全局类型 + * */ +export type SizeEnum = 'small' | 'medium' | 'large'; + +export type ShapeEnum = 'circle' | 'round'; + +export type HorizontalAlignEnum = 'left' | 'center' | 'right'; + +export type VerticalAlignEnum = 'top' | 'middle' | 'bottom'; + +export type LayoutEnum = 'vertical' | 'horizontal'; + +export type ClassName = { [className: string]: any } | ClassName[] | string; + +export type CSSSelector = string; + +export interface KeysType { + value?: string; + label?: string; + disabled?: string; +} + +export interface TreeKeysType extends KeysType { + children?: string; +} + +export interface HTMLElementAttributes { + [attribute: string]: string; +} + +export interface TScroll { + /** + * 表示除可视区域外,额外渲染的行数,避免快速滚动过程中,新出现的内容来不及渲染从而出现空白 + * @default 20 + */ + bufferSize?: number; + /** + * 表示每行内容是否同一个固定高度,仅在 `scroll.type` 为 `virtual` 时有效,该属性设置为 `true` 时,可用于简化虚拟滚动内部计算逻辑,提升性能,此时则需要明确指定 `scroll.rowHeight` 属性的值 + * @default false + */ + isFixedRowHeight?: boolean; + /** + * 行高,不会给元素添加样式高度,仅作为滚动时的行高参考。一般情况不需要设置该属性。如果设置,可尽量将该属性设置为每行平均高度,从而使得滚动过程更加平滑 + */ + rowHeight?: number; + /** + * 启动虚拟滚动的阈值。为保证组件收益最大化,当数据量小于阈值 `scroll.threshold` 时,无论虚拟滚动的配置是否存在,组件内部都不会开启虚拟滚动 + * @default 100 + */ + threshold?: number; + /** + * 滚动加载类型,有两种:懒加载和虚拟滚动。
值为 `lazy` ,表示滚动时会进行懒加载,非可视区域内的内容将不会默认渲染,直到该内容可见时,才会进行渲染,并且已渲染的内容滚动到不可见时,不会被销毁;
值为`virtual`时,表示会进行虚拟滚动,无论滚动条滚动到哪个位置,同一时刻,仅渲染该可视区域内的内容,当需要展示的数据量较大时,建议开启该特性 + */ + type: 'lazy' | 'virtual'; +} + +/** + * @deprecated use TScroll instead + */ +export type InfinityScroll = TScroll; + +export interface ScrollToElementParams { + /** 跳转元素下标 */ + index?: number; + /** 跳转元素距离顶部的距离 */ + top?: number; + /** 单个元素高度非固定场景下,即 isFixedRowHeight = false。延迟设置元素位置,一般用于依赖不同高度异步渲染等场景,单位:毫秒 */ + time?: number; + behavior?: 'auto' | 'smooth'; +} + +export interface ComponentScrollToElementParams extends ScrollToElementParams { + key?: string | number; +} diff --git a/uni_modules/tdesign-uniapp/components/common/config.js b/uni_modules/tdesign-uniapp/components/common/config.js new file mode 100644 index 0000000..79606c4 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/config.js @@ -0,0 +1,9 @@ +export default { + data() { + return {}; + }, + prefix: 't', +}; +export const prefix = 't'; + +export const ISOLATED_RELATION_KEY = '-1'; diff --git a/uni_modules/tdesign-uniapp/components/common/dom/index.js b/uni_modules/tdesign-uniapp/components/common/dom/index.js new file mode 100644 index 0000000..d0f0586 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/dom/index.js @@ -0,0 +1 @@ +export { selectComponent } from './select-component'; diff --git a/uni_modules/tdesign-uniapp/components/common/dom/select-component.js b/uni_modules/tdesign-uniapp/components/common/dom/select-component.js new file mode 100644 index 0000000..f71ced7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/dom/select-component.js @@ -0,0 +1,26 @@ +export function selectComponent(context, selector) { + if (!selector || !context) return; + + if (typeof selector === 'function') { + return selector(context); + } + + let attribute = selector; + if (attribute.match(/^[^\w]/)) { + attribute = attribute.slice(1); + } + + if ( + context.$refs && context.$refs[attribute]) { + return context.$refs[attribute]; + } + + if (context && typeof context.$selectComponent === 'function') { + const res = context.$selectComponent(selector); + return res; + } + + return context && context.selectComponent && context.selectComponent(selector); +} + + diff --git a/uni_modules/tdesign-uniapp/components/common/event/dynamic.js b/uni_modules/tdesign-uniapp/components/common/event/dynamic.js new file mode 100644 index 0000000..3d8a4b8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/event/dynamic.js @@ -0,0 +1,10 @@ +/** + * 解析事件里的动态函数名,这种没有()的函数名,在uniapp不被执行 + * 比如:立即 + * @param {*} exp + */ +export function parseEventDynamicCode(e, exp, ...args) { + if (typeof(this[exp]) === 'function') { + this[exp](e, ...args); + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/functional/mixin.js b/uni_modules/tdesign-uniapp/components/common/functional/mixin.js new file mode 100644 index 0000000..bbda343 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/functional/mixin.js @@ -0,0 +1,51 @@ +function formatPropKey(key) { + return key.replace(/^(\w)/, (a, b) => `data${b.toUpperCase()}`); +} + + +function getPropsWatch(props) { + const watchProps = Object.keys(props).reduce((acc, item) => { + acc[item] = { + handler(val) { + this[formatPropKey(item)] = val; + }, + }; + return acc; + }, {}); + return watchProps; +} + + +function getPropsData(context, props) { + const propsData = Object.keys(props).reduce((acc, item) => { + acc[formatPropKey(item)] = context[item]; + return acc; + }, {}); + return propsData; +} + + +function setPropsToData(data) { + Object.keys(data).forEach((key) => { + this[formatPropKey(key)] = data[key]; + }); +} + + +export function getFunctionalMixin(dialogProps) { + return { + data() { + return { + ...getPropsData(this, dialogProps), + }; + }, + watch: { + ...getPropsWatch(dialogProps), + }, + methods: { + setData(data) { + setPropsToData.call(this, data); + }, + }, + }; +} diff --git a/uni_modules/tdesign-uniapp/components/common/relation/index.js b/uni_modules/tdesign-uniapp/components/common/relation/index.js new file mode 100644 index 0000000..039e982 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/relation/index.js @@ -0,0 +1,6 @@ +export { RELATION_MAP } from './parent-map'; + +export { + ChildrenMixin, + ParentMixin, +} from './relation'; diff --git a/uni_modules/tdesign-uniapp/components/common/relation/parent-map.js b/uni_modules/tdesign-uniapp/components/common/relation/parent-map.js new file mode 100644 index 0000000..7e3fec5 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/relation/parent-map.js @@ -0,0 +1,26 @@ +export const RELATION_MAP = { + CollapsePanel: 'Collapse', + TabPanel: 'Tabs', + + StepItem: 'Steps', + TabBarItem: 'TabBar', + SideBarItem: 'SideBar', + GridItem: 'Grid', + DropdownItem: 'DropdownMenu', + + Radio: 'RadioGroup', + Checkbox: 'CheckboxGroup', + Cell: 'CellGroup', + Avatar: 'AvatarGroup', + PickerItem: 'Picker', + + IndexesAnchor: 'Indexes', + SwiperNav: 'Swiper', + + Col: 'Row', + BackTop: 'PullDownRefresh', + + FormItem: 'Form', + + FormKey: 'FormKey', +}; diff --git a/uni_modules/tdesign-uniapp/components/common/relation/relation.js b/uni_modules/tdesign-uniapp/components/common/relation/relation.js new file mode 100644 index 0000000..9a45a9d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/relation/relation.js @@ -0,0 +1,213 @@ +// #ifdef H5 +// import { sortChildren } from '../../common/dom/vnodes-h5'; +// #endif +// #ifndef H5 +// import { sortMPChildren } from '../../common/dom/vnodes-mp'; +// #endif + +function findNearListParent(children = [], name) { + let temp; + for (const item of children) { + // console.log('__nodeId__', item, item.$options.name, parentRelationKey, thisRelationKey); + const parentRelationKey = item.$props && item.$props.relationKey; + const thisRelationKey = this.$props && this.$props.relationKey; + if (item.$options.name === name && parentRelationKey === thisRelationKey) { + temp = item; + } + if (item === this && temp) { + // console.log('item === this', item); + return temp; + } + const foundInChildren = findNearListParent.call(this, item.$children, name); + if (foundInChildren) { + return foundInChildren; + } + } + + return temp; +} + + +function getParentInToutiao(name) { + let parent = this.$parent; + if (!parent) { + const pages = getCurrentPages(); + parent = pages[pages.length - 1]?.$vm; + } + + if (parent && !parent.$parent) { + const children = parent.$children; + if (!children || !children.length) return; + const result = findNearListParent.call(this, children, name); + // const result2 = children.find(item => item.$options.name === name); + // console.log('result2', result2, result2.__nodeId__); + // console.log('result', result, result.__nodeId__); + return result; + } +} + + +function getParent(name = '') { + const found = getParentInToutiao.call(this, name); + if (found) { + return found; + } + + let parent = this.$parent; + + if (!parent) { + return; + } + + let parentName = parent.$options.name; + while (parentName !== name) { + parent = parent.$parent; + if (!parent) return false; + parentName = parent.$options.name; + } + return parent; +} + + +export function ChildrenMixin(parent, options = {}) { + const indexKey = options.indexKey || 'index'; + + return { + inject: { + // #ifndef MP-TOUTIAO + [parent]: { + default: null, + }, + // #endif + }, + data() { + return { + // #ifdef MP-TOUTIAO + [parent]: null, + // #endif + }; + }, + computed: { + // 会造成循环引用 + // parent() { + // if (this.disableBindRelation) { + // return null; + // } + + // return this[parent]; + // }, + + [indexKey]() { + const that = this; + + that.bindRelation(); + + if (that[parent]) { + return that[parent].children.indexOf(this); + } + + return null; + }, + }, + + watch: { + disableBindRelation(val) { + const that = this; + if (!val) { + that.bindRelation(); + } + }, + }, + + created() { + // 循环引用调试代码 + // this[parent].children.push(this); + // #ifndef H5 + const that = this; + that.bindRelation(); + // #endif + }, + + + mounted() { + // #ifdef H5 + const that = this; + that.bindRelation(); + // #endif + }, + + // #ifdef VUE2 + beforeDestroy() { + const that = this; + that.onBeforeMount(); + }, + // #endif + + // #ifdef VUE3 + beforeUnmount() { + const that = this; + that.onBeforeMount(); + }, + // #endif + + methods: { + bindRelation() { + // #ifdef MP-TOUTIAO + const parentComponentName = `T${parent}`; + this[parent] = getParent.call(this, parentComponentName); + // #endif + if (!this[parent] || (this[parent].children && this[parent].children.indexOf(this) !== -1)) { + return; + } + + const children = [...(this[parent].children || []), this]; + + + // #ifdef H5 + try { + // sortChildren(children, this[parent]); + } catch (err) { + console.log('err', err); + } + // #endif + + // #ifndef H5 + // sortMPChildren(children); + // #endif + + this[parent].children = children; + this.innerAfterLinked?.(this); + this[parent].innerAfterLinked?.(this); + + // console.log('bindRelation.children', children); + }, + onBeforeMount() { + const that = this; + if (that[parent]) { + that[parent].children = that[parent].children.filter(item => item !== that); + this[parent].innerAfterUnLinked?.(this); + this.innerAfterUnLinked?.(this); + + that?.destroyCallback?.(); + } + }, + }, + }; +} + +export function ParentMixin(parent) { + return { + provide() { + return { + [parent]: this, + }; + }, + + data() { + return { + // 会造成循环引用 + // children: [], + }; + }, + }; +} diff --git a/uni_modules/tdesign-uniapp/components/common/route.js b/uni_modules/tdesign-uniapp/components/common/route.js new file mode 100644 index 0000000..52d084a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/route.js @@ -0,0 +1,13 @@ +export function goBackOrGoHome(home = '/pages/home/home') { + const pages = getCurrentPages(); + + if (pages.length > 1) { + uni.navigateBack(); + } else { + uni.navigateTo({ + url: home, + }); + } +} + +export default goBackOrGoHome; diff --git a/uni_modules/tdesign-uniapp/components/common/runtime/index.js b/uni_modules/tdesign-uniapp/components/common/runtime/index.js new file mode 100644 index 0000000..4b4f3d5 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/runtime/index.js @@ -0,0 +1,5 @@ +export function initTDesign() { + return { + }; +} + diff --git a/uni_modules/tdesign-uniapp/components/common/runtime/relation.js b/uni_modules/tdesign-uniapp/components/common/runtime/relation.js new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/tdesign-uniapp/components/common/runtime/wxs-polyfill.js b/uni_modules/tdesign-uniapp/components/common/runtime/wxs-polyfill.js new file mode 100644 index 0000000..5c972ee --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/runtime/wxs-polyfill.js @@ -0,0 +1,16 @@ +function getRegExp() { + const args = Array.prototype.slice.call(arguments); + args.unshift(RegExp); + return new (Function.prototype.bind.apply(RegExp, args))(); +} + +/** + * wxs getDate + */ +function getDate() { + const args = Array.prototype.slice.call(arguments); + args.unshift(Date); + return new (Function.prototype.bind.apply(Date, args))(); +} + +export { getDate, getRegExp }; diff --git a/uni_modules/tdesign-uniapp/components/common/shared/calendar/index.js b/uni_modules/tdesign-uniapp/components/common/shared/calendar/index.js new file mode 100644 index 0000000..186d6cb --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/calendar/index.js @@ -0,0 +1,128 @@ +import { getDateRect, isSameDate, getMonthDateRect, isValidDate, getDate } from '../date'; + +export default class TCalendar { + constructor(options = {}) { + this.type = 'single'; + Object.assign(this, options); + if (!this.minDate) this.minDate = getDate(); + if (!this.maxDate) this.maxDate = getDate(6); + } + + getTrimValue() { + const { value, type } = this; + const format = (val) => { + if (val instanceof Date) return val; + if (typeof val === 'number') return new Date(val); + return new Date(); + }; + if (type === 'single' && isValidDate(value)) return format(value); + if (type === 'multiple' || type === 'range') { + if (Array.isArray(value)) { + const isValid = value.every(item => isValidDate(item)); + return isValid ? value.map(item => format(item)) : []; + } + return []; + } + } + + getDays(weekdays) { + const ans = []; + let i = this.firstDayOfWeek % 7; + while (ans.length < 7) { + ans.push(weekdays[i]); + i = (i + 1) % 7; + } + return ans; + } + + getMonths() { + const ans = []; + const selectedDate = this.getTrimValue(); + const { minDate, maxDate, type, allowSameDay, format } = this; + const minDateRect = getDateRect(minDate); + let { year: minYear, month: minMonth } = minDateRect; + const { time: minTime } = minDateRect; + const { year: maxYear, month: maxMonth, time: maxTime } = getDateRect(maxDate); + const calcType = (year, month, date) => { + const curDate = new Date(year, month, date, 23, 59, 59); + + if (type === 'single' && selectedDate) { + if (isSameDate({ year, month, date }, selectedDate)) return 'selected'; + } + if (type === 'multiple' && selectedDate) { + const hit = selectedDate.some(item => isSameDate({ year, month, date }, item)); + if (hit) { + return 'selected'; + } + } + if (type === 'range' && selectedDate) { + if (Array.isArray(selectedDate)) { + const [startDate, endDate] = selectedDate; + const compareWithStart = startDate && isSameDate({ year, month, date }, startDate); + const compareWithEnd = endDate && isSameDate({ year, month, date }, endDate); + + if (compareWithStart && compareWithEnd && allowSameDay) return 'start-end'; + if (compareWithStart) return 'start'; + if (compareWithEnd) return 'end'; + if (startDate && endDate && curDate.getTime() > startDate.getTime() && curDate.getTime() < endDate.getTime()) return 'centre'; + } + } + + const minCurDate = new Date(year, month, date, 0, 0, 0); + if (curDate.getTime() < minTime || minCurDate.getTime() > maxTime) { + return 'disabled'; + } + return ''; + }; + + while (minYear < maxYear || (minYear === maxYear && minMonth <= maxMonth)) { + const target = getMonthDateRect(new Date(minYear, minMonth, 1)); + const months = []; + for (let i = 1; i <= 31; i += 1) { + if (i > target.lastDate) break; + const dateObj = { + date: new Date(minYear, minMonth, i), + day: i, + type: calcType(minYear, minMonth, i), + }; + months.push(format ? format(dateObj) : dateObj); + } + ans.push({ + year: minYear, + month: minMonth, + months, + weekdayOfFirstDay: target.weekdayOfFirstDay, + }); + const curDate = getDateRect(new Date(minYear, minMonth + 1, 1)); + minYear = curDate.year; + minMonth = curDate.month; + } + + return ans; + } + + select({ cellType, year, month, date }) { + const { type } = this; + const selectedDate = this.getTrimValue(); + if (cellType === 'disabled') return; + const selected = new Date(year, month, date); + this.value = selected; + if (type === 'range' && Array.isArray(selectedDate)) { + if (selectedDate.length === 1 && selected >= selectedDate[0]) { + this.value = [selectedDate[0], selected]; + } else { + this.value = [selected]; + } + } else if (type === 'multiple' && Array.isArray(selectedDate)) { + const newVal = [...selectedDate]; + const index = selectedDate.findIndex(item => isSameDate(item, selected)); + if (index > -1) { + newVal.splice(index, 1); + } else { + newVal.push(selected); + } + this.value = newVal; + } + return this.value; + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/shared/calendar/type.ts b/uni_modules/tdesign-uniapp/components/common/shared/calendar/type.ts new file mode 100644 index 0000000..0fd05cd --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/calendar/type.ts @@ -0,0 +1,14 @@ +export type TCalendarValue = number | Date; + +export type TDateType = 'selected' | 'disabled' | 'start-end' | 'start' | 'centre' | 'end' | ''; + +export type TCalendarType = 'single' | 'multiple' | 'range'; + +export interface TDate { + date: Date; + day: number; + type: TDateType; + className?: string; + prefix?: string; + suffix?: string; +} diff --git a/uni_modules/tdesign-uniapp/components/common/shared/color-picker/cmyk.js b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/cmyk.js new file mode 100644 index 0000000..531c727 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/cmyk.js @@ -0,0 +1,78 @@ +/** + * rgb 转 cmyk + * @param red + * @param green + * @param blue + * @returns + */ +export const rgb2cmyk = (red, green, blue) => { + let computedC = 0; + let computedM = 0; + let computedY = 0; + let computedK = 0; + const r = parseInt(`${red}`.replace(/\s/g, ''), 10); + const g = parseInt(`${green}`.replace(/\s/g, ''), 10); + const b = parseInt(`${blue}`.replace(/\s/g, ''), 10); + if (r === 0 && g === 0 && b === 0) { + computedK = 1; + return [0, 0, 0, 1]; + } + computedC = 1 - r / 255; + computedM = 1 - g / 255; + computedY = 1 - b / 255; + const minCMY = Math.min(computedC, Math.min(computedM, computedY)); + computedC = (computedC - minCMY) / (1 - minCMY); + computedM = (computedM - minCMY) / (1 - minCMY); + computedY = (computedY - minCMY) / (1 - minCMY); + computedK = minCMY; + return [computedC, computedM, computedY, computedK]; +}; +/** + * cmyk 转 rgb + * @param cyan + * @param magenta + * @param yellow + * @param black + * @returns + */ +export const cmyk2rgb = (cyan, magenta, yellow, black) => { + let c = cyan / 100; + let m = magenta / 100; + let y = yellow / 100; + const k = black / 100; + c = c * (1 - k) + k; + m = m * (1 - k) + k; + y = y * (1 - k) + k; + let r = 1 - c; + let g = 1 - m; + let b = 1 - y; + r = Math.round(255 * r); + g = Math.round(255 * g); + b = Math.round(255 * b); + return { + r, + g, + b, + }; +}; +const REG_CMYK_STRING = /cmyk\((\d+%?),(\d+%?),(\d+%?),(\d+%?)\)/; +const toNumber = str => Math.max(0, Math.min(255, parseInt(str, 10))); +/** + * 输入色转rgb + * @param input + * @returns + */ +export const cmykInputToColor = (input) => { + if (/cmyk/i.test(input)) { + const str = input.replace(/\s/g, ''); + const match = str.match(REG_CMYK_STRING); + const c = toNumber(match[1]); + const m = toNumber(match[2]); + const y = toNumber(match[3]); + const k = toNumber(match[4]); + const { r, g, b } = cmyk2rgb(c, m, y, k); + return `rgb(${r}, ${g}, ${b})`; + } + return input; +}; + diff --git a/uni_modules/tdesign-uniapp/components/common/shared/color-picker/color.js b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/color.js new file mode 100644 index 0000000..74b123c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/color.js @@ -0,0 +1,428 @@ +import tinyColor from '../../../npm/tinycolor2/esm/tinycolor.js'; +import { cmykInputToColor, rgb2cmyk } from './cmyk'; +import { parseGradientString, isGradientColor } from './gradient'; + +const mathRound = Math.round; +const hsv2rgba = states => tinyColor(states).toRgb(); +const hsv2hsva = states => tinyColor(states).toHsv(); +const hsv2hsla = states => tinyColor(states).toHsl(); +/** + * 将渐变对象转换成字符串 + * @param object + * @returns + */ +export const gradientColors2string = (object) => { + const { points, degree } = object; + const colorsStop = points + .sort((pA, pB) => pA.left - pB.left) + .map(p => `${p.color} ${Math.round(p.left * 100) / 100}%`); + return `linear-gradient(${degree}deg,${colorsStop.join(',')})`; +}; +/** + * 去除颜色的透明度 + * @param color + * @returns + */ +export const getColorWithoutAlpha = color => tinyColor(color).setAlpha(1) + .toHexString(); +// 生成一个随机ID +export const genId = () => (1 + Math.random() * 4294967295).toString(16); +/** + * 生成一个渐变颜色 + * @param left + * @param color + * @returns + */ +export const genGradientPoint = (left, color) => ({ + id: genId(), + left, + color, +}); +export class Color { + constructor(input) { + this.states = { + s: 100, + v: 100, + h: 100, + a: 1, + }; + this.gradientStates = { + colors: [], + degree: 0, + selectedId: null, + css: '', + }; + this.update(input); + } + + update(input) { + let _a; let _b; + const gradientColors = parseGradientString(input); + if (this.isGradient && !gradientColors) { + // 处理gradient模式下切换不同格式时的交互问题,输入的不是渐变字符串才使用当前处理 + const colorHsv = tinyColor(input).toHsv(); + this.states = colorHsv; + this.updateCurrentGradientColor(); + return; + } + this.originColor = input; + this.isGradient = false; + let colorInput = input; + if (gradientColors) { + this.isGradient = true; + const object = gradientColors; + const points = object.points.map(c => genGradientPoint(c.left, c.color)); + this.gradientStates = { + colors: points, + degree: object.degree, + selectedId: ((_a = points[0]) === null || _a === void 0 ? void 0 : _a.id) || null, + }; + this.gradientStates.css = this.linearGradient; + colorInput = (_b = this.gradientSelectedPoint) === null || _b === void 0 ? void 0 : _b.color; + } + this.updateStates(colorInput); + } + + get saturation() { + return this.states.s; + } + + set saturation(value) { + this.states.s = Math.max(0, Math.min(100, value)); + this.updateCurrentGradientColor(); + } + + get value() { + return this.states.v; + } + + set value(value) { + this.states.v = Math.max(0, Math.min(100, value)); + this.updateCurrentGradientColor(); + } + + get hue() { + return this.states.h; + } + + set hue(value) { + this.states.h = Math.max(0, Math.min(360, value)); + this.updateCurrentGradientColor(); + } + + get alpha() { + return this.states.a; + } + + set alpha(value) { + this.states.a = Math.max(0, Math.min(1, Math.round(value * 100) / 100)); + this.updateCurrentGradientColor(); + } + + get rgb() { + const { r, g, b } = hsv2rgba(this.states); + return `rgb(${mathRound(r)}, ${mathRound(g)}, ${mathRound(b)})`; + } + + get rgba() { + const { r, g, b, a } = hsv2rgba(this.states); + return `rgba(${mathRound(r)}, ${mathRound(g)}, ${mathRound(b)}, ${a})`; + } + + get hsv() { + const { h, s, v } = this.getHsva(); + return `hsv(${h}, ${s}%, ${v}%)`; + } + + get hsva() { + const { h, s, v, a } = this.getHsva(); + return `hsva(${h}, ${s}%, ${v}%, ${a})`; + } + + get hsl() { + const { h, s, l } = this.getHsla(); + return `hsl(${h}, ${s}%, ${l}%)`; + } + + get hsla() { + const { h, s, l, a } = this.getHsla(); + return `hsla(${h}, ${s}%, ${l}%, ${a})`; + } + + get hex() { + return tinyColor(this.states).toHexString(); + } + + get hex8() { + return tinyColor(this.states).toHex8String(); + } + + get cmyk() { + const { c, m, y, k } = this.getCmyk(); + return `cmyk(${c}, ${m}, ${y}, ${k})`; + } + + get css() { + if (this.isGradient) { + return this.linearGradient; + } + return this.rgba; + } + + get linearGradient() { + const { gradientColors, gradientDegree } = this; + return gradientColors2string({ + points: gradientColors, + degree: gradientDegree, + }); + } + + get gradientColors() { + return this.gradientStates.colors; + } + + set gradientColors(colors) { + this.gradientStates.colors = colors; + this.gradientStates.css = this.linearGradient; + } + + get gradientSelectedId() { + return this.gradientStates.selectedId; + } + + set gradientSelectedId(id) { + let _a; + if (id === this.gradientSelectedId) { + return; + } + this.gradientStates.selectedId = id; + this.updateStates((_a = this.gradientSelectedPoint) === null || _a === void 0 ? void 0 : _a.color); + } + + get gradientDegree() { + return this.gradientStates.degree; + } + + set gradientDegree(degree) { + this.gradientStates.degree = Math.max(0, Math.min(360, degree)); + this.gradientStates.css = this.linearGradient; + } + + get gradientSelectedPoint() { + const { gradientColors, gradientSelectedId } = this; + return gradientColors.find(color => color.id === gradientSelectedId); + } + + getFormatsColorMap() { + return { + HEX: this.hex, + CMYK: this.cmyk, + RGB: this.rgb, + RGBA: this.rgba, + HSL: this.hsl, + HSLA: this.hsla, + HSV: this.hsv, + HSVA: this.hsva, + CSS: this.css, + HEX8: this.hex8, + }; + } + + updateCurrentGradientColor() { + const { isGradient, gradientColors, gradientSelectedId } = this; + const { length } = gradientColors; + const current = this.gradientSelectedPoint; + if (!isGradient || length === 0 || !current) { + return false; + } + const index = gradientColors.findIndex(color => color.id === gradientSelectedId); + const newColor = { ...current, color: this.rgba }; + gradientColors.splice(index, 1, newColor); + this.gradientColors = gradientColors.slice(); + return this; + } + + updateStates(input) { + const color = tinyColor(cmykInputToColor(input)); + const hsva = color.toHsv(); + this.states = hsva; + } + + getRgba() { + const { r, g, b, a } = hsv2rgba(this.states); + return { + r: mathRound(r), + g: mathRound(g), + b: mathRound(b), + a, + }; + } + + getCmyk() { + const { r, g, b } = this.getRgba(); + const [c, m, y, k] = rgb2cmyk(r, g, b); + return { + c: mathRound(c * 100), + m: mathRound(m * 100), + y: mathRound(y * 100), + k: mathRound(k * 100), + }; + } + + getHsva() { + let { h, s, v, a } = hsv2hsva(this.states); + h = mathRound(h); + s = mathRound(s * 100); + v = mathRound(v * 100); + a *= 1; + return { + h, + s, + v, + a, + }; + } + + getHsla() { + let { h, s, l, a } = hsv2hsla(this.states); + h = mathRound(h); + s = mathRound(s * 100); + l = mathRound(l * 100); + a *= 1; + return { + h, + s, + l, + a, + }; + } + + /** + * 判断输入色是否与当前色相同 + * @param color + * @returns + */ + equals(color) { + return tinyColor.equals(this.rgba, color); + } + + /** + * 校验输入色是否是一个有效颜色 + * @param color + * @returns + */ + static isValid(color) { + if (parseGradientString(color)) { + return true; + } + return tinyColor(color).isValid(); + } + + static hsva2color(h, s, v, a) { + return tinyColor({ + h, + s, + v, + a, + }).toHsvString(); + } + + static hsla2color(h, s, l, a) { + return tinyColor({ + h, + s, + l, + a, + }).toHslString(); + } + + static rgba2color(r, g, b, a) { + return tinyColor({ + r, + g, + b, + a, + }).toHsvString(); + } + + static hex2color(hex, a) { + const color = tinyColor(hex); + color.setAlpha(a); + return color.toHexString(); + } + + /** + * 对象转颜色字符串 + * @param object + * @param format + * @returns + */ + static object2color(object, format) { + if (format === 'CMYK') { + const { c, m, y, k } = object; + return `cmyk(${c}, ${m}, ${y}, ${k})`; + } + const color = tinyColor(object, { + format, + }); + return color.toRgbString(); + } +} +/** + * 是否是渐变色 + * @param input + * @returns + */ +Color.isGradientColor = input => !!isGradientColor(input); +/** + * 比较两个颜色是否相同 + * @param color1 + * @param color2 + * @returns + */ +Color.compare = (color1, color2) => { + const isGradientColor1 = Color.isGradientColor(color1); + const isGradientColor2 = Color.isGradientColor(color2); + if (isGradientColor1 && isGradientColor2) { + const gradientColor1 = gradientColors2string(parseGradientString(color1)); + const gradientColor2 = gradientColors2string(parseGradientString(color2)); + return gradientColor1 === gradientColor2; + } + if (!isGradientColor1 && !isGradientColor2) { + return tinyColor.equals(color1, color2); + } + return false; +}; +const COLOR_OBJECT_OUTPUT_KEYS = [ + 'alpha', + 'css', + 'hex', + 'hex8', + 'hsl', + 'hsla', + 'hsv', + 'hsva', + 'rgb', + 'rgba', + 'saturation', + 'value', + 'isGradient', +]; +/** + * 获取对外输出的color对象 + * @param color + * @returns + */ +export const getColorObject = (color) => { + if (!color) { + return null; + } + const colorObject = Object.create(null); + // eslint-disable-next-line no-return-assign + COLOR_OBJECT_OUTPUT_KEYS.forEach(key => (colorObject[key] = color[key])); + if (color.isGradient) { + colorObject.linearGradient = color.linearGradient; + } + return colorObject; +}; +export default Color; diff --git a/uni_modules/tdesign-uniapp/components/common/shared/color-picker/gradient.js b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/gradient.js new file mode 100644 index 0000000..c2fffe9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/gradient.js @@ -0,0 +1,180 @@ +/** + * 用于反解析渐变字符串为对象 + * https://stackoverflow.com/questions/20215440/parse-css-gradient-rule-with-javascript-regex + */ +import tinyColor from '../../../npm/tinycolor2/esm/tinycolor.js'; +import { isString, isNull } from '../../validator'; +/** ../../validator.js + * Utility combine multiple regular expressions. + * + * @param {RegExp[]|string[]} regexpList List of regular expressions or strings. + * @param {string} flags Normal RegExp flags. + */ +const combineRegExp = (regexpList, flags) => { + let source = ''; + for (let i = 0; i < regexpList.length; i += 1) { + if (isString(regexpList[i])) { + source += regexpList[i]; + } else { + source += regexpList[i].source; + } + } + return new RegExp(source, flags); +}; +/** + * Generate the required regular expressions once. + * + * Regular Expressions are easier to manage this way and can be well described. + * + * @result {object} Object containing regular expressions. + */ +const generateRegExp = () => { + // Note any variables with "Capture" in name include capturing bracket set(s). + const searchFlags = 'gi'; // ignore case for angles, "rgb" etc + const rAngle = /(?:[+-]?\d*\.?\d+)(?:deg|grad|rad|turn)/; // Angle +ive, -ive and angle types + // optional 2nd part + const rSideCornerCapture = /to\s+((?:(?:left|right|top|bottom)(?:\s+(?:top|bottom|left|right))?))/; + const rComma = /\s*,\s*/; // Allow space around comma. + const rColorHex = /#(?:[a-f0-9]{6}|[a-f0-9]{3})/; // 3 or 6 character form + const rDigits3 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*\)/; + const // "(1, 2, 3)" + rDigits4 = /\(\s*(?:\d{1,3}\s*,\s*){2}\d{1,3}\s*,\s*\d*\.?\d+\)/; + const // "(1, 2, 3, 4)" + rValue = /(?:[+-]?\d*\.?\d+)(?:%|[a-z]+)?/; + const // ".9", "-5px", "100%". + rKeyword = /[_a-z-][_a-z0-9-]*/; + const // "red", "transparent". + rColor = combineRegExp(['(?:', rColorHex, '|', '(?:rgb|hsl)', rDigits3, '|', '(?:rgba|hsla)', rDigits4, '|', rKeyword, ')'], ''); + const rColorStop = combineRegExp([rColor, '(?:\\s+', rValue, '(?:\\s+', rValue, ')?)?'], ''); + const // Single Color Stop, optional %, optional length. + rColorStopList = combineRegExp(['(?:', rColorStop, rComma, ')*', rColorStop], ''); + const // List of color stops min 1. + rLineCapture = combineRegExp(['(?:(', rAngle, ')|', rSideCornerCapture, ')'], ''); + const // Angle or SideCorner + rGradientSearch = combineRegExp(['(?:(', rLineCapture, ')', rComma, ')?(', rColorStopList, ')'], searchFlags); + const // Capture 1:"line", 2:"angle" (optional), 3:"side corner" (optional) and 4:"stop list". + rColorStopSearch = combineRegExp(['\\s*(', rColor, ')', '(?:\\s+', '(', rValue, '))?', '(?:', rComma, '\\s*)?'], searchFlags); // Capture 1:"color" and 2:"position" (optional). + return { + gradientSearch: rGradientSearch, + colorStopSearch: rColorStopSearch, + }; +}; +/** + * Actually parse the input gradient parameters string into an object for reusability. + * + * + * @note Really this only supports the standard syntax not historical versions, see MDN for details + * https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient + * + * @param regExpLib + * @param {string} input + * @returns {object|undefined} + */ +const parseGradient = (regExpLib, input) => { + let result; + let matchColorStop; + let stopResult; + // reset search position, because we reuse regex. + regExpLib.gradientSearch.lastIndex = 0; + const matchGradient = regExpLib.gradientSearch.exec(input); + if (!isNull(matchGradient)) { + result = { + original: matchGradient[0], + colorStopList: [], + }; + // Line (Angle or Side-Corner). + if (matchGradient[1]) { + // eslint-disable-next-line prefer-destructuring + result.line = matchGradient[1]; + } + // Angle or undefined if side-corner. + if (matchGradient[2]) { + // eslint-disable-next-line prefer-destructuring + result.angle = matchGradient[2]; + } + // Side-corner or undefined if angle. + if (matchGradient[3]) { + // eslint-disable-next-line prefer-destructuring + result.sideCorner = matchGradient[3]; + } + // reset search position, because we reuse regex. + regExpLib.colorStopSearch.lastIndex = 0; + // Loop though all the color-stops. + matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]); + while (!isNull(matchColorStop)) { + stopResult = { + color: matchColorStop[1], + }; + // Position (optional). + if (matchColorStop[2]) { + // eslint-disable-next-line prefer-destructuring + stopResult.position = matchColorStop[2]; + } + result.colorStopList.push(stopResult); + // Continue searching from previous position. + matchColorStop = regExpLib.colorStopSearch.exec(matchGradient[4]); + } + } + // Can be undefined if match not found. + return result; +}; +const REGEXP_LIB = generateRegExp(); +const REG_GRADIENT = /.*gradient\s*\(((?:\([^)]*\)|[^)(]*)*)\)/gim; +/** + * 验证是否是渐变字符串 + * @param input + * @returns + */ +export const isGradientColor = (input) => { + REG_GRADIENT.lastIndex = 0; + return REG_GRADIENT.exec(input); +}; +// 边界字符串和角度关系 +const sideCornerDegreeMap = { + top: 0, + right: 90, + bottom: 180, + left: 270, + 'top left': 225, + 'left top': 225, + 'top right': 135, + 'right top': 135, + 'bottom left': 315, + 'left bottom': 315, + 'bottom right': 45, + 'right bottom': 45, +}; +/** + * 解析渐变字符串为 GradientColors 对象 + * @param input + * @returns + */ +export const parseGradientString = (input) => { + const match = isGradientColor(input); + if (!match) { + return false; + } + const gradientColors = { + points: [], + degree: 0, + }; + const result = parseGradient(REGEXP_LIB, match[1]); + if (result.original.trim() !== match[1].trim()) { + return false; + } + const points = result.colorStopList.map(({ color, position }) => { + const point = Object.create(null); + point.color = tinyColor(color).toRgbString(); + point.left = parseFloat(position); + return point; + }); + gradientColors.points = points; + let degree = parseInt(result.angle, 10); + if (Number.isNaN(degree)) { + degree = sideCornerDegreeMap[result.sideCorner] || 90; + } + gradientColors.degree = degree; + return gradientColors; +}; +export default parseGradientString; + diff --git a/uni_modules/tdesign-uniapp/components/common/shared/color-picker/index.js b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/index.js new file mode 100644 index 0000000..ad18475 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/color-picker/index.js @@ -0,0 +1,3 @@ +export * from './cmyk'; +export * from './color'; +export * from './gradient'; diff --git a/uni_modules/tdesign-uniapp/components/common/shared/date.js b/uni_modules/tdesign-uniapp/components/common/shared/date.js new file mode 100644 index 0000000..5d59db8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/date.js @@ -0,0 +1,46 @@ + +export const getDateRect = (date) => { + const _date = new Date(date); + + return { + year: _date.getFullYear(), + month: _date.getMonth(), + date: _date.getDate(), + day: _date.getDay(), + time: _date.getTime(), + }; +}; + +export const isSameDate = (date1, date2) => { + if (date1 instanceof Date || typeof date1 === 'number') date1 = getDateRect(date1); + if (date2 instanceof Date || typeof date2 === 'number') date2 = getDateRect(date2); + const keys = ['year', 'month', 'date']; + return keys.every(key => date1[key] === date2[key]); +}; + +export const getMonthDateRect = (date) => { + const { year, month } = getDateRect(date); + const firstDay = new Date(year, month, 1); + const weekdayOfFirstDay = firstDay.getDay(); + const lastDate = new Date(+new Date(year, month + 1, 1) - 24 * 3600 * 1000).getDate(); + + return { + year, + month, + weekdayOfFirstDay, + lastDate, + }; +}; + +export const isValidDate = val => typeof val === 'number' || val instanceof Date; + +export const getDate = (...args) => { + const now = new Date(); + if (args.length === 0) return now; + if (args.length === 1 && args[0] <= 1000) { + const { year, month, date } = getDateRect(now); + return new Date(year, month + args[0], date); + } + // eslint-disable-next-line prefer-spread + return Date.apply(null, args); +}; diff --git a/uni_modules/tdesign-uniapp/components/common/shared/qrcode/qrcodegen.js b/uni_modules/tdesign-uniapp/components/common/shared/qrcode/qrcodegen.js new file mode 100644 index 0000000..b75846b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/qrcode/qrcodegen.js @@ -0,0 +1,884 @@ +/* eslint-disable */ +// Copyright (c) Project Nayuki. (MIT License) +// https://www.nayuki.io/page/qr-code-generator-library +// Modification with code reorder and prettier +// -------------------------------------------- +// Appends the given number of low-order bits of the given value +// to the given buffer. Requires 0 <= len <= 31 and 0 <= val < 2^len. +function appendBits(val, len, bb) { + if (len < 0 || len > 31 || val >>> len !== 0) { + throw new RangeError('Value out of range'); + } + for (let i = len - 1; i >= 0; i-- // Append bit by bit + ) { + bb.push((val >>> i) & 1); + } +} +// Returns true iff the i'th bit of x is set to 1. +function getBit(x, i) { + return ((x >>> i) & 1) !== 0; +} +// Throws an exception if the given condition is false. +function assert(cond) { + if (!cond) { + throw new Error('Assertion error'); + } +} +/* ---- Public helper enumeration ----*/ +/* + * Describes how a segment's data bits are numbererpreted. Immutable. + */ +export class Mode { + constructor(modeBits, numBitsCharCount) { + this.modeBits = modeBits; + this.numBitsCharCount = numBitsCharCount; + } + /* -- Method --*/ + // (Package-private) Returns the bit width of the character count field for a segment in + // this mode in a QR Code at the given version number. The result is in the range [0, 16]. + numCharCountBits(ver) { + return this.numBitsCharCount[Math.floor((ver + 7) / 17)]; + } +} +/* -- Constants --*/ +Mode.NUMERIC = new Mode(0x1, [10, 12, 14]); +Mode.ALPHANUMERIC = new Mode(0x2, [9, 11, 13]); +Mode.BYTE = new Mode(0x4, [8, 16, 16]); +Mode.KANJI = new Mode(0x8, [8, 10, 12]); +Mode.ECI = new Mode(0x7, [0, 0, 0]); +/* ---- Public helper enumeration ----*/ +/* + * The error correction level in a QR Code symbol. Immutable. + */ +export class Ecc { + constructor(ordinal, formatBits) { + this.ordinal = ordinal; + this.formatBits = formatBits; + } +} +/* -- Constants --*/ +Ecc.LOW = new Ecc(0, 1); // The QR Code can tolerate about 7% erroneous codewords +Ecc.MEDIUM = new Ecc(1, 0); // The QR Code can tolerate about 15% erroneous codewords +Ecc.QUARTILE = new Ecc(2, 3); // The QR Code can tolerate about 25% erroneous codewords +Ecc.HIGH = new Ecc(3, 2); // The QR Code can tolerate about 30% erroneous codewords +/* + * A segment of character/binary/control data in a QR Code symbol. + * Instances of this class are immutable. + * The mid-level way to create a segment is to take the payload data + * and call a static factory function such as QrSegment.makeNumeric(). + * The low-level way to create a segment is to custom-make the bit buffer + * and call the QrSegment() constructor with appropriate values. + * This segment class imposes no length restrictions, but QR Codes have restrictions. + * Even in the most favorable conditions, a QR Code can only hold 7089 characters of data. + * Any segment longer than this is meaningless for the purpose of generating QR Codes. + */ +export class QrSegment { + // Creates a new QR Code segment with the given attributes and data. + // The character count (numChars) must agree with the mode and the bit buffer length, + // but the constranumber isn't checked. The given bit buffer is cloned and stored. + constructor(mode, numChars, bitData) { + this.mode = mode; + this.numChars = numChars; + this.bitData = bitData; + if (numChars < 0) { + throw new RangeError('Invalid argument'); + } + this.bitData = bitData.slice(); // Make defensive copy + } + /* -- Static factory functions (mid level) --*/ + // Returns a segment representing the given binary data encoded in + // byte mode. All input byte arrays are acceptable. Any text string + // can be converted to UTF-8 bytes and encoded as a byte mode segment. + static makeBytes(data) { + const bb = []; + for (const b of data) { + appendBits(b, 8, bb); + } + return new QrSegment(Mode.BYTE, data.length, bb); + } + // Returns a segment representing the given string of decimal digits encoded in numeric mode. + static makeNumeric(digits) { + if (!QrSegment.isNumeric(digits)) { + throw new RangeError('String contains non-numeric characters'); + } + const bb = []; + for (let i = 0; i < digits.length;) { + // Consume up to 3 digits per iteration + const n = Math.min(digits.length - i, 3); + appendBits(parseInt(digits.substring(i, i + n), 10), n * 3 + 1, bb); + i += n; + } + return new QrSegment(Mode.NUMERIC, digits.length, bb); + } + // Returns a segment representing the given text string encoded in alphanumeric mode. + // The characters allowed are: 0 to 9, A to Z (uppercase only), space, + // dollar, percent, asterisk, plus, hyphen, period, slash, colon. + static makeAlphanumeric(text) { + if (!QrSegment.isAlphanumeric(text)) { + throw new RangeError('String contains unencodable characters in alphanumeric mode'); + } + const bb = []; + let i; + for (i = 0; i + 2 <= text.length; i += 2) { + // Process groups of 2 + let temp = QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)) * 45; + temp += QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i + 1)); + appendBits(temp, 11, bb); + } + if (i < text.length) { + // 1 character remaining + appendBits(QrSegment.ALPHANUMERIC_CHARSET.indexOf(text.charAt(i)), 6, bb); + } + return new QrSegment(Mode.ALPHANUMERIC, text.length, bb); + } + // Returns a new mutable list of zero or more segments to represent the given Unicode text string. + // The result may use various segment modes and switch modes to optimize the length of the bit stream. + static makeSegments(text) { + // Select the most efficient segment encoding automatically + if (text === '') { + return []; + } + if (QrSegment.isNumeric(text)) { + return [QrSegment.makeNumeric(text)]; + } + if (QrSegment.isAlphanumeric(text)) { + return [QrSegment.makeAlphanumeric(text)]; + } + return [QrSegment.makeBytes(QrSegment.toUtf8ByteArray(text))]; + } + // Returns a segment representing an Extended Channel Interpretation + // (ECI) designator with the given assignment value. + static makeEci(assignVal) { + const bb = []; + if (assignVal < 0) { + throw new RangeError('ECI assignment value out of range'); + } + else if (assignVal < 1 << 7) { + appendBits(assignVal, 8, bb); + } + else if (assignVal < 1 << 14) { + appendBits(0b10, 2, bb); + appendBits(assignVal, 14, bb); + } + else if (assignVal < 1000000) { + appendBits(0b110, 3, bb); + appendBits(assignVal, 21, bb); + } + else { + throw new RangeError('ECI assignment value out of range'); + } + return new QrSegment(Mode.ECI, 0, bb); + } + // Tests whether the given string can be encoded as a segment in numeric mode. + // A string is encodable iff each character is in the range 0 to 9. + static isNumeric(text) { + return QrSegment.NUMERIC_REGEX.test(text); + } + // Tests whether the given string can be encoded as a segment in alphanumeric mode. + // A string is encodable iff each character is in the following set: 0 to 9, A to Z + // (uppercase only), space, dollar, percent, asterisk, plus, hyphen, period, slash, colon. + static isAlphanumeric(text) { + return QrSegment.ALPHANUMERIC_REGEX.test(text); + } + /* -- Methods --*/ + // Returns a new copy of the data bits of this segment. + getData() { + return this.bitData.slice(); // Make defensive copy + } + // (Package-private) Calculates and returns the number of bits needed to encode the given segments at + // the given version. The result is infinity if a segment has too many characters to fit its length field. + static getTotalBits(segs, version) { + let result = 0; + for (const seg of segs) { + const ccbits = seg.mode.numCharCountBits(version); + if (seg.numChars >= 1 << ccbits) { + return Infinity; // The segment's length doesn't fit the field's bit width + } + result += 4 + ccbits + seg.bitData.length; + } + return result; + } + // Returns a new array of bytes representing the given string encoded in UTF-8. + static toUtf8ByteArray(input) { + const str = encodeURI(input); + const result = []; + for (let i = 0; i < str.length; i++) { + if (str.charAt(i) !== '%') { + result.push(str.charCodeAt(i)); + } + else { + result.push(parseInt(str.substring(i + 1, i + 3), 16)); + i += 2; + } + } + return result; + } +} +/* -- Constants --*/ +// Describes precisely all strings that are encodable in numeric mode. +QrSegment.NUMERIC_REGEX = /^[0-9]*$/; +// Describes precisely all strings that are encodable in alphanumeric mode. +QrSegment.ALPHANUMERIC_REGEX = /^[A-Z0-9 $%*+.\/:-]*$/; +// The set of all legal characters in alphanumeric mode, +// where each character value maps to the index in the string. +QrSegment.ALPHANUMERIC_CHARSET = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:'; +/* + * A QR Code symbol, which is a type of two-dimension barcode. + * Invented by Denso Wave and described in the ISO/IEC 18004 standard. + * Instances of this class represent an immutable square grid of dark and light cells. + * The class provides static factory functions to create a QR Code from text or binary data. + * The class covers the QR Code Model 2 specification, supporting all versions (sizes) + * from 1 to 40, all 4 error correction levels, and 4 character encoding modes. + * + * Ways to create a QR Code object: + * - High level: Take the payload data and call QrCode.encodeText() or QrCode.encodeBinary(). + * - Mid level: Custom-make the list of segments and call QrCode.encodeSegments(). + * - Low level: Custom-make the array of data codeword bytes (including + * segment headers and final padding, excluding error correction codewords), + * supply the appropriate version number, and call the QrCode() constructor. + * (Note that all ways require supplying the desired error correction level.) + */ +export class QrCode { + // Creates a new QR Code with the given version number, + // error correction level, data codeword bytes, and mask number. + // This is a low-level API that most users should not use directly. + // A mid-level API is the encodeSegments() function. + constructor( + // The version number of this QR Code, which is between 1 and 40 (inclusive). + // This determines the size of this barcode. + version, + // The error correction level used in this QR Code. + errorCorrectionLevel, dataCodewords, oriMsk) { + // The modules of this QR Code (false = light, true = dark). + // Immutable after constructor finishes. Accessed through getModule(). + this.modules = []; + // Indicates function modules that are not subjected to masking. Discarded when constructor finishes. + this.isFunction = []; + let msk = oriMsk; + this.version = version; + this.errorCorrectionLevel = errorCorrectionLevel; + // Check scalar arguments + if (version < QrCode.MIN_VERSION || version > QrCode.MAX_VERSION) { + throw new RangeError('Version value out of range'); + } + if (msk < -1 || msk > 7) { + throw new RangeError('Mask value out of range'); + } + this.size = version * 4 + 17; + // Initialize both grids to be size*size arrays of Boolean false + const row = []; + for (let i = 0; i < this.size; i++) { + row.push(false); + } + for (let i = 0; i < this.size; i++) { + this.modules.push(row.slice()); // Initially all light + this.isFunction.push(row.slice()); + } + // Compute ECC, draw modules + this.drawFunctionPatterns(); + const allCodewords = this.addEccAndInterleave(dataCodewords); + this.drawCodewords(allCodewords); + // Do masking + if (msk === -1) { + // Automatically choose best mask + let minPenalty = 1000000000; + for (let i = 0; i < 8; i++) { + this.applyMask(i); + this.drawFormatBits(i); + const penalty = this.getPenaltyScore(); + if (penalty < minPenalty) { + msk = i; + minPenalty = penalty; + } + this.applyMask(i); // Undoes the mask due to XOR + } + } + assert(msk >= 0 && msk <= 7); + this.mask = msk; + this.applyMask(msk); // Apply the final choice of mask + this.drawFormatBits(msk); // Overwrite old format bits + this.isFunction = []; + } + /* -- Static factory functions (high level) --*/ + // Returns a QR Code representing the given Unicode text string at the given error correction level. + // As a conservative upper bound, this function is guaranteed to succeed for strings that have 738 or fewer + // Unicode code ponumbers (not UTF-16 code units) if the low error correction level is used. The smallest possible + // QR Code version is automatically chosen for the output. The ECC level of the result may be higher than the + // ecl argument if it can be done without increasing the version. + static encodeText(text, ecl) { + const segs = QrSegment.makeSegments(text); + return QrCode.encodeSegments(segs, ecl); + } + // Returns a QR Code representing the given binary data at the given error correction level. + // This function always encodes using the binary segment mode, not any text mode. The maximum number of + // bytes allowed is 2953. The smallest possible QR Code version is automatically chosen for the output. + // The ECC level of the result may be higher than the ecl argument if it can be done without increasing the version. + static encodeBinary(data, ecl) { + const seg = QrSegment.makeBytes(data); + return QrCode.encodeSegments([seg], ecl); + } + /* -- Static factory functions (mid level) --*/ + // Returns a QR Code representing the given segments with the given encoding parameters. + // The smallest possible QR Code version within the given range is automatically + // chosen for the output. Iff boostEcl is true, then the ECC level of the result + // may be higher than the ecl argument if it can be done without increasing the + // version. The mask number is either between 0 to 7 (inclusive) to force that + // mask, or -1 to automatically choose an appropriate mask (which may be slow). + // This function allows the user to create a custom sequence of segments that switches + // between modes (such as alphanumeric and byte) to encode text in less space. + // This is a mid-level API; the high-level API is encodeText() and encodeBinary(). + static encodeSegments(segs, oriEcl, minVersion = 1, maxVersion = 40, mask = -1, boostEcl = true) { + if (!(QrCode.MIN_VERSION <= minVersion && minVersion <= maxVersion && maxVersion <= QrCode.MAX_VERSION) || + mask < -1 || + mask > 7) { + throw new RangeError('Invalid value'); + } + // Find the minimal version number to use + let version; + let dataUsedBits; + for (version = minVersion;; version++) { + const dataCapacityBits = QrCode.getNumDataCodewords(version, oriEcl) * 8; // Number of data bits available + const usedBits = QrSegment.getTotalBits(segs, version); + if (usedBits <= dataCapacityBits) { + dataUsedBits = usedBits; + break; // This version number is found to be suitable + } + if (version >= maxVersion) { + // All versions in the range could not fit the given data + throw new RangeError('Data too long'); + } + } + let ecl = oriEcl; + // Increase the error correction level while the data still fits in the current version number + for (const newEcl of [Ecc.MEDIUM, Ecc.QUARTILE, Ecc.HIGH]) { + // From low to high + if (boostEcl && dataUsedBits <= QrCode.getNumDataCodewords(version, newEcl) * 8) { + ecl = newEcl; + } + } + // Concatenate all segments to create the data bit string + const bb = []; + for (const seg of segs) { + appendBits(seg.mode.modeBits, 4, bb); + appendBits(seg.numChars, seg.mode.numCharCountBits(version), bb); + for (const b of seg.getData()) { + bb.push(b); + } + } + assert(bb.length === dataUsedBits); + // Add terminator and pad up to a byte if applicable + const dataCapacityBits = QrCode.getNumDataCodewords(version, ecl) * 8; + assert(bb.length <= dataCapacityBits); + appendBits(0, Math.min(4, dataCapacityBits - bb.length), bb); + appendBits(0, (8 - (bb.length % 8)) % 8, bb); + assert(bb.length % 8 === 0); + // Pad with alternating bytes until data capacity is reached + for (let padByte = 0xec; bb.length < dataCapacityBits; padByte ^= 0xec ^ 0x11) { + appendBits(padByte, 8, bb); + } + // Pack bits numbero bytes in big endian + const dataCodewords = []; + while (dataCodewords.length * 8 < bb.length) { + dataCodewords.push(0); + } + bb.forEach((b, i) => { + dataCodewords[i >>> 3] |= b << (7 - (i & 7)); + }); + // Create the QR Code object + return new QrCode(version, ecl, dataCodewords, mask); + } + /* -- Accessor methods --*/ + // Returns the color of the module (pixel) at the given coordinates, which is false + // for light or true for dark. The top left corner has the coordinates (x=0, y=0). + // If the given coordinates are out of bounds, then false (light) is returned. + getModule(x, y) { + return x >= 0 && x < this.size && y >= 0 && y < this.size && this.modules[y][x]; + } + // Modified to expose modules for easy access + getModules() { + return this.modules; + } + /* -- Private helper methods for constructor: Drawing function modules --*/ + // Reads this object's version field, and draws and marks all function modules. + drawFunctionPatterns() { + // Draw horizontal and vertical timing patterns + for (let i = 0; i < this.size; i++) { + this.setFunctionModule(6, i, i % 2 === 0); + this.setFunctionModule(i, 6, i % 2 === 0); + } + // Draw 3 finder patterns (all corners except bottom right; overwrites some timing modules) + this.drawFinderPattern(3, 3); + this.drawFinderPattern(this.size - 4, 3); + this.drawFinderPattern(3, this.size - 4); + // Draw numerous alignment patterns + const alignPatPos = this.getAlignmentPatternPositions(); + const numAlign = alignPatPos.length; + for (let i = 0; i < numAlign; i++) { + for (let j = 0; j < numAlign; j++) { + // Don't draw on the three finder corners + if (!((i === 0 && j === 0) || (i === 0 && j === numAlign - 1) || (i === numAlign - 1 && j === 0))) { + this.drawAlignmentPattern(alignPatPos[i], alignPatPos[j]); + } + } + } + // Draw configuration data + this.drawFormatBits(0); // Dummy mask value; overwritten later in the constructor + this.drawVersion(); + } + // Draws two copies of the format bits (with its own error correction code) + // based on the given mask and this object's error correction level field. + drawFormatBits(mask) { + // Calculate error correction code and pack bits + const data = (this.errorCorrectionLevel.formatBits << 3) | mask; // errCorrLvl is unumber2, mask is unumber3 + let rem = data; + for (let i = 0; i < 10; i++) { + rem = (rem << 1) ^ ((rem >>> 9) * 0x537); + } + const bits = ((data << 10) | rem) ^ 0x5412; // unumber15 + assert(bits >>> 15 === 0); + // Draw first copy + for (let i = 0; i <= 5; i++) { + this.setFunctionModule(8, i, getBit(bits, i)); + } + this.setFunctionModule(8, 7, getBit(bits, 6)); + this.setFunctionModule(8, 8, getBit(bits, 7)); + this.setFunctionModule(7, 8, getBit(bits, 8)); + for (let i = 9; i < 15; i++) { + this.setFunctionModule(14 - i, 8, getBit(bits, i)); + } + // Draw second copy + for (let i = 0; i < 8; i++) { + this.setFunctionModule(this.size - 1 - i, 8, getBit(bits, i)); + } + for (let i = 8; i < 15; i++) { + this.setFunctionModule(8, this.size - 15 + i, getBit(bits, i)); + } + this.setFunctionModule(8, this.size - 8, true); // Always dark + } + // Draws two copies of the version bits (with its own error correction code), + // based on this object's version field, iff 7 <= version <= 40. + drawVersion() { + if (this.version < 7) { + return; + } + // Calculate error correction code and pack bits + let rem = this.version; // version is unumber6, in the range [7, 40] + for (let i = 0; i < 12; i++) { + rem = (rem << 1) ^ ((rem >>> 11) * 0x1f25); + } + const bits = (this.version << 12) | rem; // unumber18 + assert(bits >>> 18 === 0); + // Draw two copies + for (let i = 0; i < 18; i++) { + const color = getBit(bits, i); + const a = this.size - 11 + (i % 3); + const b = Math.floor(i / 3); + this.setFunctionModule(a, b, color); + this.setFunctionModule(b, a, color); + } + } + // Draws a 9*9 finder pattern including the border separator, + // with the center module at (x, y). Modules can be out of bounds. + drawFinderPattern(x, y) { + for (let dy = -4; dy <= 4; dy++) { + for (let dx = -4; dx <= 4; dx++) { + const dist = Math.max(Math.abs(dx), Math.abs(dy)); // Chebyshev/infinity norm + const xx = x + dx; + const yy = y + dy; + if (xx >= 0 && xx < this.size && yy >= 0 && yy < this.size) { + this.setFunctionModule(xx, yy, dist !== 2 && dist !== 4); + } + } + } + } + // Draws a 5*5 alignment pattern, with the center module + // at (x, y). All modules must be in bounds. + drawAlignmentPattern(x, y) { + for (let dy = -2; dy <= 2; dy++) { + for (let dx = -2; dx <= 2; dx++) { + this.setFunctionModule(x + dx, y + dy, Math.max(Math.abs(dx), Math.abs(dy)) !== 1); + } + } + } + // Sets the color of a module and marks it as a function module. + // Only used by the constructor. Coordinates must be in bounds. + setFunctionModule(x, y, isDark) { + this.modules[y][x] = isDark; + this.isFunction[y][x] = true; + } + /* -- Private helper methods for constructor: Codewords and masking --*/ + // Returns a new byte string representing the given data with the appropriate error correction + // codewords appended to it, based on this object's version and error correction level. + addEccAndInterleave(data) { + const ver = this.version; + const ecl = this.errorCorrectionLevel; + if (data.length !== QrCode.getNumDataCodewords(ver, ecl)) { + throw new RangeError('Invalid argument'); + } + // Calculate parameter numbers + const numBlocks = QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]; + const blockEccLen = QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver]; + const rawCodewords = Math.floor(QrCode.getNumRawDataModules(ver) / 8); + const numShortBlocks = numBlocks - (rawCodewords % numBlocks); + const shortBlockLen = Math.floor(rawCodewords / numBlocks); + // Split data numbero blocks and append ECC to each block + const blocks = []; + const rsDiv = QrCode.reedSolomonComputeDivisor(blockEccLen); + for (let i = 0, k = 0; i < numBlocks; i++) { + const dat = data.slice(k, k + shortBlockLen - blockEccLen + (i < numShortBlocks ? 0 : 1)); + k += dat.length; + const ecc = QrCode.reedSolomonComputeRemainder(dat, rsDiv); + if (i < numShortBlocks) { + dat.push(0); + } + blocks.push(dat.concat(ecc)); + } + // Interleave (not concatenate) the bytes from every block numbero a single sequence + const result = []; + for (let i = 0; i < blocks[0].length; i++) { + blocks.forEach((block, j) => { + // Skip the padding byte in short blocks + if (i !== shortBlockLen - blockEccLen || j >= numShortBlocks) { + result.push(block[i]); + } + }); + } + assert(result.length === rawCodewords); + return result; + } + // Draws the given sequence of 8-bit codewords (data and error correction) onto the entire + // data area of this QR Code. Function modules need to be marked off before this is called. + drawCodewords(data) { + if (data.length !== Math.floor(QrCode.getNumRawDataModules(this.version) / 8)) { + throw new RangeError('Invalid argument'); + } + let i = 0; // Bit index numbero the data + // Do the funny zigzag scan + for (let right = this.size - 1; right >= 1; right -= 2) { + // Index of right column in each column pair + if (right === 6) { + right = 5; + } + for (let vert = 0; vert < this.size; vert++) { + // Vertical counter + for (let j = 0; j < 2; j++) { + const x = right - j; // Actual x coordinate + const upward = ((right + 1) & 2) === 0; + const y = upward ? this.size - 1 - vert : vert; // Actual y coordinate + if (!this.isFunction[y][x] && i < data.length * 8) { + this.modules[y][x] = getBit(data[i >>> 3], 7 - (i & 7)); + i++; + } + // If this QR Code has any remainder bits (0 to 7), they were assigned as + // 0/false/light by the constructor and are left unchanged by this method + } + } + } + assert(i === data.length * 8); + } + // XORs the codeword modules in this QR Code with the given mask pattern. + // The function modules must be marked and the codeword bits must be drawn + // before masking. Due to the arithmetic of XOR, calling applyMask() with + // the same mask value a second time will undo the mask. A final well-formed + // QR Code needs exactly one (not zero, two, etc.) mask applied. + applyMask(mask) { + if (mask < 0 || mask > 7) { + throw new RangeError('Mask value out of range'); + } + for (let y = 0; y < this.size; y++) { + for (let x = 0; x < this.size; x++) { + let invert; + switch (mask) { + case 0: + invert = (x + y) % 2 === 0; + break; + case 1: + invert = y % 2 === 0; + break; + case 2: + invert = x % 3 === 0; + break; + case 3: + invert = (x + y) % 3 === 0; + break; + case 4: + invert = (Math.floor(x / 3) + Math.floor(y / 2)) % 2 === 0; + break; + case 5: + invert = ((x * y) % 2) + ((x * y) % 3) === 0; + break; + case 6: + invert = (((x * y) % 2) + ((x * y) % 3)) % 2 === 0; + break; + case 7: + invert = (((x + y) % 2) + ((x * y) % 3)) % 2 === 0; + break; + default: + throw new Error('Unreachable'); + } + if (!this.isFunction[y][x] && invert) { + this.modules[y][x] = !this.modules[y][x]; + } + } + } + } + // Calculates and returns the penalty score based on state of this QR Code's current modules. + // This is used by the automatic mask choice algorithm to find the mask pattern that yields the lowest score. + getPenaltyScore() { + let result = 0; + // Adjacent modules in row having same color, and finder-like patterns + for (let y = 0; y < this.size; y++) { + let runColor = false; + let runX = 0; + const runHistory = [0, 0, 0, 0, 0, 0, 0]; + for (let x = 0; x < this.size; x++) { + if (this.modules[y][x] === runColor) { + runX++; + if (runX === 5) { + result += QrCode.PENALTY_N1; + } + else if (runX > 5) { + result++; + } + } + else { + this.finderPenaltyAddHistory(runX, runHistory); + if (!runColor) { + result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; + } + runColor = this.modules[y][x]; + runX = 1; + } + } + result += this.finderPenaltyTerminateAndCount(runColor, runX, runHistory) * QrCode.PENALTY_N3; + } + // Adjacent modules in column having same color, and finder-like patterns + for (let x = 0; x < this.size; x++) { + let runColor = false; + let runY = 0; + const runHistory = [0, 0, 0, 0, 0, 0, 0]; + for (let y = 0; y < this.size; y++) { + if (this.modules[y][x] === runColor) { + runY++; + if (runY === 5) { + result += QrCode.PENALTY_N1; + } + else if (runY > 5) { + result++; + } + } + else { + this.finderPenaltyAddHistory(runY, runHistory); + if (!runColor) { + result += this.finderPenaltyCountPatterns(runHistory) * QrCode.PENALTY_N3; + } + runColor = this.modules[y][x]; + runY = 1; + } + } + result += this.finderPenaltyTerminateAndCount(runColor, runY, runHistory) * QrCode.PENALTY_N3; + } + // 2*2 blocks of modules having same color + for (let y = 0; y < this.size - 1; y++) { + for (let x = 0; x < this.size - 1; x++) { + const color = this.modules[y][x]; + if (color === this.modules[y][x + 1] && + color === this.modules[y + 1][x] && + color === this.modules[y + 1][x + 1]) { + result += QrCode.PENALTY_N2; + } + } + } + // Balance of dark and light modules + let dark = 0; + for (const row of this.modules) { + dark = row.reduce((sum, color) => sum + (color ? 1 : 0), dark); + } + const total = this.size * this.size; // Note that size is odd, so dark/total !== 1/2 + // Compute the smallest numbereger k >= 0 such that (45-5k)% <= dark/total <= (55+5k)% + const k = Math.ceil(Math.abs(dark * 20 - total * 10) / total) - 1; + assert(k >= 0 && k <= 9); + result += k * QrCode.PENALTY_N4; + assert(result >= 0 && result <= 2568888); // Non-tight upper bound based on default values of PENALTY_N1, ..., N4 + return result; + } + /* -- Private helper functions --*/ + // Returns an ascending list of positions of alignment patterns for this version number. + // Each position is in the range [0,177), and are used on both the x and y axes. + // This could be implemented as lookup table of 40 variable-length lists of numberegers. + getAlignmentPatternPositions() { + if (this.version === 1) { + return []; + } + const numAlign = Math.floor(this.version / 7) + 2; + const step = this.version === 32 ? 26 : Math.ceil((this.version * 4 + 4) / (numAlign * 2 - 2)) * 2; + const result = [6]; + for (let pos = this.size - 7; result.length < numAlign; pos -= step) { + result.splice(1, 0, pos); + } + return result; + } + // Returns the number of data bits that can be stored in a QR Code of the given version number, after + // all function modules are excluded. This includes remainder bits, so it might not be a multiple of 8. + // The result is in the range [208, 29648]. This could be implemented as a 40-entry lookup table. + static getNumRawDataModules(ver) { + if (ver < QrCode.MIN_VERSION || ver > QrCode.MAX_VERSION) { + throw new RangeError('Version number out of range'); + } + let result = (16 * ver + 128) * ver + 64; + if (ver >= 2) { + const numAlign = Math.floor(ver / 7) + 2; + result -= (25 * numAlign - 10) * numAlign - 55; + if (ver >= 7) { + result -= 36; + } + } + assert(result >= 208 && result <= 29648); + return result; + } + // Returns the number of 8-bit data (i.e. not error correction) codewords contained in any + // QR Code of the given version number and error correction level, with remainder bits discarded. + // This stateless pure function could be implemented as a (40*4)-cell lookup table. + static getNumDataCodewords(ver, ecl) { + return (Math.floor(QrCode.getNumRawDataModules(ver) / 8) - + QrCode.ECC_CODEWORDS_PER_BLOCK[ecl.ordinal][ver] * QrCode.NUM_ERROR_CORRECTION_BLOCKS[ecl.ordinal][ver]); + } + // Returns a Reed-Solomon ECC generator polynomial for the given degree. This could be + // implemented as a lookup table over all possible parameter values, instead of as an algorithm. + static reedSolomonComputeDivisor(degree) { + if (degree < 1 || degree > 255) { + throw new RangeError('Degree out of range'); + } + // Polynomial coefficients are stored from highest to lowest power, excluding the leading term which is always 1. + // For example the polynomial x^3 + 255x^2 + 8x + 93 is stored as the unumber8 array [255, 8, 93]. + const result = []; + for (let i = 0; i < degree - 1; i++) { + result.push(0); + } + result.push(1); // Start off with the monomial x^0 + // Compute the product polynomial (x - r^0) * (x - r^1) * (x - r^2) * ... * (x - r^{degree-1}), + // and drop the highest monomial term which is always 1x^degree. + // Note that r = 0x02, which is a generator element of this field GF(2^8/0x11D). + let root = 1; + for (let i = 0; i < degree; i++) { + // Multiply the current product by (x - r^i) + for (let j = 0; j < result.length; j++) { + result[j] = QrCode.reedSolomonMultiply(result[j], root); + if (j + 1 < result.length) { + result[j] ^= result[j + 1]; + } + } + root = QrCode.reedSolomonMultiply(root, 0x02); + } + return result; + } + // Returns the Reed-Solomon error correction codeword for the given data and divisor polynomials. + static reedSolomonComputeRemainder(data, divisor) { + const result = divisor.map(() => 0); + for (const b of data) { + // Polynomial division + const factor = b ^ result.shift(); + result.push(0); + divisor.forEach((coef, i) => { + result[i] ^= QrCode.reedSolomonMultiply(coef, factor); + }); + } + return result; + } + // Returns the product of the two given field elements modulo GF(2^8/0x11D). The arguments and result + // are unsigned 8-bit numberegers. This could be implemented as a lookup table of 256*256 entries of unumber8. + static reedSolomonMultiply(x, y) { + if (x >>> 8 !== 0 || y >>> 8 !== 0) { + throw new RangeError('Byte out of range'); + } + // Russian peasant multiplication + let z = 0; + for (let i = 7; i >= 0; i--) { + z = (z << 1) ^ ((z >>> 7) * 0x11d); + z ^= ((y >>> i) & 1) * x; + } + assert(z >>> 8 === 0); + return z; + } + // Can only be called immediately after a light run is added, and + // returns either 0, 1, or 2. A helper function for getPenaltyScore(). + finderPenaltyCountPatterns(runHistory) { + const n = runHistory[1]; + assert(n <= this.size * 3); + const core = n > 0 && runHistory[2] === n && runHistory[3] === n * 3 && runHistory[4] === n && runHistory[5] === n; + return ((core && runHistory[0] >= n * 4 && runHistory[6] >= n ? 1 : 0) + + (core && runHistory[6] >= n * 4 && runHistory[0] >= n ? 1 : 0)); + } + // Must be called at the end of a line (row or column) of modules. A helper function for getPenaltyScore(). + finderPenaltyTerminateAndCount(currentRunColor, oriCurrentRunLength, runHistory) { + let currentRunLength = oriCurrentRunLength; + if (currentRunColor) { + // Terminate dark run + this.finderPenaltyAddHistory(currentRunLength, runHistory); + currentRunLength = 0; + } + currentRunLength += this.size; // Add light border to final run + this.finderPenaltyAddHistory(currentRunLength, runHistory); + return this.finderPenaltyCountPatterns(runHistory); + } + // Pushes the given value to the front and drops the last value. A helper function for getPenaltyScore(). + finderPenaltyAddHistory(oriCurrentRunLength, runHistory) { + let currentRunLength = oriCurrentRunLength; + if (runHistory[0] === 0) { + currentRunLength += this.size; // Add light border to initial run + } + runHistory.pop(); + runHistory.unshift(currentRunLength); + } +} +/* -- Constants and tables --*/ +// The minimum version number supported in the QR Code Model 2 standard. +QrCode.MIN_VERSION = 1; +// The maximum version number supported in the QR Code Model 2 standard. +QrCode.MAX_VERSION = 40; +// For use in getPenaltyScore(), when evaluating which mask is best. +QrCode.PENALTY_N1 = 3; +QrCode.PENALTY_N2 = 3; +QrCode.PENALTY_N3 = 40; +QrCode.PENALTY_N4 = 10; +QrCode.ECC_CODEWORDS_PER_BLOCK = [ + // Version: (note that index 0 is for padding, and is set to an illegal value) + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + [ + -1, 7, 10, 15, 20, 26, 18, 20, 24, 30, 18, 20, 24, 26, 30, 22, 24, 28, 30, 28, 28, 28, 28, 30, 30, 26, 28, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + ], + [ + -1, 10, 16, 26, 18, 24, 16, 18, 22, 22, 26, 30, 22, 22, 24, 24, 28, 28, 26, 26, 26, 26, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + ], + [ + -1, 13, 22, 18, 26, 18, 24, 18, 22, 20, 24, 28, 26, 24, 20, 30, 24, 28, 28, 26, 30, 28, 30, 30, 30, 30, 28, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + ], + [ + -1, 17, 28, 22, 16, 22, 28, 26, 26, 24, 28, 24, 28, 22, 24, 24, 30, 28, 28, 26, 28, 30, 24, 30, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + ], // High +]; +QrCode.NUM_ERROR_CORRECTION_BLOCKS = [ + // Version: (note that index 0 is for padding, and is set to an illegal value) + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 Error correction level + [ + -1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 4, 4, 4, 4, 4, 6, 6, 6, 6, 7, 8, 8, 9, 9, 10, 12, 12, 12, 13, 14, 15, 16, 17, 18, + 19, 19, 20, 21, 22, 24, 25, + ], + [ + -1, 1, 1, 1, 2, 2, 4, 4, 4, 5, 5, 5, 8, 9, 9, 10, 10, 11, 13, 14, 16, 17, 17, 18, 20, 21, 23, 25, 26, 28, 29, 31, + 33, 35, 37, 38, 40, 43, 45, 47, 49, + ], + [ + -1, 1, 1, 2, 2, 4, 4, 6, 6, 8, 8, 8, 10, 12, 16, 12, 17, 16, 18, 21, 20, 23, 23, 25, 27, 29, 34, 34, 35, 38, 40, + 43, 45, 48, 51, 53, 56, 59, 62, 65, 68, + ], + [ + -1, 1, 1, 2, 4, 4, 4, 5, 6, 8, 8, 11, 11, 16, 16, 18, 16, 19, 21, 25, 25, 25, 34, 30, 32, 35, 37, 40, 42, 45, 48, + 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, + ], // High +]; + diff --git a/uni_modules/tdesign-uniapp/components/common/shared/qrcode/utils.js b/uni_modules/tdesign-uniapp/components/common/shared/qrcode/utils.js new file mode 100644 index 0000000..66f8ab1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/shared/qrcode/utils.js @@ -0,0 +1,124 @@ +import { Ecc } from './qrcodegen'; +// =================== ERROR_LEVEL ========================== +export const ERROR_LEVEL_MAP = { + L: Ecc.LOW, + M: Ecc.MEDIUM, + Q: Ecc.QUARTILE, + H: Ecc.HIGH, +}; +// =================== DEFAULT_VALUE ========================== +export const DEFAULT_SIZE = 160; +export const DEFAULT_LEVEL = 'M'; +export const DEFAULT_BACKGROUND_COLOR = '#FFFFFF'; +export const DEFAULT_FRONT_COLOR = '#000000'; +export const DEFAULT_NEED_MARGIN = false; +export const DEFAULT_MINVERSION = 1; +export const SPEC_MARGIN_SIZE = 4; +export const DEFAULT_MARGIN_SIZE = 0; +export const DEFAULT_IMG_SCALE = 0.1; +// =================== UTILS ========================== +/** + * Generate a path string from modules + * @param modules + * @param margin + * @returns + */ +export const generatePath = (modules, margin = 0) => { + const ops = []; + modules.forEach((row, y) => { + let start = null; + row.forEach((cell, x) => { + if (!cell && start !== null) { + ops.push(`M${start + margin} ${y + margin}h${x - start}v1H${start + margin}z`); + start = null; + return; + } + if (x === row.length - 1) { + if (!cell) { + return; + } + if (start === null) { + ops.push(`M${x + margin},${y + margin} h1v1H${x + margin}z`); + } else { + ops.push(`M${start + margin},${y + margin} h${x + 1 - start}v1H${start + margin}z`); + } + return; + } + if (cell && start === null) { + start = x; + } + }); + }); + return ops.join(''); +}; +/** + * Excavate modules + * @param modules + * @param excavation + * @returns + */ +export const excavateModules = (modules, excavation) => modules.slice().map((row, y) => { + if (y < excavation.y || y >= excavation.y + excavation.h) { + return row; + } + return row.map((cell, x) => { + if (x < excavation.x || x >= excavation.x + excavation.w) { + return cell; + } + return false; + }); +}); +/** + * Get image settings + * @param cells The modules of the QR code + * @param size The size of the QR code + * @param margin + * @param imageSettings + * @returns + */ +export const getImageSettings = (cells, size, margin, imageSettings) => { + if (imageSettings == null) { + return null; + } + const numCells = cells.length + margin * 2; + const defaultSize = Math.floor(size * DEFAULT_IMG_SCALE); + const scale = numCells / size; + const w = (imageSettings.width || defaultSize) * scale; + const h = (imageSettings.height || defaultSize) * scale; + const x = imageSettings.x == null ? cells.length / 2 - w / 2 : imageSettings.x * scale; + const y = imageSettings.y == null ? cells.length / 2 - h / 2 : imageSettings.y * scale; + const opacity = imageSettings.opacity == null ? 1 : imageSettings.opacity; + let excavation = null; + if (imageSettings.excavate) { + const floorX = Math.floor(x); + const floorY = Math.floor(y); + const ceilW = Math.ceil(w + x - floorX); + const ceilH = Math.ceil(h + y - floorY); + excavation = { x: floorX, y: floorY, w: ceilW, h: ceilH }; + } + const { crossOrigin } = imageSettings; + return { x, y, h, w, excavation, opacity, crossOrigin }; +}; +/** + * Get margin size + * @param needMargin Whether need margin + * @param marginSize Custom margin size + * @returns + */ +export const getMarginSize = (needMargin, marginSize) => { + if (marginSize != null) { + return Math.max(Math.floor(marginSize), 0); + } + return needMargin ? SPEC_MARGIN_SIZE : DEFAULT_MARGIN_SIZE; +}; +/** + * Check if Path2D is supported + */ +export const isSupportPath2d = (() => { + try { + new Path2D().addPath(new Path2D()); + } catch (_a) { + return false; + } + return true; +})(); diff --git a/uni_modules/tdesign-uniapp/components/common/src/control.js b/uni_modules/tdesign-uniapp/components/common/src/control.js new file mode 100644 index 0000000..777c09f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/src/control.js @@ -0,0 +1,66 @@ +const defaultOption = { + valueKey: 'value', + defaultValueKey: 'defaultValue', + changeEventName: 'change', + strict: true, +}; +/** + * 受控函数 + * 用法示例: + * { + * attached() { + * this.control = useControl.call(this); + * } + * } + * 注意事项: + * 1:命名规范:约束value等命名,一般不需要改。内部属性统一命名以_开头。 + * 2:value默认值:小程序number类型未传值(undefined)会初始化为0,导致无法判断。建议默认值设置为null + * 3:prop变化:需要开发者自己监听,observers = { value(val):{ this.control.set(val) } } + * @param this 页面实例 + * @param option 配置项 参见ControlOption + * @returns + */ +function useControl(option) { + const { valueKey, defaultValueKey, changeEventName, strict } = { + ...defaultOption, + ...option, + }; + const props = this.properties || {}; + const value = props[valueKey]; + // 半受控时,不需要defaultValueKey,默认值与valueKey相同 + const defaultValue = props[strict ? defaultValueKey : valueKey]; + let controlled = false; + // 完全受控模式:检查受控属性,判断是否受控 + if (strict && typeof value !== 'undefined' && value !== null) { + controlled = true; + } + const set = (newVal, extObj, fn) => { + this.setData( + { + [`_${valueKey}`]: newVal, + ...extObj, + }, + fn, + ); + }; + return { + controlled, + initValue: controlled ? value : defaultValue, + set, + get: () => this.data[`_${valueKey}`], + change: (newVal, customChangeData, customUpdateFn) => { + this.$emit(changeEventName, typeof customChangeData !== 'undefined' ? customChangeData : newVal); + // 完全受控模式,使用了受控属性,必须配合change事件来更新 + if (controlled) { + return; + } + if (typeof customUpdateFn === 'function') { + customUpdateFn(); + } else { + set(newVal); + } + }, + }; +} + +export { useControl }; diff --git a/uni_modules/tdesign-uniapp/components/common/src/flatTool.js b/uni_modules/tdesign-uniapp/components/common/src/flatTool.js new file mode 100644 index 0000000..7828119 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/src/flatTool.js @@ -0,0 +1,99 @@ +import { isObject } from '../validator'; + +/** **************************************************************** + MIT License https://github.com/qiu8310/minapp/blob/v1.0.0-alpha.1/packages/minapp-core/src/system/util/object.ts + Author Mora (https://github.com/qiu8310) +****************************************************************** */ + +export const getPrototypeOf = function (obj) { + return Object.getPrototypeOf ? Object.getPrototypeOf(obj) : obj.__proto__; +}; + +/** + * 遍历继承关系类的 prototype + * + * @export + * @param {Function} callback - 回调函数,函数参数是遍历的每个实例的 prototype,函数如果返回 false,会终止遍历 + * @param {any} fromCtor - 要遍历的起始 class 或 prototype + * @param {any} toCtor - 要遍历的结束 class 或 prototype + * @param {boolean} [includeToCtor=true] - 是否要包含结束 toCtor 本身 + * + * @example + * A -> B -> C + * + * 在 C 中调用: iterateInheritedPrototype(fn, A, C, true) + * 则,fn 会被调用三次,分别是 fn(A.prototype) fn(B.prototype) fn(C.prototype) + */ +export const iterateInheritedPrototype = function iterateInheritedPrototype( + callback, + fromCtor, + toCtor, + includeToCtor = true, +) { + let proto = fromCtor.prototype || fromCtor; + const toProto = toCtor.prototype || toCtor; + + while (proto) { + if (!includeToCtor && proto === toProto) break; + if (callback(proto) === false) break; + if (proto === toProto) break; + proto = getPrototypeOf(proto); + } +}; + + +/** + * + * 将一个可能包含原型链的对象扁平化成单个对象 + * + * 如,现有这样的类的继承关系 A -> B -> C,当创建一个实例 a = new A() 时 + * + * a 实例会存有 B、C 的原型链,使用此函数 newa = toObject(a) 之后, + * newa 就会变成一个 PlainObject,但它有 A、B、C 上的所有属性和方法, + * 当然不包括静态属性或方法 + * + * 注意1:用此方法的话,尽量避免在类中使用胖函数,胖函数的 this 死死的绑定 + * 在原对象中,无法重新绑定 + * + * 注意2:类继承的时候不要在函数中调用 super,toObject 之后是扁平的,没有 super 之说 + */ +export const toObject = function toObject( + something, + options = {}, +) { + const obj = {}; + if (!isObject(something)) return obj; + + const excludes = options.excludes || ['constructor']; + const { enumerable = true, configurable = 0, writable = 0 } = options; + const defaultDesc = {}; + if (enumerable !== 0) defaultDesc.enumerable = enumerable; + if (configurable !== 0) defaultDesc.configurable = configurable; + if (writable !== 0) defaultDesc.writable = writable; + + iterateInheritedPrototype( + (proto) => { + Object.getOwnPropertyNames(proto).forEach((key) => { + if (excludes.indexOf(key) >= 0) return; + if (Object.prototype.hasOwnProperty.call(obj, key)) return; + const desc = Object.getOwnPropertyDescriptor(proto, key); + + const fnKeys = ['get', 'set', 'value']; + fnKeys.forEach((k) => { + if (typeof desc[k] === 'function') { + const oldFn = desc[k]; + desc[k] = function (...args) { + return oldFn.apply(Object.prototype.hasOwnProperty.call(options, 'bindTo') ? options.bindTo : this, args); + }; + } + }); + Object.defineProperty(obj, key, { ...desc, ...defaultDesc }); + }); + }, + something, + options.till || Object, + false, + ); + + return obj; +}; diff --git a/uni_modules/tdesign-uniapp/components/common/src/index.js b/uni_modules/tdesign-uniapp/components/common/src/index.js new file mode 100644 index 0000000..94e7ce1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/src/index.js @@ -0,0 +1,4 @@ +export * from './superComponent'; +export * from './flatTool'; +export * from './instantiationDecorator'; +export * from './control'; diff --git a/uni_modules/tdesign-uniapp/components/common/src/instantiationDecorator.js b/uni_modules/tdesign-uniapp/components/common/src/instantiationDecorator.js new file mode 100644 index 0000000..c099aec --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/src/instantiationDecorator.js @@ -0,0 +1,251 @@ +/* eslint-disable no-param-reassign */ +import { isPlainObject } from '../validator'; +import { canUseVirtualHost } from '../version'; +import { toCamel, toPascal, hyphenate } from '../utils'; + +const getInnerControlledValue = key => `data${key.replace(/^(\w)/, (e, t) => t.toUpperCase())}`; +const getDefaultKey = key => `default${key.replace(/^(\w)/, (e, t) => t.toUpperCase())}`; + +const ARIAL_PROPS = [ + { key: 'ariaHidden', type: Boolean }, + { key: 'ariaRole', type: String }, + { key: 'ariaLabel', type: String }, + { key: 'ariaLabelledby', type: String }, + { key: 'ariaDescribedby', type: String }, + { key: 'ariaBusy', type: Boolean }, +]; + +const getPropsDefault = (type, disableBoolean = false) => { + if (type === Boolean && !disableBoolean) { + return false; + } + if (type === String) { + return ''; + } + return undefined; +}; + +const COMMON_PROPS = { + ...ARIAL_PROPS.reduce((acc, item) => ({ + ...acc, + [item.key]: { + type: item.type, + default: getPropsDefault(item.type), + }, + }), {}), + + customStyle: { type: [String, Object], default: '' }, +}; + + +export const toComponent = function (options) { + if (!options.properties && options.props) { + options.properties = options.props; + } + + if (options.properties) { + Object.keys(options.properties).forEach((k) => { + let opt = options.properties[k]; + // 如果不是 Object 类型,则默认指定 type = options.properties[k]; + if (!isPlainObject(opt)) { + opt = { type: opt }; + } + options.properties[k] = opt; + }); + } + + if (!options.methods) options.methods = {}; + + if (!options.lifetimes) options.lifetimes = {}; + + const oldCreated = options.created; + const { controlledProps = [] } = options; + + options.created = function (...args) { + if (oldCreated) { + oldCreated.apply(this, args); + } + controlledProps.forEach(({ key }) => { + const defaultKey = getDefaultKey(key); + const tDataKey = getInnerControlledValue(key); + this[tDataKey] = this[key]; + + if (this[key] == null) { + this._selfControlled = true; + } + + if (this[key] == null && this[defaultKey] != null) { + this[tDataKey] = this[defaultKey]; + } + }); + }; + + options.methods._trigger = function (evtName, detail, opts) { + const target = controlledProps.find(item => item.event === evtName); + + if (target) { + const { key } = target; + if (this._selfControlled) { + const tDataKey = getInnerControlledValue(key); + this[tDataKey] = detail[key]; + } + this.$emit( + `update:${key}`, + detail[key], + opts, + ); + } + + this.$emit( + evtName, + detail, + opts, + ); + }; + return options; +}; + + +/** + * 将一个继承了 BaseComponent 的类转化成 小程序 Component 的调用 + * 根据最新的微信 d.ts 描述文件,Component 在实例化的时候,会忽略不支持的自定义属性 + */ +export const wxComponent = function wxComponent() { + return function (baseConstructor) { + class WxComponent extends baseConstructor { + // 暂时移除了冗余的代码,后续补充 + } + + const current = new WxComponent(); + + current.options = current.options || {}; + + // 所有组件默认都开启css作用域 + // 写到这里是为了防止组件设置 options 时无意覆盖掉了 addGlobalClass + // 使用 "styleIsolation": "apply-shared" 代替 addGlobalClass: true,see https://developers.weixin.qq.com/miniprogram/dev/framework/custom-component/glass-easel/migration.html#JSON-%E9%85%8D%E7%BD%AE + // if (current.options.addGlobalClass === undefined) { + // current.options.addGlobalClass = true; + // } + + if (canUseVirtualHost() && current.options.virtualHost == null) { + current.options.virtualHost = true; + } + + const obj = toComponent(current); + + return obj; + }; +}; + +function sortPropsType(type) { + if (!Array.isArray(type)) { + return type; + } + type.sort((a, b) => { + if (a === Boolean) { + return -1; + } + if (b === Boolean) { + return 1; + } + return 0; + }); + return type; +} + +function filterProps(props, controlledProps) { + const newProps = {}; + const emits = []; + const reg = /^on[A-Z][a-z]/; + const controlledKeys = Object.values(controlledProps).map(item => item.key); + const unControlledKeys = controlledKeys.map(key => getDefaultKey(key)); + + Object.keys(props).forEach((key) => { + const curType = props[key].type || props[key]; + + if (reg.test(key) && props[key].type === Function) { + const str = key.replace(/^on/, ''); + const eventName = str.charAt(0).toLowerCase() + str.slice(1); + emits.push(...[hyphenate(eventName), eventName]); + } else if (controlledKeys.indexOf(key) > -1 + || unControlledKeys.indexOf(key) > -1 + ) { + const newType = Array.isArray(curType) ? curType : [curType]; + newProps[key] = { + type: [null, ...newType], + default: null, + }; + } else if ( + [Boolean, String].indexOf(props[key].type) > -1 + && props[key].default === undefined + ) { + newProps[key] = { + ...props[key], + default: getPropsDefault(props[key].type, true), + }; + } else { + newProps[key] = { + ...(typeof props[key] === 'object' ? props[key] : {}), + type: sortPropsType(curType), + }; + } + }); + + return { + newProps, + emits, + }; +} + +const getEmitsByControlledProps = controlledProps => Object.values(controlledProps).map(item => `update:${item.key}`); + +export const uniComponent = function (info) { + const { newProps, emits } = filterProps(info.props || {}, info.controlledProps || {}); + info.props = { + ...getExternalClasses(info), + ...newProps, + ...COMMON_PROPS, + }; + info.emits = Array.from(new Set([ + ...(info.emits || []), + + ...(getEmitsByControlledProps(info.controlledProps || {})), + ...emits, + ])); + + info.options = { + ...(info.options || {}), + multipleSlots: true, + }; + + if (canUseVirtualHost() && info.options.virtualHost == null) { + info.options.virtualHost = true; + } + + if (!info.options.styleIsolation) { + info.options.styleIsolation = 'shared'; + } + if (info.name) { + info.name = toPascal(info.name); + } + + const obj = toComponent(info); + return obj; +}; + + +export function getExternalClasses(info) { + if (!info.externalClasses) { + return {}; + } + const { externalClasses } = info; + const list = Array.isArray(externalClasses) ? externalClasses : [externalClasses]; + + return list.reduce((acc, item) => ({ + ...acc, + [toCamel(item)]: { + type: String, + default: '', + }, + }), {}); +} diff --git a/uni_modules/tdesign-uniapp/components/common/src/superComponent.js b/uni_modules/tdesign-uniapp/components/common/src/superComponent.js new file mode 100644 index 0000000..3f85068 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/src/superComponent.js @@ -0,0 +1,5 @@ +export class SuperComponent { + constructor() { + this.app = getApp(); + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/_variables.less b/uni_modules/tdesign-uniapp/components/common/style/_variables.less new file mode 100644 index 0000000..9bdf019 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/_variables.less @@ -0,0 +1,232 @@ +// 公共前缀 +@prefix: t; + +@primary-color-1: var(--td-primary-color-1, #f2f3ff); +@primary-color-2: var(--td-primary-color-2, #d9e1ff); +@primary-color-3: var(--td-primary-color-3, #b5c7ff); +@primary-color-4: var(--td-primary-color-4, #8eabff); +@primary-color-5: var(--td-primary-color-5, #618dff); +@primary-color-6: var(--td-primary-color-6, #366ef4); +@primary-color-7: var(--td-primary-color-7, #0052d9); +@primary-color-8: var(--td-primary-color-8, #003cab); +@primary-color-9: var(--td-primary-color-9, #002a7c); +@primary-color-10: var(--td-primary-color-10, #001a57); + +@error-color-1: var(--td-error-color-1, #fff0ed); +@error-color-2: var(--td-error-color-2, #ffd8d2); +@error-color-3: var(--td-error-color-3, #ffb9b0); +@error-color-4: var(--td-error-color-4, #ff9285); +@error-color-5: var(--td-error-color-5, #f6685d); +@error-color-6: var(--td-error-color-6, #d54941); +@error-color-7: var(--td-error-color-7, #ad352f); +@error-color-8: var(--td-error-color-8, #881f1c); +@error-color-9: var(--td-error-color-9, #68070a); +@error-color-10: var(--td-error-color-10, #490002); + +@warning-color-1: var(--td-warning-color-1, #fff1e9); +@warning-color-2: var(--td-warning-color-2, #ffd9c2); +@warning-color-3: var(--td-warning-color-3, #ffb98c); +@warning-color-4: var(--td-warning-color-4, #fa9550); +@warning-color-5: var(--td-warning-color-5, #e37318); +@warning-color-6: var(--td-warning-color-6, #be5a00); +@warning-color-7: var(--td-warning-color-7, #954500); +@warning-color-8: var(--td-warning-color-8, #713300); +@warning-color-9: var(--td-warning-color-9, #532300); +@warning-color-10: var(--td-warning-color-10, #3b1700); + +@success-color-1: var(--td-success-color-1, #e3f9e9); +@success-color-2: var(--td-success-color-2, #c6f3d7); +@success-color-3: var(--td-success-color-3, #92dab2); +@success-color-4: var(--td-success-color-4, #56c08d); +@success-color-5: var(--td-success-color-5, #2ba471); +@success-color-6: var(--td-success-color-6, #008858); +@success-color-7: var(--td-success-color-7, #006c45); +@success-color-8: var(--td-success-color-8, #005334); +@success-color-9: var(--td-success-color-9, #003b23); +@success-color-10: var(--td-success-color-10, #002515); + +@gray-color-1: var(--td-gray-color-1, #f3f3f3); +@gray-color-2: var(--td-gray-color-2, #eeeeee); +@gray-color-3: var(--td-gray-color-3, #e7e7e7); +@gray-color-4: var(--td-gray-color-4, #dcdcdc); +@gray-color-5: var(--td-gray-color-5, #c5c5c5); +@gray-color-6: var(--td-gray-color-6, #a6a6a6); +@gray-color-7: var(--td-gray-color-7, #8b8b8b); +@gray-color-8: var(--td-gray-color-8, #777777); +@gray-color-9: var(--td-gray-color-9, #5e5e5e); +@gray-color-10: var(--td-gray-color-10, #4b4b4b); +@gray-color-11: var(--td-gray-color-11, #383838); +@gray-color-12: var(--td-gray-color-12, #2c2c2c); +@gray-color-13: var(--td-gray-color-13, #242424); +@gray-color-14: var(--td-gray-color-14, #181818); + +@font-gray-1: var(--td-font-gray-1, rgba(0, 0, 0, 0.9)); +@font-gray-2: var(--td-font-gray-2, rgba(0, 0, 0, 0.6)); +@font-gray-3: var(--td-font-gray-3, rgba(0, 0, 0, 0.4)); +@font-gray-4: var(--td-font-gray-4, rgba(0, 0, 0, 0.26)); + +@font-white-1: var(--td-font-white-1, rgba(255, 255, 255, 1)); +@font-white-2: var(--td-font-white-2, rgba(255, 255, 255, 0.55)); +@font-white-3: var(--td-font-white-3, rgba(255, 255, 255, 0.35)); +@font-white-4: var(--td-font-white-4, rgba(255, 255, 255, 0.22)); + +// 边框色 +@border-color: var(--td-border-color, @gray-color-3); + +// Spacer +@spacer: var(--td-spacer, 16rpx); +@spacer-1: var(--td-spacer-1, 24rpx); // 间距-小-x +@spacer-2: var(--td-spacer-2, 32rpx); // 间距-小 +@spacer-3: var(--td-spacer-3, 48rpx); // 间距-中 +@spacer-4: var(--td-spacer-4, 64rpx); // 间距-大 +@spacer-5: var(--td-spacer-5, 96rpx); // 间距-大-x +@spacer-6: var(--td-spacer-6, 160rpx); // 间距-大-xx + +@text-line-height: var(--td-line-height-body-extraSmall, 16px); +@text-line-height-xs: var(--td-line-height-body-extraSmall, @text-line-height); +@text-line-height-s: var(--td-line-height-body-small, 20px); +@text-line-height-base: var(--td-line-height-title-small, 22px); +@text-line-height-m: var(--td-line-height-title-medium, 24px); +@text-line-height-l: var(--td-line-height-title-large, 26px); +@text-line-height-xl: var(--td-line-height-title-extraLarge, 28px); +@text-line-height-xxl: var(--td-line-height-headline-large, 44px); + +// Font +@font-size: var(--td-font-size, 20rpx); +@font-size-xs: var(--td-font-size-xs, @font-size); // 字号-一级字号 +@font-size-s: var(--td-font-size-s, 24rpx); // 字号-二级字号 +@font-size-base: var(--td-font-size-base, 28rpx); // 字号-三级字号 +@font-size-m: var(--td-font-size-m, 32rpx); // 字号-二级字号 +@font-size-l: var(--td-font-size-l, 36rpx); // 字号-四级字号 +@font-size-xl: var(--td-font-size-xl, 40rpx); // 字号-五级字号 +@font-size-xxl: var(--td-font-size-xxl, 72rpx); + +@font-family: var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular); // 字体-磅数-常规 +@font-family-medium: var(--td-font-family-medium, PingFang SC, Microsoft YaHei, Arial Medium); // 字体-磅数-粗体 + +// 使用以下与Design Token对应的font token开发组件对应字体、行高 +@font-link-small: var(--td-font-link-small, 24rpx / 40rpx @font-family); +@font-link-medium: var(--td-font-link-medium, 28rpx / 44rpx @font-family); +@font-link-large: var(--td-font-link-large, 32rpx / 48rpx @font-family); +@font-mark-extraSmall: var(--td-font-mark-extraSmall, 600 20rpx / 32rpx @font-family); +@font-mark-small: var(--td-font-mark-small, 600 24rpx / 40rpx @font-family); +@font-mark-medium: var(--td-font-mark-medium, 600 28rpx / 44rpx @font-family); +@font-mark-large: var(--td-font-mark-large, 600 32rpx / 48rpx @font-family); +@font-body-extraSmall: var(--td-font-body-extraSmall, 20rpx / 32rpx @font-family); +@font-body-small: var(--td-font-body-small, 24rpx / 40rpx @font-family); +@font-body-medium: var(--td-font-body-medium, 28rpx / 44rpx @font-family); +@font-body-large: var(--td-font-body-large, 32rpx / 48rpx @font-family); +@font-title-small: var(--td-font-title-small, 600 28rpx / 44rpx @font-family); +@font-title-medium: var(--td-font-title-medium, 600 32rpx / 48rpx @font-family); +@font-title-large: var(--td-font-title-large, 600 36rpx / 52rpx @font-family); +@font-title-extraLarge: var(--td-font-title-extraLarge, 600 40rpx / 56rpx @font-family); +@font-headline-small: var(--td-font-headline-small, 600 48rpx / 64rpx @font-family); +@font-headline-medium: var(--td-font-headline-medium, 600 56rpx / 72rpx @font-family); +@font-headline-large: var(--td-font-headline-large, 600 72rpx / 88rpx @font-family); +@font-display-medium: var(--td-font-display-medium, 600 96rpx / 112rpx @font-family); +@font-display-large: var(--td-font-display-large, 600 128rpx / 144rpx @font-family); + +// 圆角 +@radius-small: var(--td-radius-small, 6rpx); +@radius-default: var(--td-radius-default, 12rpx); +@radius-large: var(--td-radius-large, 18rpx); +@radius-extraLarge: var(--td-radius-extraLarge, 24rpx); +@radius-round: var(--td-radius-round, 999px); +@radius-circle: var(--td-radius-circle, 50%); + +// 阴影 +@shadow-1: var( + --td-shadow-1, + 0 1px 10px rgba(0, 0, 0, 5%), + 0 4px 5px rgba(0, 0, 0, 8%), + 0 2px 4px -1px rgba(0, 0, 0, 12%) +); +@shadow-2: var( + --td-shadow-2, + 0 3px 14px 2px rgba(0, 0, 0, 5%), + 0 8px 10px 1px rgba(0, 0, 0, 6%), + 0 5px 5px -3px rgba(0, 0, 0, 10%) +); +@shadow-3: var( + --td-shadow-3, + 0 6px 30px 5px rgba(0, 0, 0, 5%), + 0 16px 24px 2px rgba(0, 0, 0, 4%), + 0 8px 10px -5px rgba(0, 0, 0, 8%) +); +@shadow-4: var(--td-shadow-4, 0 2px 8px 0 rgba(0, 0, 0, 0.06)); + +// 动画 +@anim-time-fn-easing: var(--td-anim-time-fn-easing, cubic-bezier(0.38, 0, 0.24, 1)); +@anim-time-fn-ease-out: var(--td-anim-time-fn-ease-out, cubic-bezier(0, 0, 0.15, 1)); +@anim-time-fn-ease-in: var(--td-anim-time-fn-ease-in, cubic-bezier(0.82, 0, 1, 0.9)); +@anim-duration-base: var(--td-anim-duration-base, 0.2s); +@anim-duration-moderate: var(--td-anim-duration-moderate, 0.24s); +@anim-duration-slow: var(--td-anim-duration-slow, 0.28s); + +// 容器 +@bg-color-page: var(--td-bg-color-page, @gray-color-1); +@bg-color-container: var(--td-bg-color-container, @font-white-1); +@bg-color-container-active: var(--td-bg-color-container-active, @gray-color-3); + +@bg-color-secondarycontainer: var(--td-bg-color-secondarycontainer, @gray-color-1); +@bg-color-secondarycontainer-active: var(--td-bg-color-secondarycontainer-active, @gray-color-4); + +@bg-color-component: var(--td-bg-color-component, @gray-color-3); +@bg-color-component-active: var(--td-bg-color-component-active, @gray-color-6); +@bg-color-component-disabled: var(--td-bg-color-component-disabled, @gray-color-2); + +@bg-color-secondarycomponent: var(--td-bg-color-secondarycomponent, @gray-color-4); +@bg-color-secondarycomponent-active: var(--td-bg-color-secondarycomponent-active, @gray-color-6); + +// 特殊组件背景色,目前只用于 button、input 组件多主题场景,浅色主题下固定为白色,深色主题下为 transparent 适配背景颜色 +@bg-color-specialcomponent: var(--td-bg-color-specialcomponent, #fff); + +@component-stroke: var(--td-component-stroke, @gray-color-3); +@border-level-1-color: var(--td-border-level-1-color, @gray-color-3); +@component-border: var(--td-component-border, @gray-color-4); +@border-level-2-color: var(--td-border-level-2-color, @gray-color-4); + +// 主题 +@brand-color: var(--td-brand-color, @primary-color-7); +@brand-color-active: var(--td-brand-color-active, @primary-color-8); +@brand-color-disabled: var(--td-brand-color-disabled, @primary-color-3); +@brand-color-focus: var(--td-brand-color-focus, @primary-color-1); +@brand-color-light: var(--td-brand-color-light, @primary-color-1); +@brand-color-light-active: var(--td-brand-color-light-active, @primary-color-2); + +@error-color: var(--td-error-color, @error-color-6); +@error-color-active: var(--td-error-color-active, @error-color-7); +@error-color-disabled: var(--td-error-color-disabled, @error-color-3); +@error-color-focus: var(--td-error-color-focus, @error-color-2); +@error-color-light: var(--td-error-color-light, @error-color-1); + +@warning-color: var(--td-warning-color, @warning-color-5); +@warning-color-active: var(--td-warning-color-active, @warning-color-6); +@warning-color-disabled: var(--td-warning-color-disabled, @warning-color-3); +@warning-color-focus: var(--td-warning-color-focus, @warning-color-2); +@warning-color-light: var(--td-warning-color-light, @warning-color-1); + +@success-color: var(--td-success-color, @success-color-5); +@success-color-active: var(--td-success-color-active, @success-color-6); +@success-color-disabled: var(--td-success-color-disabled, @success-color-3); +@success-color-focus: var(--td-success-color-focus, @success-color-2); +@success-color-light: var(--td-success-color-light, @success-color-1); + +// 文字 +@text-color-primary: var(--td-text-color-primary, @font-gray-1); +@text-color-secondary: var(--td-text-color-secondary, @font-gray-2); +@text-color-placeholder: var(--td-text-color-placeholder, @font-gray-3); +@text-color-disabled: var(--td-text-color-disabled, @font-gray-4); +@text-color-anti: var(--td-text-color-anti, @font-white-1); +@text-color-brand: var(--td-text-color-brand, @brand-color); +@text-color-link: var(--td-text-color-link, @brand-color); + +// 定位 +@position-fixed-top: var(--td-position-fixed-top, 0); + +// 遮罩 +@mask-active: var(--td-mask-active, rgba(0, 0, 0, 0.6)); // 遮罩-弹出 +@mask-disabled: var(--td-mask-disabled, rgba(255, 255, 255, 0.6)); // 遮罩-禁用 +@mask-bg: var(--td-mask-background, rgba(255, 255, 255, 0.96)); // 二维码遮罩 + +@text-line-height: 1.5; diff --git a/uni_modules/tdesign-uniapp/components/common/style/base.less b/uni_modules/tdesign-uniapp/components/common/style/base.less new file mode 100644 index 0000000..4b8938f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/base.less @@ -0,0 +1,4 @@ +// 变量 +@import './_variables.less'; + +@import './mixins/_index.less'; diff --git a/uni_modules/tdesign-uniapp/components/common/style/icons.css b/uni_modules/tdesign-uniapp/components/common/style/icons.css new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/tdesign-uniapp/components/common/style/index.css b/uni_modules/tdesign-uniapp/components/common/style/index.css new file mode 100644 index 0000000..5eb332d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/index.css @@ -0,0 +1,13 @@ +.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); +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_border.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_border.less new file mode 100644 index 0000000..d765a92 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_border.less @@ -0,0 +1,34 @@ +@import '../_variables.less'; + +.border(@position: bottom, @border-color: @component-border) { + position: relative; + + &::after { + content: ''; + display: block; + position: absolute; + top: if(@position = top, 0, unset); + bottom: if(@position = bottom, 0, unset); + left: if(@position = left, 0, unset); + right: if(@position = right, 0, unset); + background-color: @border-color; + } +} + +.border(@position: bottom, @border-color: @gray-color-1) when(@position = bottom) , (@position = top) { + &::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); + } +} + +.border(@position: bottom, @border-color: @gray-color-1) when(@position = left),(@position = right) { + &::after { + width: 1px; + top: 0; + bottom: 0; + transform: scaleX(0.5); + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_clearfix.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_clearfix.less new file mode 100644 index 0000000..c9f0cb1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_clearfix.less @@ -0,0 +1,7 @@ +.clearfix() { + &::after { + display: table; + clear: both; + content: ''; + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_cursor.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_cursor.less new file mode 100644 index 0000000..c1e5882 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_cursor.less @@ -0,0 +1,6 @@ +.cursor-pointer() { + cursor: pointer; + -webkit-tap-highlight-color: transparent; + -webkit-user-select: none; + user-select: none; +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_ellipsis.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_ellipsis.less new file mode 100644 index 0000000..6501e6e --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_ellipsis.less @@ -0,0 +1,15 @@ +.ellipsis(@w:auto) { + width: @w; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + word-wrap: normal; +} + +.ellipsisLn(@line) { + overflow: hidden; + text-overflow: ellipsis; + -webkit-line-clamp: @line; + display: -webkit-box; + -webkit-box-orient: vertical; +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_hairline.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_hairline.less new file mode 100644 index 0000000..41ca8db --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_hairline.less @@ -0,0 +1,55 @@ +@import '../_variables.less'; + +.hairline-base() { + position: absolute; + box-sizing: border-box; + content: ' '; + pointer-events: none; +} + +.hairline(@color: @border-level-1-color) { + .hairline-base(); + top: -50%; + right: -50%; + bottom: -50%; + left: -50%; + border: 1px solid @color; + transform: scale(0.5); +} + +.hairline-top(@color: @border-level-1-color) { + .hairline-base(); + right: 0; + left: 0; + top: 0; + border-top: 1px solid @color; + transform: scaleY(0.5); + transform-origin: 0 0; +} + +.hairline-bottom(@color: @border-level-1-color, @width: 1px) { + .hairline-base(); + right: 0; + left: 0; + bottom: 0; + border-bottom: @width solid @color; + transform: scaleY(0.5); +} + +.hairline-left(@color: @border-level-1-color) { + .hairline-base(); + top: 0; + bottom: 0; + left: 0; + border-left: 1px solid @color; + transform: scaleX(0.5); +} + +.hairline-right(@color: @border-level-1-color) { + .hairline-base(); + top: 0; + bottom: 0; + right: 0; + border-right: 1px solid @color; + transform: scaleX(0.5); +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_index.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_index.less new file mode 100644 index 0000000..b1d4c49 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_index.less @@ -0,0 +1,6 @@ +@import './_clearfix.less'; +@import './_hairline.less'; +@import './_ellipsis.less'; +@import './_cursor.less'; +@import './_border.less'; +@import './_other.less'; diff --git a/uni_modules/tdesign-uniapp/components/common/style/mixins/_other.less b/uni_modules/tdesign-uniapp/components/common/style/mixins/_other.less new file mode 100644 index 0000000..05a5b7b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/mixins/_other.less @@ -0,0 +1,14 @@ +// 屏幕中不显示, 但可被读屏 +.sr-only() { + &--sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + clip-path: inset(50%); + border: 0; + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/index.css b/uni_modules/tdesign-uniapp/components/common/style/theme/index.css new file mode 100644 index 0000000..05530bf --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/index.css @@ -0,0 +1,519 @@ + +/* ./raw/_light.less */ +@media (prefers-color-scheme: light) { + /* #ifdef H5 */ + :root, + /* #endif */ + page, + .page { + --td-brand-color-1: #f2f3ff; + --td-brand-color-2: #d9e1ff; + --td-brand-color-3: #b5c7ff; + --td-brand-color-4: #8eabff; + --td-brand-color-5: #618dff; + --td-brand-color-6: #366ef4; + --td-brand-color-7: #0052d9; + --td-brand-color-8: #003cab; + --td-brand-color-9: #002a7c; + --td-brand-color-10: #001a57; + + --td-primary-color-1: var(--td-brand-color-1); + --td-primary-color-2: var(--td-brand-color-2); + --td-primary-color-3: var(--td-brand-color-3); + --td-primary-color-4: var(--td-brand-color-4); + --td-primary-color-5: var(--td-brand-color-5); + --td-primary-color-6: var(--td-brand-color-6); + --td-primary-color-7: var(--td-brand-color-7); + --td-primary-color-8: var(--td-brand-color-8); + --td-primary-color-9: var(--td-brand-color-9); + --td-primary-color-10: var(--td-brand-color-10); + + --td-warning-color-1: #fff1e9; + --td-warning-color-2: #ffd9c2; + --td-warning-color-3: #ffb98c; + --td-warning-color-4: #fa9550; + --td-warning-color-5: #e37318; + --td-warning-color-6: #be5a00; + --td-warning-color-7: #954500; + --td-warning-color-8: #713300; + --td-warning-color-9: #532300; + --td-warning-color-10: #3b1700; + + --td-error-color-1: #fff0ed; + --td-error-color-2: #ffd8d2; + --td-error-color-3: #ffb9b0; + --td-error-color-4: #ff9285; + --td-error-color-5: #f6685d; + --td-error-color-6: #d54941; + --td-error-color-7: #ad352f; + --td-error-color-8: #881f1c; + --td-error-color-9: #68070a; + --td-error-color-10: #490002; + + --td-success-color-1: #e3f9e9; + --td-success-color-2: #c6f3d7; + --td-success-color-3: #92dab2; + --td-success-color-4: #56c08d; + --td-success-color-5: #2ba471; + --td-success-color-6: #008858; + --td-success-color-7: #006c45; + --td-success-color-8: #005334; + --td-success-color-9: #003b23; + --td-success-color-10: #002515; + + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eeeeee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + + // 文字 & 图标 颜色 + --td-font-white-1: rgba(255, 255, 255, 1); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + + // 基础颜色 + --td-brand-color: var(--td-primary-color-7); + --td-warning-color: var(--td-warning-color-5); + --td-error-color: var(--td-error-color-6); + --td-success-color: var(--td-success-color-5); + + // 基础颜色的扩展 用于 聚焦 / 禁用 / 点击 等状态 + --td-brand-color-focus: var(--td-primary-color-1); + --td-brand-color-active: var(--td-primary-color-8); + --td-brand-color-disabled: var(--td-primary-color-3); + --td-brand-color-light: var(--td-primary-color-1); + --td-brand-color-light-active: var(--td-primary-color-2); + + // 警告色扩展 + --td-warning-color-active: var(--td-warning-color-6); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-light: var(--td-warning-color-1); + --td-warning-color-light-active: var(--td-warning-color-2); + + // 失败/错误色扩展 + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-7); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-error-color-light-active: var(--td-error-color-2); + + // 成功色扩展 + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-6); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-success-color-light-active: var(--td-success-color-2); + + // 遮罩 + --td-mask-active: rgba(0, 0, 0, 60%); // 遮罩-弹出 + --td-mask-disabled: rgba(255, 255, 255, 60%); // 遮罩-禁用 + --td-mask-background: rgba(255, 255, 255, 96%); // 二维码遮罩 + + // 背景色 + --td-bg-color-page: var(--td-gray-color-1); + --td-bg-color-container: var(--td-font-white-1); + --td-bg-color-container-active: var(--td-gray-color-3); + --td-bg-color-secondarycontainer: var(--td-gray-color-1); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-4); + --td-bg-color-component: var(--td-gray-color-3); + --td-bg-color-component-active: var(--td-gray-color-6); + --td-bg-color-component-disabled: var(--td-gray-color-2); + --td-bg-color-secondarycomponent: var(--td-gray-color-4); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-6); + + // 特殊组件背景色,目前只用于 button、input 组件多主题场景,浅色主题下固定为白色,深色主题下为 transparent 适配背景颜色 + --td-bg-color-specialcomponent: #fff; + + // 文本颜色 + --td-text-color-primary: var(--td-font-gray-1); + --td-text-color-secondary: var(--td-font-gray-2); + --td-text-color-placeholder: var(--td-font-gray-3); + --td-text-color-disabled: var(--td-font-gray-4); + --td-text-color-anti: var(--td-font-white-1); + --td-text-color-brand: var(--td-brand-color); + --td-text-color-link: var(--td-brand-color); + + // 分割线 + --td-border-level-1-color: var(--td-gray-color-3); + --td-component-stroke: var(--td-gray-color-3); + // 边框 + --td-border-level-2-color: var(--td-gray-color-4); + --td-component-border: var(--td-gray-color-4); + + // 基础/下层 投影 hover 使用的组件包括:表格 / + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, 5%), 0 4px 5px rgba(0, 0, 0, 8%), 0 2px 4px -1px rgba(0, 0, 0, 12%); + // 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 / + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, 5%), 0 8px 10px 1px rgba(0, 0, 0, 6%), 0 5px 5px -3px rgba(0, 0, 0, 10%); + // 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知 + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, 5%), 0 16px 24px 2px rgba(0, 0, 0, 4%), + 0 8px 10px -5px rgba(0, 0, 0, 8%); + --td-shadow-4: 0 2px 8px 0 rgba(0, 0, 0, 0.06); + + // 内投影 用于弹窗类组件(气泡确认框 / 全局提示 / 消息通知)的内描边 + --td-shadow-inset-top: inset 0 0.5px 0 #dcdcdc; + --td-shadow-inset-right: inset 0.5px 0 0 #dcdcdc; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #dcdcdc; + --td-shadow-inset-left: inset -0.5px 0 0 #dcdcdc; + + // table 特定阴影 + --td-table-shadow-color: rgba(0, 0, 0, 8%); + + // 滚动条颜色 + --td-scrollbar-color: rgba(0, 0, 0, 10%); + // 滚动条悬浮颜色( hover ) + --td-scrollbar-hover-color: rgba(0, 0, 0, 30%); + // 滚动条轨道颜色,不能是带透明度,否则纵向滚动时,横向滚动条会穿透 + --td-scroll-track-color: #fff; + } +} + + +/* ./raw/_dark.less */ +@media (prefers-color-scheme: dark) { + /* #ifdef H5 */ + :root, + /* #endif */ + page, + .page { + --td-brand-color-1: #1b2f51; + --td-brand-color-2: #173463; + --td-brand-color-3: #143975; + --td-brand-color-4: #103d88; + --td-brand-color-5: #0d429a; + --td-brand-color-6: #054bbe; + --td-brand-color-7: #2667d4; + --td-brand-color-8: #4582e6; + --td-brand-color-9: #699ef5; + --td-brand-color-10: #96bbf8; + + --td-primary-color-1: var(--td-brand-color-1); + --td-primary-color-2: var(--td-brand-color-2); + --td-primary-color-3: var(--td-brand-color-3); + --td-primary-color-4: var(--td-brand-color-4); + --td-primary-color-5: var(--td-brand-color-5); + --td-primary-color-6: var(--td-brand-color-6); + --td-primary-color-7: var(--td-brand-color-7); + --td-primary-color-8: var(--td-brand-color-8); + --td-primary-color-9: var(--td-brand-color-9); + --td-primary-color-10: var(--td-brand-color-10); + + --td-warning-color-1: #4f2a1d; + --td-warning-color-2: #582f21; + --td-warning-color-3: #733c23; + --td-warning-color-4: #a75d2b; + --td-warning-color-5: #cf6e2d; + --td-warning-color-6: #dc7633; + --td-warning-color-7: #e8935c; + --td-warning-color-8: #ecbf91; + --td-warning-color-9: #eed7bf; + --td-warning-color-10: #f3e9dc; + + --td-error-color-1: #472324; + --td-error-color-2: #5e2a2d; + --td-error-color-3: #703439; + --td-error-color-4: #83383e; + --td-error-color-5: #a03f46; + --td-error-color-6: #c64751; + --td-error-color-7: #de6670; + --td-error-color-8: #ec888e; + --td-error-color-9: #edb1b6; + --td-error-color-10: #eeced0; + + --td-success-color-1: #193a2a; + --td-success-color-2: #1a4230; + --td-success-color-3: #17533d; + --td-success-color-4: #0d7a55; + --td-success-color-5: #059465; + --td-success-color-6: #43af8a; + --td-success-color-7: #46bf96; + --td-success-color-8: #80d2b6; + --td-success-color-9: #b4e1d3; + --td-success-color-10: #deede8; + + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e8e8e8; + --td-gray-color-4: #ddd; + --td-gray-color-5: #c6c6c6; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + + // 文字 & 图标 颜色 + --td-font-white-1: rgba(255, 255, 255, 90%); + --td-font-white-2: rgba(255, 255, 255, 55%); + --td-font-white-3: rgba(255, 255, 255, 35%); + --td-font-white-4: rgba(255, 255, 255, 22%); + --td-font-gray-1: rgba(0, 0, 0, 90%); + --td-font-gray-2: rgba(0, 0, 0, 60%); + --td-font-gray-3: rgba(0, 0, 0, 40%); + --td-font-gray-4: rgba(0, 0, 0, 26%); + + // 基础颜色 + --td-brand-color: var(--td-primary-color-8); // 色彩-品牌-可操作 + --td-warning-color: var(--td-warning-color-5); // 色彩-功能-警告 + --td-error-color: var(--td-error-color-6); // 色彩-功能-失败 + --td-success-color: var(--td-success-color-5); // 色彩-功能-成功 + + // 基础颜色的扩展 用于 聚焦 / 禁用 / 点击 等状态 + --td-brand-color-focus: var(--td-primary-color-1); // focus态,包括鼠标和键盘 + --td-brand-color-active: var(--td-primary-color-9); // 点击态 + --td-brand-color-disabled: var(--td-primary-color-3); // 禁用态 + --td-brand-color-light: var(--td-primary-color-1); // 浅色的选中态 + --td-brand-color-light-active: var(--td-primary-color-2); // 浅色的选中态 + + // 警告色扩展 + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-active: var(--td-warning-color-4); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-light: var(--td-warning-color-1); + --td-warning-color-light-active: var(--td-warning-color-2); + + // 失败/错误色扩展 + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-5); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-error-color-light-active: var(--td-error-color-2); + + // 成功色扩展 + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-4); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-success-color-light-active: var(--td-success-color-2); + + // 遮罩 + --td-mask-active: rgba(0, 0, 0, 40%); // 遮罩-弹出 + --td-mask-disabled: rgba(0, 0, 0, 60%); // 遮罩-禁用 + --td-mask-background: rgba(36, 36, 36, 96%); // 二维码遮罩 + + // 背景色 + --td-bg-color-page: var(--td-gray-color-14); // 色彩 - page + --td-bg-color-container: var(--td-gray-color-13); // 色彩 - 容器 + --td-bg-color-secondarycontainer: var(--td-gray-color-12); // 色彩 - 次级容器 + --td-bg-color-component: var(--td-gray-color-11); // 色彩 - 组件 + --td-bg-color-container-active: var(--td-gray-color-12); // 色彩 - 容器 - active + --td-bg-color-secondarycontainer-active: var(--td-gray-color-11); // 色彩 - 次级容器 - active + --td-bg-color-component-active: var(--td-gray-color-10); // 色彩 - 组件 - active + --td-bg-color-component-disabled: var(--td-gray-color-12); // 色彩 - 组件 - disabled + --td-bg-color-secondarycomponent: var(--td-gray-color-10); // 色彩 - 次级组件 + --td-bg-color-secondarycomponent-active: var(--td-gray-color-8); // 色彩 - 次级组件 - active + + // 特殊组件背景色,目前只用于 button、input 组件多主题场景,浅色主题下固定为白色,深色主题下为 transparent 适配背景颜色 + --td-bg-color-specialcomponent: transparent; + + // 文本颜色 + --td-text-color-primary: var(--td-font-white-1); // 色彩-文字-主要 + --td-text-color-secondary: var(--td-font-white-2); // 色彩-文字-次要 + --td-text-color-placeholder: var(--td-font-white-3); // 色彩-文字-占位符/说明 + --td-text-color-disabled: var(--td-font-white-4); // 色彩-文字-禁用 + --td-text-color-anti: var(--td-font-white-1); // 色彩-文字-反色 + --td-text-color-brand: var(--td-primary-color-8); // 色彩-文字-品牌 + --td-text-color-link: var(--td-primary-color-8); // 色彩-文字-链接 + + // 分割线 + --td-border-level-1-color: var(--td-gray-color-11); + --td-component-stroke: var(--td-gray-color-11); + // 边框 + --td-border-level-2-color: var(--td-gray-color-9); + --td-component-border: var(--td-gray-color-9); + + // 基础/下层 投影 hover 使用的组件包括:表格 / + --td-shadow-1: 0 4px 6px rgba(0, 0, 0, 6%), 0 1px 10px rgba(0, 0, 0, 8%), 0 2px 4px rgba(0, 0, 0, 12%); + // 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 / + --td-shadow-2: 0 8px 10px rgba(0, 0, 0, 12%), 0 3px 14px rgba(0, 0, 0, 10%), 0 5px 5px rgba(0, 0, 0, 16%); + // 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知 + --td-shadow-3: 0 16px 24px rgba(0, 0, 0, 14%), 0 6px 30px rgba(0, 0, 0, 12%), 0 8px 10px rgba(0, 0, 0, 20%); + // 内投影 用于弹窗类组件(气泡确认框 / 全局提示 / 消息通知)的内描边 + + --td-shadow-inset-top: inset 0 0.5px 0 #5e5e5e; + --td-shadow-inset-right: inset 0.5px 0 0 #5e5e5e; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #5e5e5e; + --td-shadow-inset-left: inset -0.5px 0 0 #5e5e5e; + + // table 特定阴影 + --td-table-shadow-color: rgba(0, 0, 0, 55%); + + // 滚动条颜色 + --td-scrollbar-color: rgba(255, 255, 255, 10%); + // 滚动条轨道颜色,不能是带透明度,否则纵向滚动时,横向滚动条会穿透 + --td-scroll-track-color: #333; + } +} + + +/* ./raw/_radius.less */ +page, +.page { + // 圆角 + --td-radius-small: 12px; + --td-radius-default: 24px; + --td-radius-large: 36px; + --td-radius-extraLarge: 48px; + --td-radius-round: 999px; + --td-radius-circle: 50%; +} + + +/* ./raw/_font.less */ +page, +.page { + // 字体family token + --td-font-family: PingFang SC, Microsoft YaHei, Arial Regular; // 字体-磅数-常规 + --td-font-family-medium: PingFang SC, Microsoft YaHei, Arial Medium; // 字体-磅数-粗体 + + --td-font-size-link-small: 48px; + --td-font-size-link-medium: 56px; + --td-font-size-link-large: 64px; + --td-font-size-mark-extraSmall: 40px; + --td-font-size-mark-small: 48px; + --td-font-size-mark-medium: 56px; + --td-font-size-mark-large: 64px; + --td-font-size-body-extraSmall: 40px; + --td-font-size-body-small: 48px; + --td-font-size-body-medium: 56px; + --td-font-size-body-large: 64px; + --td-font-size-title-small: 56px; + --td-font-size-title-medium: 64px; + --td-font-size-title-large: 72px; + --td-font-size-title-extraLarge: 80px; + --td-font-size-headline-small: 96px; + --td-font-size-headline-medium: 112px; + --td-font-size-headline-large: 144px; + --td-font-size-display-medium: 192px; + --td-font-size-display-large: 256px; + + // 字体行高 token + --td-line-height-link-small: 80px; + --td-line-height-link-medium: 88px; + --td-line-height-link-large: 96px; + --td-line-height-mark-extraSmall: 64px; + --td-line-height-mark-small: 80px; + --td-line-height-mark-medium: 88px; + --td-line-height-mark-large: 96px; + --td-line-height-body-extraSmall: 64px; + --td-line-height-body-small: 80px; + --td-line-height-body-medium: 88px; + --td-line-height-body-large: 96px; + --td-line-height-title-small: 88px; + --td-line-height-title-medium: 96px; + --td-line-height-title-large: 104px; + --td-line-height-title-extraLarge: 112px; + --td-line-height-headline-small: 128px; + --td-line-height-headline-medium: 144px; + --td-line-height-headline-large: 176px; + --td-line-height-display-medium: 224px; + --td-line-height-display-large: 288px; + + // font token + --td-font-link-small: var(--td-font-size-link-small) / var(--td-line-height-link-small) var(--td-font-family); + --td-font-link-medium: var(--td-font-size-link-medium) / var(--td-line-height-link-medium) var(--td-font-family); + --td-font-link-large: var(--td-font-size-link-large) / var(--td-line-height-link-large) var(--td-font-family); + --td-font-mark-extraSmall: 600 var(--td-font-size-mark-extraSmall) / var(--td-line-height-mark-extraSmall) + var(--td-font-family); + --td-font-mark-small: 600 var(--td-font-size-mark-small) / var(--td-line-height-mark-small) var(--td-font-family); + --td-font-mark-medium: 600 var(--td-font-size-mark-medium) / var(--td-line-height-mark-medium) var(--td-font-family); + --td-font-mark-large: 600 var(--td-font-size-mark-large) / var(--td-line-height-mark-large) var(--td-font-family); + --td-font-body-extraSmall: var(--td-font-size-body-extraSmall) / var(--td-line-height-body-extraSmall) + var(--td-font-family); + --td-font-body-small: var(--td-font-size-body-small) / var(--td-line-height-body-small) var(--td-font-family); + --td-font-body-medium: var(--td-font-size-body-medium) / var(--td-line-height-body-medium) var(--td-font-family); + --td-font-body-large: var(--td-font-size-body-large) / var(--td-line-height-body-large) var(--td-font-family); + --td-font-title-small: 600 var(--td-font-size-title-small) / var(--td-line-height-title-small) var(--td-font-family); + --td-font-title-medium: 600 var(--td-font-size-title-medium) / var(--td-line-height-title-medium) + var(--td-font-family); + --td-font-title-large: 600 var(--td-font-size-title-large) / var(--td-line-height-title-large) var(--td-font-family); + --td-font-title-extraLarge: 600 var(--td-font-size-title-extraLarge) / var(--td-line-height-title-extraLarge) + var(--td-font-family); + --td-font-headline-small: 600 var(--td-font-size-headline-small) / var(--td-line-height-headline-small) + var(--td-font-family); + --td-font-headline-medium: 600 var(--td-font-size-headline-medium) / var(--td-line-height-headline-medium) + var(--td-font-family); + --td-font-headline-large: 600 var(--td-font-size-headline-large) / var(--td-line-height-headline-large) + var(--td-font-family); + --td-font-display-medium: 600 var(--td-font-size-display-medium) / var(--td-line-height-display-medium) + var(--td-font-family); + --td-font-display-large: 600 var(--td-font-size-display-large) / var(--td-line-height-display-large) + var(--td-font-family); + + // 字体大小 token + --td-font-size: 40px; + --td-font-size-xs: var(--td-font-size-body-extraSmall); + --td-font-size-s: var(--td-font-size-body-small); + --td-font-size-base: var(--td-font-size-title-small); + --td-font-size-m: var(--td-font-size-title-medium); + --td-font-size-l: var(--td-font-size-title-large); + --td-font-size-xl: var(--td-font-size-title-extraLarge); + --td-font-size-xxl: var(--td-font-size-headline-large); +} + + +/* ./raw/_spacer.less */ +page, +.page { + // Spacer + --td-spacer: 32px; + --td-spacer-1: 48px; // 间距-小-x + --td-spacer-2: 64px; // 间距-小 + --td-spacer-3: 96px; // 间距-中 + --td-spacer-4: 128px; // 间距-大 + --td-spacer-5: 192px; // 间距-大-x + --td-spacer-6: 320px; // 间距-大-xx +} + + +/* ./raw/_components.less */ +// 部分特殊处理的组件级 token +@media (prefers-color-scheme: light) { + page, + .page { + --td-picker-transparent-color: rgba(255, 255, 255, 0); + + --td-switch-dot-disabled-color: var(--td-font-white-1); + --td-switch-loading-color: var(--td-brand-color); + } +} + +@media (prefers-color-scheme: dark) { + page, + .page { + --td-button-primary-disabled-color: var(--td-font-white-4); + + --td-skeleton-animation-gradient: rgba(255, 255, 255, 6%); + + --td-slider-dot-bg-color: var(--td-gray-color-4); + --td-slider-dot-disabled-bg-color: var(--td-gray-color-11); + --td-slider-dot-disabled-border-color: var(--td-gray-color-12); + + --td-picker-transparent-color: transparent; + + --td-switch-dot-disabled-color: var(--td-font-white-2); + --td-switch-loading-color: var(--td-font-white-1); + + --td-progress-circle-inner-bg-color: var(--bg-color-page); + } +} + diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/index.less b/uni_modules/tdesign-uniapp/components/common/style/theme/index.less new file mode 100644 index 0000000..e77439c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/index.less @@ -0,0 +1,11 @@ +@import './raw/_light.less'; + +@import './raw/_dark.less'; + +@import './raw/_radius.less'; + +@import './raw/_font.less'; + +@import './raw/_spacer.less'; + +@import './raw/_components.less'; diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_components.less b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_components.less new file mode 100644 index 0000000..a99107d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_components.less @@ -0,0 +1,30 @@ +// 部分特殊处理的组件级 token +@media (prefers-color-scheme: light) { + page, + .page { + --td-picker-transparent-color: rgba(255, 255, 255, 0); + + --td-switch-dot-disabled-color: var(--td-font-white-1); + --td-switch-loading-color: var(--td-brand-color); + } +} + +@media (prefers-color-scheme: dark) { + page, + .page { + --td-button-primary-disabled-color: var(--td-font-white-4); + + --td-skeleton-animation-gradient: rgba(255, 255, 255, 6%); + + --td-slider-dot-bg-color: var(--td-gray-color-4); + --td-slider-dot-disabled-bg-color: var(--td-gray-color-11); + --td-slider-dot-disabled-border-color: var(--td-gray-color-12); + + --td-picker-transparent-color: transparent; + + --td-switch-dot-disabled-color: var(--td-font-white-2); + --td-switch-loading-color: var(--td-font-white-1); + + --td-progress-circle-inner-bg-color: var(--bg-color-page); + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_dark.less b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_dark.less new file mode 100644 index 0000000..6c08ea9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_dark.less @@ -0,0 +1,178 @@ +@media (prefers-color-scheme: dark) { + /* #ifdef H5 */ + :root, + /* #endif */ + page, + .page { + --td-brand-color-1: #1b2f51; + --td-brand-color-2: #173463; + --td-brand-color-3: #143975; + --td-brand-color-4: #103d88; + --td-brand-color-5: #0d429a; + --td-brand-color-6: #054bbe; + --td-brand-color-7: #2667d4; + --td-brand-color-8: #4582e6; + --td-brand-color-9: #699ef5; + --td-brand-color-10: #96bbf8; + + --td-primary-color-1: var(--td-brand-color-1); + --td-primary-color-2: var(--td-brand-color-2); + --td-primary-color-3: var(--td-brand-color-3); + --td-primary-color-4: var(--td-brand-color-4); + --td-primary-color-5: var(--td-brand-color-5); + --td-primary-color-6: var(--td-brand-color-6); + --td-primary-color-7: var(--td-brand-color-7); + --td-primary-color-8: var(--td-brand-color-8); + --td-primary-color-9: var(--td-brand-color-9); + --td-primary-color-10: var(--td-brand-color-10); + + --td-warning-color-1: #4f2a1d; + --td-warning-color-2: #582f21; + --td-warning-color-3: #733c23; + --td-warning-color-4: #a75d2b; + --td-warning-color-5: #cf6e2d; + --td-warning-color-6: #dc7633; + --td-warning-color-7: #e8935c; + --td-warning-color-8: #ecbf91; + --td-warning-color-9: #eed7bf; + --td-warning-color-10: #f3e9dc; + + --td-error-color-1: #472324; + --td-error-color-2: #5e2a2d; + --td-error-color-3: #703439; + --td-error-color-4: #83383e; + --td-error-color-5: #a03f46; + --td-error-color-6: #c64751; + --td-error-color-7: #de6670; + --td-error-color-8: #ec888e; + --td-error-color-9: #edb1b6; + --td-error-color-10: #eeced0; + + --td-success-color-1: #193a2a; + --td-success-color-2: #1a4230; + --td-success-color-3: #17533d; + --td-success-color-4: #0d7a55; + --td-success-color-5: #059465; + --td-success-color-6: #43af8a; + --td-success-color-7: #46bf96; + --td-success-color-8: #80d2b6; + --td-success-color-9: #b4e1d3; + --td-success-color-10: #deede8; + + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eee; + --td-gray-color-3: #e8e8e8; + --td-gray-color-4: #ddd; + --td-gray-color-5: #c6c6c6; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + + // 文字 & 图标 颜色 + --td-font-white-1: rgba(255, 255, 255, 90%); + --td-font-white-2: rgba(255, 255, 255, 55%); + --td-font-white-3: rgba(255, 255, 255, 35%); + --td-font-white-4: rgba(255, 255, 255, 22%); + --td-font-gray-1: rgba(0, 0, 0, 90%); + --td-font-gray-2: rgba(0, 0, 0, 60%); + --td-font-gray-3: rgba(0, 0, 0, 40%); + --td-font-gray-4: rgba(0, 0, 0, 26%); + + // 基础颜色 + --td-brand-color: var(--td-primary-color-8); // 色彩-品牌-可操作 + --td-warning-color: var(--td-warning-color-5); // 色彩-功能-警告 + --td-error-color: var(--td-error-color-6); // 色彩-功能-失败 + --td-success-color: var(--td-success-color-5); // 色彩-功能-成功 + + // 基础颜色的扩展 用于 聚焦 / 禁用 / 点击 等状态 + --td-brand-color-focus: var(--td-primary-color-1); // focus态,包括鼠标和键盘 + --td-brand-color-active: var(--td-primary-color-9); // 点击态 + --td-brand-color-disabled: var(--td-primary-color-3); // 禁用态 + --td-brand-color-light: var(--td-primary-color-1); // 浅色的选中态 + --td-brand-color-light-active: var(--td-primary-color-2); // 浅色的选中态 + + // 警告色扩展 + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-active: var(--td-warning-color-4); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-light: var(--td-warning-color-1); + --td-warning-color-light-active: var(--td-warning-color-2); + + // 失败/错误色扩展 + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-5); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-error-color-light-active: var(--td-error-color-2); + + // 成功色扩展 + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-4); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-success-color-light-active: var(--td-success-color-2); + + // 遮罩 + --td-mask-active: rgba(0, 0, 0, 40%); // 遮罩-弹出 + --td-mask-disabled: rgba(0, 0, 0, 60%); // 遮罩-禁用 + --td-mask-background: rgba(36, 36, 36, 96%); // 二维码遮罩 + + // 背景色 + --td-bg-color-page: var(--td-gray-color-14); // 色彩 - page + --td-bg-color-container: var(--td-gray-color-13); // 色彩 - 容器 + --td-bg-color-secondarycontainer: var(--td-gray-color-12); // 色彩 - 次级容器 + --td-bg-color-component: var(--td-gray-color-11); // 色彩 - 组件 + --td-bg-color-container-active: var(--td-gray-color-12); // 色彩 - 容器 - active + --td-bg-color-secondarycontainer-active: var(--td-gray-color-11); // 色彩 - 次级容器 - active + --td-bg-color-component-active: var(--td-gray-color-10); // 色彩 - 组件 - active + --td-bg-color-component-disabled: var(--td-gray-color-12); // 色彩 - 组件 - disabled + --td-bg-color-secondarycomponent: var(--td-gray-color-10); // 色彩 - 次级组件 + --td-bg-color-secondarycomponent-active: var(--td-gray-color-8); // 色彩 - 次级组件 - active + + // 特殊组件背景色,目前只用于 button、input 组件多主题场景,浅色主题下固定为白色,深色主题下为 transparent 适配背景颜色 + --td-bg-color-specialcomponent: transparent; + + // 文本颜色 + --td-text-color-primary: var(--td-font-white-1); // 色彩-文字-主要 + --td-text-color-secondary: var(--td-font-white-2); // 色彩-文字-次要 + --td-text-color-placeholder: var(--td-font-white-3); // 色彩-文字-占位符/说明 + --td-text-color-disabled: var(--td-font-white-4); // 色彩-文字-禁用 + --td-text-color-anti: var(--td-font-white-1); // 色彩-文字-反色 + --td-text-color-brand: var(--td-primary-color-8); // 色彩-文字-品牌 + --td-text-color-link: var(--td-primary-color-8); // 色彩-文字-链接 + + // 分割线 + --td-border-level-1-color: var(--td-gray-color-11); + --td-component-stroke: var(--td-gray-color-11); + // 边框 + --td-border-level-2-color: var(--td-gray-color-9); + --td-component-border: var(--td-gray-color-9); + + // 基础/下层 投影 hover 使用的组件包括:表格 / + --td-shadow-1: 0 4px 6px rgba(0, 0, 0, 6%), 0 1px 10px rgba(0, 0, 0, 8%), 0 2px 4px rgba(0, 0, 0, 12%); + // 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 / + --td-shadow-2: 0 8px 10px rgba(0, 0, 0, 12%), 0 3px 14px rgba(0, 0, 0, 10%), 0 5px 5px rgba(0, 0, 0, 16%); + // 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知 + --td-shadow-3: 0 16px 24px rgba(0, 0, 0, 14%), 0 6px 30px rgba(0, 0, 0, 12%), 0 8px 10px rgba(0, 0, 0, 20%); + // 内投影 用于弹窗类组件(气泡确认框 / 全局提示 / 消息通知)的内描边 + + --td-shadow-inset-top: inset 0 0.5px 0 #5e5e5e; + --td-shadow-inset-right: inset 0.5px 0 0 #5e5e5e; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #5e5e5e; + --td-shadow-inset-left: inset -0.5px 0 0 #5e5e5e; + + // table 特定阴影 + --td-table-shadow-color: rgba(0, 0, 0, 55%); + + // 滚动条颜色 + --td-scrollbar-color: rgba(255, 255, 255, 10%); + // 滚动条轨道颜色,不能是带透明度,否则纵向滚动时,横向滚动条会穿透 + --td-scroll-track-color: #333; + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_font.less b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_font.less new file mode 100644 index 0000000..3e0d0d7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_font.less @@ -0,0 +1,90 @@ +page, +.page { + // 字体family token + --td-font-family: PingFang SC, Microsoft YaHei, Arial Regular; // 字体-磅数-常规 + --td-font-family-medium: PingFang SC, Microsoft YaHei, Arial Medium; // 字体-磅数-粗体 + + --td-font-size-link-small: 24rpx; + --td-font-size-link-medium: 28rpx; + --td-font-size-link-large: 32rpx; + --td-font-size-mark-extraSmall: 20rpx; + --td-font-size-mark-small: 24rpx; + --td-font-size-mark-medium: 28rpx; + --td-font-size-mark-large: 32rpx; + --td-font-size-body-extraSmall: 20rpx; + --td-font-size-body-small: 24rpx; + --td-font-size-body-medium: 28rpx; + --td-font-size-body-large: 32rpx; + --td-font-size-title-small: 28rpx; + --td-font-size-title-medium: 32rpx; + --td-font-size-title-large: 36rpx; + --td-font-size-title-extraLarge: 40rpx; + --td-font-size-headline-small: 48rpx; + --td-font-size-headline-medium: 56rpx; + --td-font-size-headline-large: 72rpx; + --td-font-size-display-medium: 96rpx; + --td-font-size-display-large: 128rpx; + + // 字体行高 token + --td-line-height-link-small: 40rpx; + --td-line-height-link-medium: 44rpx; + --td-line-height-link-large: 48rpx; + --td-line-height-mark-extraSmall: 32rpx; + --td-line-height-mark-small: 40rpx; + --td-line-height-mark-medium: 44rpx; + --td-line-height-mark-large: 48rpx; + --td-line-height-body-extraSmall: 32rpx; + --td-line-height-body-small: 40rpx; + --td-line-height-body-medium: 44rpx; + --td-line-height-body-large: 48rpx; + --td-line-height-title-small: 44rpx; + --td-line-height-title-medium: 48rpx; + --td-line-height-title-large: 52rpx; + --td-line-height-title-extraLarge: 56rpx; + --td-line-height-headline-small: 64rpx; + --td-line-height-headline-medium: 72rpx; + --td-line-height-headline-large: 88rpx; + --td-line-height-display-medium: 112rpx; + --td-line-height-display-large: 144rpx; + + // font token + --td-font-link-small: var(--td-font-size-link-small) / var(--td-line-height-link-small) var(--td-font-family); + --td-font-link-medium: var(--td-font-size-link-medium) / var(--td-line-height-link-medium) var(--td-font-family); + --td-font-link-large: var(--td-font-size-link-large) / var(--td-line-height-link-large) var(--td-font-family); + --td-font-mark-extraSmall: 600 var(--td-font-size-mark-extraSmall) / var(--td-line-height-mark-extraSmall) + var(--td-font-family); + --td-font-mark-small: 600 var(--td-font-size-mark-small) / var(--td-line-height-mark-small) var(--td-font-family); + --td-font-mark-medium: 600 var(--td-font-size-mark-medium) / var(--td-line-height-mark-medium) var(--td-font-family); + --td-font-mark-large: 600 var(--td-font-size-mark-large) / var(--td-line-height-mark-large) var(--td-font-family); + --td-font-body-extraSmall: var(--td-font-size-body-extraSmall) / var(--td-line-height-body-extraSmall) + var(--td-font-family); + --td-font-body-small: var(--td-font-size-body-small) / var(--td-line-height-body-small) var(--td-font-family); + --td-font-body-medium: var(--td-font-size-body-medium) / var(--td-line-height-body-medium) var(--td-font-family); + --td-font-body-large: var(--td-font-size-body-large) / var(--td-line-height-body-large) var(--td-font-family); + --td-font-title-small: 600 var(--td-font-size-title-small) / var(--td-line-height-title-small) var(--td-font-family); + --td-font-title-medium: 600 var(--td-font-size-title-medium) / var(--td-line-height-title-medium) + var(--td-font-family); + --td-font-title-large: 600 var(--td-font-size-title-large) / var(--td-line-height-title-large) var(--td-font-family); + --td-font-title-extraLarge: 600 var(--td-font-size-title-extraLarge) / var(--td-line-height-title-extraLarge) + var(--td-font-family); + --td-font-headline-small: 600 var(--td-font-size-headline-small) / var(--td-line-height-headline-small) + var(--td-font-family); + --td-font-headline-medium: 600 var(--td-font-size-headline-medium) / var(--td-line-height-headline-medium) + var(--td-font-family); + --td-font-headline-large: 600 var(--td-font-size-headline-large) / var(--td-line-height-headline-large) + var(--td-font-family); + --td-font-display-medium: 600 var(--td-font-size-display-medium) / var(--td-line-height-display-medium) + var(--td-font-family); + --td-font-display-large: 600 var(--td-font-size-display-large) / var(--td-line-height-display-large) + var(--td-font-family); + + // 字体大小 token + --td-font-size: 20rpx; + --td-font-size-xs: var(--td-font-size-body-extraSmall); + --td-font-size-s: var(--td-font-size-body-small); + --td-font-size-base: var(--td-font-size-title-small); + --td-font-size-m: var(--td-font-size-title-medium); + --td-font-size-l: var(--td-font-size-title-large); + --td-font-size-xl: var(--td-font-size-title-extraLarge); + --td-font-size-xxl: var(--td-font-size-headline-large); +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_light.less b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_light.less new file mode 100644 index 0000000..537f921 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_light.less @@ -0,0 +1,182 @@ +@media (prefers-color-scheme: light) { + /* #ifdef H5 */ + :root, + /* #endif */ + page, + .page { + --td-brand-color-1: #f2f3ff; + --td-brand-color-2: #d9e1ff; + --td-brand-color-3: #b5c7ff; + --td-brand-color-4: #8eabff; + --td-brand-color-5: #618dff; + --td-brand-color-6: #366ef4; + --td-brand-color-7: #0052d9; + --td-brand-color-8: #003cab; + --td-brand-color-9: #002a7c; + --td-brand-color-10: #001a57; + + --td-primary-color-1: var(--td-brand-color-1); + --td-primary-color-2: var(--td-brand-color-2); + --td-primary-color-3: var(--td-brand-color-3); + --td-primary-color-4: var(--td-brand-color-4); + --td-primary-color-5: var(--td-brand-color-5); + --td-primary-color-6: var(--td-brand-color-6); + --td-primary-color-7: var(--td-brand-color-7); + --td-primary-color-8: var(--td-brand-color-8); + --td-primary-color-9: var(--td-brand-color-9); + --td-primary-color-10: var(--td-brand-color-10); + + --td-warning-color-1: #fff1e9; + --td-warning-color-2: #ffd9c2; + --td-warning-color-3: #ffb98c; + --td-warning-color-4: #fa9550; + --td-warning-color-5: #e37318; + --td-warning-color-6: #be5a00; + --td-warning-color-7: #954500; + --td-warning-color-8: #713300; + --td-warning-color-9: #532300; + --td-warning-color-10: #3b1700; + + --td-error-color-1: #fff0ed; + --td-error-color-2: #ffd8d2; + --td-error-color-3: #ffb9b0; + --td-error-color-4: #ff9285; + --td-error-color-5: #f6685d; + --td-error-color-6: #d54941; + --td-error-color-7: #ad352f; + --td-error-color-8: #881f1c; + --td-error-color-9: #68070a; + --td-error-color-10: #490002; + + --td-success-color-1: #e3f9e9; + --td-success-color-2: #c6f3d7; + --td-success-color-3: #92dab2; + --td-success-color-4: #56c08d; + --td-success-color-5: #2ba471; + --td-success-color-6: #008858; + --td-success-color-7: #006c45; + --td-success-color-8: #005334; + --td-success-color-9: #003b23; + --td-success-color-10: #002515; + + --td-gray-color-1: #f3f3f3; + --td-gray-color-2: #eeeeee; + --td-gray-color-3: #e7e7e7; + --td-gray-color-4: #dcdcdc; + --td-gray-color-5: #c5c5c5; + --td-gray-color-6: #a6a6a6; + --td-gray-color-7: #8b8b8b; + --td-gray-color-8: #777777; + --td-gray-color-9: #5e5e5e; + --td-gray-color-10: #4b4b4b; + --td-gray-color-11: #383838; + --td-gray-color-12: #2c2c2c; + --td-gray-color-13: #242424; + --td-gray-color-14: #181818; + + // 文字 & 图标 颜色 + --td-font-white-1: rgba(255, 255, 255, 1); + --td-font-white-2: rgba(255, 255, 255, 0.55); + --td-font-white-3: rgba(255, 255, 255, 0.35); + --td-font-white-4: rgba(255, 255, 255, 0.22); + --td-font-gray-1: rgba(0, 0, 0, 0.9); + --td-font-gray-2: rgba(0, 0, 0, 0.6); + --td-font-gray-3: rgba(0, 0, 0, 0.4); + --td-font-gray-4: rgba(0, 0, 0, 0.26); + + // 基础颜色 + --td-brand-color: var(--td-primary-color-7); + --td-warning-color: var(--td-warning-color-5); + --td-error-color: var(--td-error-color-6); + --td-success-color: var(--td-success-color-5); + + // 基础颜色的扩展 用于 聚焦 / 禁用 / 点击 等状态 + --td-brand-color-focus: var(--td-primary-color-1); + --td-brand-color-active: var(--td-primary-color-8); + --td-brand-color-disabled: var(--td-primary-color-3); + --td-brand-color-light: var(--td-primary-color-1); + --td-brand-color-light-active: var(--td-primary-color-2); + + // 警告色扩展 + --td-warning-color-active: var(--td-warning-color-6); + --td-warning-color-disabled: var(--td-warning-color-3); + --td-warning-color-focus: var(--td-warning-color-2); + --td-warning-color-light: var(--td-warning-color-1); + --td-warning-color-light-active: var(--td-warning-color-2); + + // 失败/错误色扩展 + --td-error-color-focus: var(--td-error-color-2); + --td-error-color-active: var(--td-error-color-7); + --td-error-color-disabled: var(--td-error-color-3); + --td-error-color-light: var(--td-error-color-1); + --td-error-color-light-active: var(--td-error-color-2); + + // 成功色扩展 + --td-success-color-focus: var(--td-success-color-2); + --td-success-color-active: var(--td-success-color-6); + --td-success-color-disabled: var(--td-success-color-3); + --td-success-color-light: var(--td-success-color-1); + --td-success-color-light-active: var(--td-success-color-2); + + // 遮罩 + --td-mask-active: rgba(0, 0, 0, 60%); // 遮罩-弹出 + --td-mask-disabled: rgba(255, 255, 255, 60%); // 遮罩-禁用 + --td-mask-background: rgba(255, 255, 255, 96%); // 二维码遮罩 + + // 背景色 + --td-bg-color-page: var(--td-gray-color-1); + --td-bg-color-container: var(--td-font-white-1); + --td-bg-color-container-active: var(--td-gray-color-3); + --td-bg-color-secondarycontainer: var(--td-gray-color-1); + --td-bg-color-secondarycontainer-active: var(--td-gray-color-4); + --td-bg-color-component: var(--td-gray-color-3); + --td-bg-color-component-active: var(--td-gray-color-6); + --td-bg-color-component-disabled: var(--td-gray-color-2); + --td-bg-color-secondarycomponent: var(--td-gray-color-4); + --td-bg-color-secondarycomponent-active: var(--td-gray-color-6); + + // 特殊组件背景色,目前只用于 button、input 组件多主题场景,浅色主题下固定为白色,深色主题下为 transparent 适配背景颜色 + --td-bg-color-specialcomponent: #fff; + + // 文本颜色 + --td-text-color-primary: var(--td-font-gray-1); + --td-text-color-secondary: var(--td-font-gray-2); + --td-text-color-placeholder: var(--td-font-gray-3); + --td-text-color-disabled: var(--td-font-gray-4); + --td-text-color-anti: var(--td-font-white-1); + --td-text-color-brand: var(--td-brand-color); + --td-text-color-link: var(--td-brand-color); + + // 分割线 + --td-border-level-1-color: var(--td-gray-color-3); + --td-component-stroke: var(--td-gray-color-3); + // 边框 + --td-border-level-2-color: var(--td-gray-color-4); + --td-component-border: var(--td-gray-color-4); + + // 基础/下层 投影 hover 使用的组件包括:表格 / + --td-shadow-1: 0 1px 10px rgba(0, 0, 0, 5%), 0 4px 5px rgba(0, 0, 0, 8%), 0 2px 4px -1px rgba(0, 0, 0, 12%); + // 中层投影 下拉 使用的组件包括:下拉菜单 / 气泡确认框 / 选择器 / + --td-shadow-2: 0 3px 14px 2px rgba(0, 0, 0, 5%), 0 8px 10px 1px rgba(0, 0, 0, 6%), 0 5px 5px -3px rgba(0, 0, 0, 10%); + // 上层投影(警示/弹窗)使用的组件包括:全局提示 / 消息通知 + --td-shadow-3: 0 6px 30px 5px rgba(0, 0, 0, 5%), 0 16px 24px 2px rgba(0, 0, 0, 4%), + 0 8px 10px -5px rgba(0, 0, 0, 8%); + --td-shadow-4: 0 2px 8px 0 rgba(0, 0, 0, 0.06); + + // 内投影 用于弹窗类组件(气泡确认框 / 全局提示 / 消息通知)的内描边 + --td-shadow-inset-top: inset 0 0.5px 0 #dcdcdc; + --td-shadow-inset-right: inset 0.5px 0 0 #dcdcdc; + --td-shadow-inset-bottom: inset 0 -0.5px 0 #dcdcdc; + --td-shadow-inset-left: inset -0.5px 0 0 #dcdcdc; + + // table 特定阴影 + --td-table-shadow-color: rgba(0, 0, 0, 8%); + + // 滚动条颜色 + --td-scrollbar-color: rgba(0, 0, 0, 10%); + // 滚动条悬浮颜色( hover ) + --td-scrollbar-hover-color: rgba(0, 0, 0, 30%); + // 滚动条轨道颜色,不能是带透明度,否则纵向滚动时,横向滚动条会穿透 + --td-scroll-track-color: #fff; + } +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_radius.less b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_radius.less new file mode 100644 index 0000000..4d69c6c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_radius.less @@ -0,0 +1,10 @@ +page, +.page { + // 圆角 + --td-radius-small: 6rpx; + --td-radius-default: 12rpx; + --td-radius-large: 18rpx; + --td-radius-extraLarge: 24rpx; + --td-radius-round: 999px; + --td-radius-circle: 50%; +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_spacer.less b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_spacer.less new file mode 100644 index 0000000..dcb07d9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/theme/raw/_spacer.less @@ -0,0 +1,11 @@ +page, +.page { + // Spacer + --td-spacer: 16rpx; + --td-spacer-1: 24rpx; // 间距-小-x + --td-spacer-2: 32rpx; // 间距-小 + --td-spacer-3: 48rpx; // 间距-中 + --td-spacer-4: 64rpx; // 间距-大 + --td-spacer-5: 96rpx; // 间距-大-x + --td-spacer-6: 160rpx; // 间距-大-xx +} diff --git a/uni_modules/tdesign-uniapp/components/common/style/utilities/index.css b/uni_modules/tdesign-uniapp/components/common/style/utilities/index.css new file mode 100644 index 0000000..5eb332d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/style/utilities/index.css @@ -0,0 +1,13 @@ +.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); +} diff --git a/uni_modules/tdesign-uniapp/components/common/utils.js b/uni_modules/tdesign-uniapp/components/common/utils.js new file mode 100644 index 0000000..c832ef2 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/utils.js @@ -0,0 +1,358 @@ +import { prefix } from './config'; +import { isString, isNumeric, isDef, isBoolean, isObject } from './validator'; +import { getWindowInfo, getAppBaseInfo, getDeviceInfo } from './wechat'; + +export { getWindowInfo }; + +export const systemInfo = getWindowInfo(); + +export const appBaseInfo = getAppBaseInfo(); + +export const deviceInfo = getDeviceInfo(); + + +/** + * 多参数空值合并函数 + * @param {...any} args - 任意数量的参数 + * @returns {any} 第一个非null/undefined的参数值 + */ +export function coalesce(...args) { + // 遍历所有参数 + for (let i = 0; i < args.length; i += 1) { + // 返回第一个非null且非undefined的值 + if (args[i] !== null && args[i] !== undefined) { + return args[i]; + } + } + // 如果所有参数都是null/undefined,返回最后一个参数 + return args[args.length - 1]; +} + + +export const debounce = function (func, wait = 500) { + let timerId; + return function (...rest) { + if (timerId) { + clearTimeout(timerId); + } + timerId = setTimeout(() => { + func.apply(this, rest); + }, wait); + }; +}; + +export const throttle = (func, wait = 100, options = null) => { + let previous = 0; + let timerid = null; + + if (!options) { + options = { + leading: true, + }; + } + + return function (...args) { + const now = Date.now(); + + if (!previous && !options.leading) previous = now; + + const remaining = wait - (now - previous); + const context = this; + + if (remaining <= 0) { + if (timerid) { + clearTimeout(timerid); + timerid = null; + } + previous = now; + func.apply(context, args); + } + }; +}; + +export const classNames = function (...args) { + const hasOwn = {}.hasOwnProperty; + const classes = []; + + args.forEach((arg) => { + // for (let i = 0; i < args.length; i++) { + // eslint-disable-next-line + // const arg = args[i] + if (!arg) return; + + const argType = typeof arg; + + if (argType === 'string' || argType === 'number') { + classes.push(arg); + } else if (Array.isArray(arg) && arg.length) { + const inner = classNames(...arg); + if (inner) { + classes.push(inner); + } + } else if (argType === 'object') { + // eslint-disable-next-line + for (const key in arg) { + if (hasOwn.call(arg, key) && arg[key]) { + classes.push(key); + } + } + } + }); + + return classes.join(' '); +}; + +export const styles = function (styleObj) { + return Object.keys(styleObj) + .map(styleKey => `${styleKey}: ${styleObj[styleKey]}`) + .join('; '); +}; + +export const getAnimationFrame = function (context, cb) { + return uni + .createSelectorQuery() + .in(context) + .selectViewport() + .boundingClientRect() + .exec(() => { + cb(); + }); +}; + +export const getRect = function (context, selector, needAll = false, useH5Origin = false) { + let result; + // #ifdef H5 + if (useH5Origin) { + result = document[needAll ? 'querySelectorAll' : 'querySelector'](selector)?.getBoundingClientRect(); + } + // #endif + if (result) { + return result; + } + + return new Promise((resolve, reject) => { + uni + .createSelectorQuery() + .in(context) + // eslint-disable-next-line no-unexpected-multiline + [needAll ? 'selectAll' : 'select'](selector) + .boundingClientRect((rect) => { + if (rect) { + resolve(rect); + } else { + reject(rect); + } + }) + .exec(); + }); +}; + + +export const getTreeDepth = (tree, key) => tree.reduce((maxDepth, node) => { + const keyName = coalesce(key, 'children'); + if (node[keyName] && node[keyName].length > 0) { + return Math.max(maxDepth, getTreeDepth(node[keyName], key) + 1); + } + return Math.max(maxDepth, 1); +}, 0); + +export const isIOS = function () { + return !!(deviceInfo?.system?.toLowerCase().search('ios') + 1); +}; + +/** + * 判断是否是为企微环境 + * 企微环境 wx.getSystemInfoSync() 接口会额外返回 environment 字段(微信中不返回) + * https://developer.work.weixin.qq.com/document/path/91511 + */ +export const isWxWork = deviceInfo?.environment === 'wxwork'; + +export const isPC = ['mac', 'windows'].includes(deviceInfo?.platform); + +export const addUnit = function (value) { + if (!isDef(value)) { + return undefined; + } + value = String(value); + return isNumeric(value) ? `${value}px` : value; +}; + +/** + * 计算字符串字符的长度并可以截取字符串。 + * @param char 传入字符串(maxcharacter条件下,一个汉字表示两个字符) + * @param max 规定最大字符串长度 + * @returns 当没有传入maxCharacter/maxLength 时返回字符串字符长度,当传入maxCharacter/maxLength时返回截取之后的字符串和长度。 + */ +export const getCharacterLength = (type, char, max) => { + const str = String(coalesce(char, '')); + + if (str.length === 0) { + return { + length: 0, + characters: '', + }; + } + + if (type === 'maxcharacter') { + let len = 0; + for (let i = 0; i < str.length; i += 1) { + let currentStringLength = 0; + if (str.charCodeAt(i) > 127 || str.charCodeAt(i) === 94) { + currentStringLength = 2; + } else { + currentStringLength = 1; + } + if (len + currentStringLength > max) { + return { + length: len, + characters: str.slice(0, i), + }; + } + len += currentStringLength; + } + return { + length: len, + characters: str, + }; + } + + if (type === 'maxlength') { + const length = str.length > max ? max : str.length; + return { + length, + characters: str.slice(0, length), + }; + } + + return { + length: str.length, + characters: str, + }; +}; + +export const chunk = (arr, size) => Array.from({ length: Math.ceil(arr.length / size) }, (v, i) => arr.slice(i * size, i * size + size)); + + +const getPageContext = () => { + const pages = getCurrentPages(); + const page = pages[pages.length - 1]; + return (page).$$basePage || page; +}; + + +const findInstance = (context, pageContext, pureSelector) => { + if (context && context.$refs[pureSelector]) { + return context.$refs[pureSelector]; + } + if (pageContext && pageContext.$refs[pureSelector]) { + return pageContext.$refs[pureSelector]; + } + return null; +}; + +export const getInstance = function (context, selector) { + const pageContext = getPageContext(); + const pureSelector = /^[.#]/.test(selector) ? selector.slice(1) : selector; + const instance = findInstance(context, pageContext, pureSelector); + + if (!instance) { + console.warn('未找到组件,请检查 selector 是否正确'); + return null; + } + return instance; +}; + +export const unitConvert = (value) => { + if (typeof value === 'string') { + if (value.includes('rpx')) { + return (parseInt(value, 10) * (coalesce(systemInfo?.screenWidth, 750))) / 750; + } + return parseInt(value, 10); + } + return coalesce(value, 0); +}; + +export const setIcon = (iconName, icon, defaultIcon) => { + if (icon) { + if (typeof icon === 'string') { + return { + [`${iconName}Name`]: icon, + [`${iconName}Data`]: {}, + }; + } + if (typeof icon === 'object') { + return { + [`${iconName}Name`]: '', + [`${iconName}Data`]: icon, + }; + } + return { + [`${iconName}Name`]: defaultIcon, + [`${iconName}Data`]: {}, + }; + } + return { + [`${iconName}Name`]: '', + [`${iconName}Data`]: {}, + }; +}; + +export const toCamel = str => str.replace(/-(\w)/g, (match, m1) => m1.toUpperCase()); +export const toPascal = name => name + .split('-') + .map(part => part.charAt(0).toUpperCase() + part.slice(1)) + .join(''); + +export function hyphenate(str) { + const hyphenateRE = /\B([A-Z])/g; + return str.replace(hyphenateRE, '-$1').toLowerCase(); +} +export const getCurrentPage = function () { + const pages = getCurrentPages(); + return pages[pages.length - 1]; +}; + +export const uniqueFactory = (compName) => { + let number = 0; + return () => { + const uniqueId = `${prefix}_${compName}_${number}`; + number += 1; + return uniqueId; + }; +}; + +export const calcIcon = (icon, defaultIcon) => { + if (icon && ((isBoolean(icon) && defaultIcon) || isString(icon))) { + return { name: isBoolean(icon) ? defaultIcon : icon }; + } + if (isObject(icon)) { + return icon; + } + return null; +}; + +export const isOverSize = (size, sizeLimit) => { + if (!sizeLimit) return false; + + const base = 1000; + const unitMap = { + B: 1, + KB: base, + MB: base * base, + GB: base * base * base, + }; + const computedSize = typeof sizeLimit === 'number' + ? sizeLimit * base + : sizeLimit?.size * unitMap[coalesce(sizeLimit?.unit, 'KB')]; // 单位 KB + + return size > computedSize; +}; + +export const rpx2px = rpx => Math.floor((systemInfo.windowWidth * rpx) / 750); + +export const nextTick = () => new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, 33); +}); + diff --git a/uni_modules/tdesign-uniapp/components/common/utils.wxs.js b/uni_modules/tdesign-uniapp/components/common/utils.wxs.js new file mode 100644 index 0000000..bab875d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/utils.wxs.js @@ -0,0 +1,139 @@ +import { getRegExp } from './runtime/wxs-polyfill'; +/* utils */ + +/** + * addUnit */ +// 为 css 添加单位 +function addUnit(value) { + const REGEXP = getRegExp('^-?\\d+(.\\d+)?$'); + if (value == null) { + return undefined; + } + return REGEXP.test(`${value}`) ? `${value}px` : value; +} + +function isString(string) { + return typeof string === 'string'; +} + +function isArray(array) { + return Array.isArray(array); +} + +function isObject(x) { + const type = typeof x; + return x !== null && (type === 'object' || type === 'function'); +} + +function isBoolean(value) { + return typeof value === 'boolean'; +} + +const isNoEmptyObj = function (obj) { + return isObject(obj) && JSON.stringify(obj) !== '{}'; +}; + +function includes(arr, value) { + if (!arr || !isArray(arr)) return false; + + let i = 0; + const len = arr.length; + + for (; i < len; i++) { + if (arr[i] === value) return true; + } + return false; +} + +function cls(base, arr) { + const res = [base]; + let i = 0; + for (let size = arr.length; i < size; i++) { + const item = arr[i]; + + if (item && Array.isArray(item)) { + const key = arr[i][0]; + const value = arr[i][1]; + + if (value) { + res.push(`${base}--${key}`); + } + } else if (typeof item === 'string' || typeof item === 'number') { + if (item) { + res.push(`${base}--${item}`); + } + } + } + return res.join(' '); +} + +function getBadgeAriaLabel(options) { + const maxCount = options.maxCount || 99; + if (options.dot) { + return '有新的消息'; + } + if (options.count === '...') { + return '有很多消息'; + } + if (isNaN(options.count)) { + return options.count; + } + const str1 = `有${maxCount}+条消息`; + const str2 = `有${options.count}条消息`; + return Number(options.count) > maxCount ? str1 : str2; +} + +function endsWith(str, endStr) { + return str.slice(-endStr.length) === endStr ? str : str + endStr; +} + +function keys(obj) { + return JSON.stringify(obj) + .replace(getRegExp('{|}|"', 'g'), '') + .split(',') + .map(item => item.split(':')[0]); +} + +function kebabCase(str) { + return str + .replace(getRegExp('[A-Z]', 'g'), ele => `-${ele}`) + .toLowerCase(); +} + +// eslint-disable-next-line no-underscore-dangle +function _style(styles) { + if (isArray(styles)) { + return styles + .filter(item => item != null && item !== '') + .map(item => ((isArray(item) || isObject(item)) ? _style(item) : endsWith(item, ';'))) + .join(' '); + } + + if (isObject(styles)) { + return keys(styles) + .filter(key => styles[key] != null && styles[key] !== '') + .map(key => [kebabCase(key), [styles[key]]].join(':')) + .join(';'); + } + + return styles; +} + +function isValidIconName(str) { + // prettier-ignore + return getRegExp('^[A-Za-z0-9-_]+$').test(str); +} + +export default { + addUnit, + isString, + isArray, + isObject, + isBoolean, + isNoEmptyObj, + includes, + cls, + getBadgeAriaLabel, + _style, + isValidIconName, +}; diff --git a/uni_modules/tdesign-uniapp/components/common/validator.js b/uni_modules/tdesign-uniapp/components/common/validator.js new file mode 100644 index 0000000..8c7bdf7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/validator.js @@ -0,0 +1,38 @@ +export function isFunction(val) { + return typeof val === 'function'; +} + +export const isString = val => typeof val === 'string'; + +export const isNull = value => value === null; + +export const isUndefined = value => value === undefined; + +export function isDef(value) { + return !isUndefined(value) && !isNull(value); +} + +export function isInteger(value) { + return Number.isInteger(value); +} + +export function isNumeric(value) { + return !Number.isNaN(Number(value)); +} + +export function isNumber(value) { + return typeof value === 'number'; +} + +export function isBoolean(value) { + return typeof value === 'boolean'; +} + +export function isObject(x) { + const type = typeof x; + return x !== null && (type === 'object' || type === 'function'); +} + +export function isPlainObject(val) { + return val !== null && typeof val === 'object' && Object.prototype.toString.call(val) === '[object Object]'; +} diff --git a/uni_modules/tdesign-uniapp/components/common/version.js b/uni_modules/tdesign-uniapp/components/common/version.js new file mode 100644 index 0000000..36791b4 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/version.js @@ -0,0 +1,66 @@ +import { getAppBaseInfo } from './wechat'; + +let systemInfo; + +// 获取系统信息 +function getSystemInfo() { + if (systemInfo == null) { + systemInfo = getAppBaseInfo(); + } + return systemInfo; +} + +// 版本号比较, 参考:https://developers.weixin.qq.com/miniprogram/dev/framework/compatibility.html +export function compareVersion(v1, v2) { + v1 = v1.split('.'); + v2 = v2.split('.'); + const len = Math.max(v1.length, v2.length); + + while (v1.length < len) { + v1.push('0'); + } + while (v2.length < len) { + v2.push('0'); + } + + for (let i = 0; i < len; i += 1) { + const num1 = parseInt(v1[i], 10); + const num2 = parseInt(v2[i], 10); + + if (num1 > num2) { + return 1; + } + if (num1 < num2) { + return -1; + } + } + + return 0; +} + +function judgeByVersion(version) { + const currentSDKVersion = getSystemInfo().SDKVersion; + return compareVersion(currentSDKVersion, version) >= 0; +} + +export function canIUseFormFieldButton() { + return judgeByVersion('2.10.3'); +} + +export function canUseVirtualHost() { + let result = false; + + // #ifdef MP-WEIXIN + result = judgeByVersion('2.19.2'); + // #endif + + // #ifdef H5 || APP-PLUS || MP-ALIPAY + result = true; + // #endif + + return result; +} + +export function canUseProxyScrollView() { + return judgeByVersion('2.19.2'); +} diff --git a/uni_modules/tdesign-uniapp/components/common/wechat.js b/uni_modules/tdesign-uniapp/components/common/wechat.js new file mode 100644 index 0000000..0d1df43 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/common/wechat.js @@ -0,0 +1,22 @@ +export const getObserver = (context, selector) => new Promise((resolve) => { + uni + .createIntersectionObserver(context, { + nativeMode: true, + }) + .relativeToViewport() + .observe(selector, (res) => { + resolve(res); + }); +}); + +/** + * 背景:单页模式下, getWindowInfo、getAppBaseInfo、getDeviceInfo 等接口均返回 undefined。 + * 复现路径:分享到朋友圈,再打开单页模式的该页面,uni.getWindowInfo() 等接口返回 undefined + * 代码片段:https://developers.weixin.qq.com/s/mzvZ8FmH7vVW + */ + +export const getWindowInfo = () => (uni.getWindowInfo ? uni.getWindowInfo() || uni.getSystemInfoSync() : uni.getSystemInfoSync()); + +export const getAppBaseInfo = () => (uni.getAppBaseInfo ? uni.getAppBaseInfo() || uni.getSystemInfoSync() : uni.getSystemInfoSync()); + +export const getDeviceInfo = () => (uni.getDeviceInfo ? uni.getDeviceInfo() || uni.getSystemInfoSync() : uni.getSystemInfoSync()); diff --git a/uni_modules/tdesign-uniapp/components/count-down/README.en-US.md b/uni_modules/tdesign-uniapp/components/count-down/README.en-US.md new file mode 100644 index 0000000..8f50543 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/README.en-US.md @@ -0,0 +1,50 @@ +:: BASE_DOC :: + +## API + +### CountDown Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +auto-start | Boolean | true | \- | N +content | String | 'default' | \- | N +format | String | HH:mm:ss | \- | N +millisecond | Boolean | false | \- | N +size | String | 'medium' | options: small/medium/large | N +split-with-unit | Boolean | false | \- | N +theme | String | 'default' | options: default/round/square | N +time | Number | 0 | required | Y + +### CountDown Events + +name | params | description +-- | -- | -- +change | `(time: TimeData)` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/count-down/type.ts)。
`interface TimeData { days: number; hours: number; minutes: number; seconds: number; milliseconds: number }`
+finish | \- | \- + +### CountDown Slots + +name | Description +-- | -- +\- | \- +content | \- + +### CountDown External Classes + +className | Description +-- | -- +t-class | \- +t-class-count | \- +t-class-split | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-countdown-bg-color | @error-color | - +--td-countdown-default-color | @text-color-primary | - +--td-countdown-round-border-radius | @radius-circle | - +--td-countdown-round-color | @text-color-anti | - +--td-countdown-square-border-radius | @radius-small | - diff --git a/uni_modules/tdesign-uniapp/components/count-down/README.md b/uni_modules/tdesign-uniapp/components/count-down/README.md new file mode 100644 index 0000000..36c613c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/README.md @@ -0,0 +1,76 @@ +--- +title: CountDown 倒计时 +description: 用于实时展示倒计时数值。 +spline: data +isComponent: true +--- + + +> CountDown 组件用于实时展示倒计时数值。 +如果需要与站点演示一致的数字字体效果,推荐您到 数字字体章节,将 TCloudNumber 字体下载并将包含的 TCloudNumberVF.ttf 做为 TCloudNumber 字体资源引入到具体项目中使用。 + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TCountDown from '@tdesign/uniapp/count-down/count-down.vue'; +``` + +### 基础倒计时 + +{{ base }} + +### 调整尺寸 + +{{ size }} + +## API + +### CountDown Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +auto-start | Boolean | true | 是否自动开始倒计时 | N +content | String | 'default' | 最终倒计时的展示内容,值为'default'时使用默认的格式,否则使用自定义样式插槽 | N +format | String | HH:mm:ss | 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 | N +millisecond | Boolean | false | 是否开启毫秒级渲染 | N +size | String | 'medium' | 倒计时尺寸。可选项:small/medium/large | N +split-with-unit | Boolean | false | 使用时间单位分割 | N +theme | String | 'default' | 倒计时风格。可选项:default/round/square | N +time | Number | 0 | 必需。倒计时时长,单位毫秒 | Y + +### CountDown Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(time: TimeData)` | 时间变化时触发。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/count-down/type.ts)。
`interface TimeData { days: number; hours: number; minutes: number; seconds: number; milliseconds: number }`
+finish | \- | 倒计时结束时触发 + +### CountDown Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,作用同 `content` 插槽 +content | 自定义 `content` 显示内容 + +### CountDown External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-count | 计数样式类 +t-class-split | 分隔线样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-countdown-bg-color | @error-color | - +--td-countdown-default-color | @text-color-primary | - +--td-countdown-round-border-radius | @radius-circle | - +--td-countdown-round-color | @text-color-anti | - +--td-countdown-square-border-radius | @radius-small | - diff --git a/uni_modules/tdesign-uniapp/components/count-down/computed.js b/uni_modules/tdesign-uniapp/components/count-down/computed.js new file mode 100644 index 0000000..f48dcd3 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/computed.js @@ -0,0 +1,3 @@ +export const format = function (num) { + return num < 10 ? `0${num}` : num; +}; diff --git a/uni_modules/tdesign-uniapp/components/count-down/count-down.css b/uni_modules/tdesign-uniapp/components/count-down/count-down.css new file mode 100644 index 0000000..31c38f8 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/count-down.css @@ -0,0 +1,110 @@ +.t-count-down--small.t-count-down--default { + font-size: var(--td-font-size-base, 28rpx); +} +.t-count-down--small.t-count-down--round > .t-count-down__item { + font-size: var(--td-font-size-s, 24rpx); +} +.t-count-down--small.t-count-down--square > .t-count-down__item { + font-size: var(--td-font-size-s, 24rpx); +} +.t-count-down--small.t-count-down--round > .t-count-down__item, +.t-count-down--small.t-count-down--square > .t-count-down__item { + width: 40rpx; + height: 40rpx; +} +.t-count-down--small.t-count-down--round > .t-count-down__split--dot, +.t-count-down--small.t-count-down--square > .t-count-down__split--dot { + margin: 0 4rpx; + font-size: var(--td-font-size-base, 28rpx); + font-weight: 700; +} +.t-count-down--small.t-count-down--round > .t-count-down__split--text, +.t-count-down--small.t-count-down--square > .t-count-down__split--text { + margin: 0 8rpx; + font-size: var(--td-font-size, 20rpx); +} +.t-count-down--medium.t-count-down--default { + font-size: var(--td-font-size-m, 32rpx); +} +.t-count-down--medium.t-count-down--round > .t-count-down__item { + font-size: var(--td-font-size-base, 28rpx); +} +.t-count-down--medium.t-count-down--square > .t-count-down__item { + font-size: var(--td-font-size-base, 28rpx); +} +.t-count-down--medium.t-count-down--round > .t-count-down__item, +.t-count-down--medium.t-count-down--square > .t-count-down__item { + width: 48rpx; + height: 48rpx; +} +.t-count-down--medium.t-count-down--round > .t-count-down__split--dot, +.t-count-down--medium.t-count-down--square > .t-count-down__split--dot { + margin: 0 6rpx; + font-size: var(--td-font-size-m, 32rpx); + font-weight: 700; +} +.t-count-down--medium.t-count-down--round > .t-count-down__split--text, +.t-count-down--medium.t-count-down--square > .t-count-down__split--text { + margin: 0 10rpx; + font-size: var(--td-font-size-s, 24rpx); +} +.t-count-down--large.t-count-down--default { + font-size: 36rpx; +} +.t-count-down--large.t-count-down--round > .t-count-down__item { + font-size: var(--td-font-size-m, 32rpx); +} +.t-count-down--large.t-count-down--square > .t-count-down__item { + font-size: var(--td-font-size-m, 32rpx); +} +.t-count-down--large.t-count-down--round > .t-count-down__item, +.t-count-down--large.t-count-down--square > .t-count-down__item { + width: 56rpx; + height: 56rpx; +} +.t-count-down--large.t-count-down--round > .t-count-down__split--dot, +.t-count-down--large.t-count-down--square > .t-count-down__split--dot { + margin: 0 12rpx; + font-size: 36rpx; + font-weight: 700; +} +.t-count-down--large.t-count-down--round > .t-count-down__split--text, +.t-count-down--large.t-count-down--square > .t-count-down__split--text { + margin: 0 12rpx; + font-size: var(--td-font-size-base, 28rpx); +} +.t-count-down { + font-family: TCloudNumber, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Hiragino Sans GB, Microsoft YaHei UI, Microsoft YaHei, Source Han Sans CN, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol; + display: flex; +} +.t-count-down .t-count-down__item, +.t-count-down .t-count-down__split { + display: flex; + align-items: center; + justify-content: center; +} +.t-count-down--square > .t-count-down__split--dot, +.t-count-down--round > .t-count-down__split--dot { + color: var(--td-error-color, var(--td-error-color-6, #d54941)); +} +.t-count-down--square > .t-count-down__split--text, +.t-count-down--round > .t-count-down__split--text { + color: var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))); +} +.t-count-down--default { + color: var(--td-countdown-default-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-count-down--square { + color: var(--td-countdown-round-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); +} +.t-count-down--square > .t-count-down__item { + border-radius: var(--td-countdown-square-border-radius, var(--td-radius-small, 6rpx)); + background: var(--td-countdown-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} +.t-count-down--round { + color: var(--td-countdown-round-color, var(--td-text-color-anti, var(--td-font-white-1, #ffffff))); +} +.t-count-down--round > .t-count-down__item { + border-radius: var(--td-countdown-round-border-radius, var(--td-radius-circle, 50%)); + background: var(--td-countdown-bg-color, var(--td-error-color, var(--td-error-color-6, #d54941))); +} diff --git a/uni_modules/tdesign-uniapp/components/count-down/count-down.vue b/uni_modules/tdesign-uniapp/components/count-down/count-down.vue new file mode 100644 index 0000000..7d7846a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/count-down.vue @@ -0,0 +1,166 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/count-down/props.ts b/uni_modules/tdesign-uniapp/components/count-down/props.ts new file mode 100644 index 0000000..f0a2f41 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/props.ts @@ -0,0 +1,62 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdCountDownProps } from './type'; +export default { + /** 是否自动开始倒计时 */ + autoStart: { + type: Boolean, + default: true, + }, + /** 最终倒计时的展示内容,值为'default'时使用默认的格式,否则使用自定义样式插槽 */ + content: { + type: String, + default: 'default' as TdCountDownProps['content'], + }, + /** 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 */ + format: { + type: String, + default: 'HH:mm:ss', + }, + /** 是否开启毫秒级渲染 */ + millisecond: Boolean, + /** 倒计时尺寸 */ + size: { + type: String, + default: 'medium' as TdCountDownProps['size'], + validator(val: TdCountDownProps['size']): boolean { + if (!val) return true; + return ['small', 'medium', 'large'].includes(val); + }, + }, + /** 使用时间单位分割 */ + splitWithUnit: Boolean, + /** 倒计时风格 */ + theme: { + type: String, + default: 'default' as TdCountDownProps['theme'], + validator(val: TdCountDownProps['theme']): boolean { + if (!val) return true; + return ['default', 'round', 'square'].includes(val); + }, + }, + /** 倒计时时长,单位毫秒 */ + time: { + type: Number, + default: 0, + required: true, + }, + /** 时间变化时触发 */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 倒计时结束时触发 */ + onFinish: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/count-down/type.ts b/uni_modules/tdesign-uniapp/components/count-down/type.ts new file mode 100644 index 0000000..4023138 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/type.ts @@ -0,0 +1,64 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdCountDownProps { + /** + * 是否自动开始倒计时 + * @default true + */ + autoStart?: boolean; + /** + * 最终倒计时的展示内容,值为'default'时使用默认的格式,否则使用自定义样式插槽 + * @default 'default' + */ + content?: string; + /** + * 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 + * @default HH:mm:ss + */ + format?: string; + /** + * 是否开启毫秒级渲染 + * @default false + */ + millisecond?: boolean; + /** + * 倒计时尺寸 + * @default 'medium' + */ + size?: 'small' | 'medium' | 'large'; + /** + * 使用时间单位分割 + * @default false + */ + splitWithUnit?: boolean; + /** + * 倒计时风格 + * @default 'default' + */ + theme?: 'default' | 'round' | 'square'; + /** + * 倒计时时长,单位毫秒 + * @default 0 + */ + time: number; + /** + * 时间变化时触发 + */ + onChange?: (time: TimeData) => void; + /** + * 倒计时结束时触发 + */ + onFinish?: () => void; +} + +export interface TimeData { + days: number; + hours: number; + minutes: number; + seconds: number; + milliseconds: number; +} diff --git a/uni_modules/tdesign-uniapp/components/count-down/utils.js b/uni_modules/tdesign-uniapp/components/count-down/utils.js new file mode 100644 index 0000000..334e37a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/count-down/utils.js @@ -0,0 +1,72 @@ +export const TimeDataUnit = { + DD: '天', + HH: '时', + mm: '分', + ss: '秒', + SSS: '毫秒', +}; + +const SECOND = 1000; +const MINUTE = 60 * SECOND; +const HOUR = 60 * MINUTE; +const DAY = 24 * HOUR; + +export const parseTimeData = function (time) { + const days = Math.floor(time / DAY); + const hours = Math.floor((time % DAY) / HOUR); + const minutes = Math.floor((time % HOUR) / MINUTE); + const seconds = Math.floor((time % MINUTE) / SECOND); + const milliseconds = Math.floor(time % SECOND); + return { + DD: days, + HH: hours, + mm: minutes, + ss: seconds, + SSS: milliseconds, + }; +}; + +export const isSameSecond = function (time1, time2) { + return Math.floor(time1 / 1000) === Math.floor(time2 / 1000); +}; + + +/** + * + * @param time 倒计时时间,毫秒单位 + * @param format 倒计时格式化字符串,例如:dd天hh小时mm分ss秒SSS毫秒,hh:mm:ss.SSS,hh:mm:ss + */ +export const parseFormat = function (time, format) { + const obj = { + 'D+': Math.floor(time / 86400000), // 日 + 'H+': Math.floor((time % 86400000) / 3600000), // 小时 + 'm+': Math.floor((time % 3600000) / 60000), // 分 + 's+': Math.floor((time % 60000) / 1000), // 秒 + 'S+': Math.floor(time % 1000), // 毫秒 + }; + const timeList = []; + let timeText = format; + + Object.keys(obj).forEach((prop) => { + if (new RegExp(`(${prop})`).test(timeText)) { + timeText = timeText.replace(RegExp.$1, (match, offset, source) => { + const v = `${(obj)[prop]}`; + let digit = v; + if (match.length > 1) { + digit = (match.replace(new RegExp(match[0], 'g'), '0') + v).substr(v.length); + } + const unit = source.substr(offset + match.length); + const last = timeList[timeList.length - 1]; + if (last) { + const index = last.unit.indexOf(match); + if (index !== -1) { + last.unit = last.unit.substr(0, index); + } + } + timeList.push({ digit, unit, match }); + return digit; + }); + } + }); + return { timeText, timeList }; +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/README.en-US.md b/uni_modules/tdesign-uniapp/components/date-time-picker/README.en-US.md new file mode 100644 index 0000000..4a4718b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/README.en-US.md @@ -0,0 +1,61 @@ +:: BASE_DOC :: + +## API + +### DateTimePicker Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +auto-close | Boolean | false | \- | N +cancel-btn | String | 取消 | \- | N +confirm-btn | String | - | \- | N +custom-locale | String | zh | \- | N +end | String / Number | - | \- | N +filter | Function | - | Typescript:`(type: TimeModeValues, columns: DateTimePickerColumn) => DateTimePickerColumn` `type DateTimePickerColumn = DateTimePickerColumnItem[]` `interface DateTimePickerColumnItem { label: string,value: string}`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +format | String | 'YYYY-MM-DD HH:mm:ss' | \- | N +formatter | Function | - | Typescript:`(option: DateTimePickerColumnItem, columnIndex: number) => DateTimePickerColumnItem` | N +header | Boolean | true | \- | N +mode | String / Array | 'date' | Typescript:`DateTimePickerMode` `type DateTimePickerMode = TimeModeValues \| Array ` `type TimeModeValues = 'year' \| 'month' \| 'date' \| 'hour' \| 'minute' \| 'second' \| 'null'`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +popup-props | Object | {} | popup properties。Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +show-week | Boolean | false | \- | N +start | String / Number | - | \- | N +steps | Object | {} | Typescript:`{ [key in TimeModeValues]?: number }` | N +title | String | - | title of picker | N +use-popup | Boolean | true | \- | N +value | String / Number | - | `v-model:value` is supported。Typescript:`DateValue` `type DateValue = string \| number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +default-value | String / Number | - | uncontrolled property。Typescript:`DateValue` `type DateValue = string \| number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +visible | Boolean | false | \- | N + +### DateTimePicker Events + +name | params | description +-- | -- | -- +cancel | \- | \- +change | `(context: { value: DateValue })` | \- +close | `(context: { trigger: DateTimePickerTriggerSource })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts)。
`type DateTimePickerTriggerSource = 'overlay' \| 'cancel-btn' \| 'confirm-btn'`
+confirm | `(context: { value: DateValue })` | \- +pick | `(context: { value: DateValue })` | \- + +### DateTimePicker Slots + +name | Description +-- | -- +footer | \- +header | \- + +### DateTimePicker External Classes + +className | Description +-- | -- +t-class | \- +t-class-cancel | \- +t-class-confirm | \- +t-class-title | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-data-time-picker-year-width | 128rpx | - diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/README.md b/uni_modules/tdesign-uniapp/components/date-time-picker/README.md new file mode 100644 index 0000000..60d18af --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/README.md @@ -0,0 +1,105 @@ +--- +title: DateTimePicker 时间选择器 +description: 用于选择一个时间点或者一个时间段。 +spline: form +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TDateTimePicker from '@tdesign/uniapp/date-time-picker/date-time-picker.vue'; +``` + +### 组件类型 + +#### 年月日选择器 + +{{ year-month-date }} + +#### 年月选择器 + +{{ year-month }} + +### 时间选择器 + +包括:`时分秒`、`时分`两个示例 + +{{ time }} + +#### 年月日时分秒选择器 + +{{ date-all }} + +### 组件用法 + +#### 调整步数 + +{{ steps }} + +#### 不使用 Popup + +{{ without-popup }} + +## API + +### DateTimePicker Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +auto-close | Boolean | false | 自动关闭;在确认、取消、点击遮罩层自动关闭,不需要手动设置 visible | N +cancel-btn | String | 取消 | 取消按钮文字 | N +confirm-btn | String | - | 确定按钮文字 | N +custom-locale | String | zh | 组件国际化语言,目前支持: 简体中文(zh)、(tc)、英文(en)、日语(ja)、韩语(ko)、俄语(ru)等六种语言 | N +end | String / Number | - | 选择器的最大可选时间,默认为当前时间+10年 | N +filter | Function | - | 列选项过滤函数,支持自定义列内容。(type 值可为: year, month, date, hour, minute, second)。TS 类型:`(type: TimeModeValues, columns: DateTimePickerColumn) => DateTimePickerColumn` `type DateTimePickerColumn = DateTimePickerColumnItem[]` `interface DateTimePickerColumnItem { label: string,value: string}`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +format | String | 'YYYY-MM-DD HH:mm:ss' | 用于格式化 pick、change、confirm 事件返回的值,[详细文档](https://day.js.org/docs/en/display/format) | N +formatter | Function | - | 格式化标签。TS 类型:`(option: DateTimePickerColumnItem, columnIndex: number) => DateTimePickerColumnItem` | N +header | Boolean | true | 头部内容。值为 true 显示空白头部,值为 false 不显示任何内容 | N +mode | String / Array | 'date' | year = 年;month = 年月;date = 年月日;hour = 年月日时; minute = 年月日时分;当类型为数组时,第一个值控制年月日,第二个值控制时分秒。TS 类型:`DateTimePickerMode` `type DateTimePickerMode = TimeModeValues \| Array ` `type TimeModeValues = 'year' \| 'month' \| 'date' \| 'hour' \| 'minute' \| 'second' \| 'null'`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +popup-props | Object | {} | 透传 Popup 组件全部属性。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +show-week | Boolean | false | 是否在日期旁边显示周几(如周一,周二,周日等) | N +start | String / Number | - | 选择器的最小可选时间,默认为当前时间-10年 | N +steps | Object | {} | 时间间隔步数,示例:`{ minute: 5 }`。TS 类型:`{ [key in TimeModeValues]?: number }` | N +title | String | - | 标题 | N +use-popup | Boolean | true | 是否使用弹出层包裹 | N +value | String / Number | - | 选中值。支持语法糖 `v-model:value`。TS 类型:`DateValue` `type DateValue = string \| number`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +default-value | String / Number | - | 选中值。非受控属性。TS 类型:`DateValue` `type DateValue = string \| number`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts) | N +visible | Boolean | false | 是否显示 | N + +### DateTimePicker Events + +名称 | 参数 | 描述 +-- | -- | -- +cancel | \- | 取消按钮点击时触发 +change | `(context: { value: DateValue })` | 确认按钮点击时触发 +close | `(context: { trigger: DateTimePickerTriggerSource })` | 关闭时触发。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/date-time-picker/type.ts)。
`type DateTimePickerTriggerSource = 'overlay' \| 'cancel-btn' \| 'confirm-btn'`
+confirm | `(context: { value: DateValue })` | 确认按钮点击时触发 +pick | `(context: { value: DateValue })` | 选中值发生变化时触发 + +### DateTimePicker Slots + +名称 | 描述 +-- | -- +footer | 底部内容 +header | 自定义 `header` 显示内容 + +### DateTimePicker External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-cancel | 取消样式类 +t-class-confirm | 确认样式类 +t-class-title | 标题样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-data-time-picker-year-width | 128rpx | - diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/date-time-picker.css b/uni_modules/tdesign-uniapp/components/date-time-picker/date-time-picker.css new file mode 100644 index 0000000..8c605be --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/date-time-picker.css @@ -0,0 +1,4 @@ +.t-date-time-picker__item--roomly { + width: var(--td-data-time-picker-year-width, 128rpx); + flex: 0 0 var(--td-data-time-picker-year-width, 128rpx); +} diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/date-time-picker.vue b/uni_modules/tdesign-uniapp/components/date-time-picker/date-time-picker.vue new file mode 100644 index 0000000..6ba453b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/date-time-picker.vue @@ -0,0 +1,570 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/dayjs.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/dayjs.js new file mode 100644 index 0000000..7092915 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/dayjs.js @@ -0,0 +1,81 @@ +// dayjs 语言包 +import * as enLocale from '../../npm/dayjs/esm/locale/en'; +import * as zhLocale from '../../npm/dayjs/esm/locale/zh-cn'; +import * as tcLocale from '../../npm/dayjs/esm/locale/zh-tw'; // 繁体 +import * as koLocale from '../../npm/dayjs/esm/locale/ko'; // 韩语 +import * as jaLocale from '../../npm/dayjs/esm/locale/ja'; // 日语 +import * as ruLocale from '../../npm/dayjs/esm/locale/ru'; // 俄语 + +// 本地语言包 +import en from './en'; +import zh from './zh'; +import tc from './tc'; +import ko from './ko'; +import ja from './ja'; +import ru from './ru'; + +export default { + default: { + key: 'zh-cn', + label: '简体中文', + locale: zhLocale, + i18n: zh, + }, + en: { + key: 'en', + label: 'English', + locale: enLocale, + i18n: en, + }, + 'zh-cn': { + key: 'zh-cn', + label: '简体中文', + locale: zhLocale, + i18n: zh, + }, + // 容错处理 + zh: { + key: 'zh-cn', + label: '简体中文', + locale: zhLocale, + i18n: zh, + }, + 'zh-tw': { + key: 'zh-tw', + label: '繁体中文', + locale: tcLocale, + i18n: tc, + }, + // 容错处理 + tc: { + key: 'zh-tw', + label: '繁体中文', + locale: tcLocale, + i18n: tc, + }, + ko: { + key: 'ko', + label: '한국어', + locale: koLocale, + i18n: ko, + }, + // 容错处理 + kr: { + key: 'ko', + label: '한국어', + locale: koLocale, + i18n: ko, + }, + ja: { + key: 'ja', + label: '日本語', + locale: jaLocale, + i18n: ja, + }, + ru: { + key: 'ru', + label: 'русский', + locale: ruLocale, + i18n: ru, + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/en.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/en.js new file mode 100644 index 0000000..59f5a03 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/en.js @@ -0,0 +1,12 @@ +export default { + year: '', + month: '', + date: '', + hour: '', + minute: '', + second: '', + am: 'AM', + pm: 'PM', + confirm: 'confirm', + cancel: 'cancel', +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ja.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ja.js new file mode 100644 index 0000000..0756974 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ja.js @@ -0,0 +1,12 @@ +export default { + year: '年', + month: '月', + date: '日', + hour: '時', + minute: '分', + second: '秒', + am: '午前', + pm: '午後', + confirm: '確認', + cancel: 'キャンセル', +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ko.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ko.js new file mode 100644 index 0000000..76a3953 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ko.js @@ -0,0 +1,12 @@ +export default { + year: '년', + month: '월', + date: '일', + hour: '시', + minute: '분', + second: '초', + am: '오전', + pm: '오후', + confirm: '확인', + cancel: '취소', +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ru.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ru.js new file mode 100644 index 0000000..23c707c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/ru.js @@ -0,0 +1,12 @@ +export default { + year: '', + month: '', + date: '', + hour: '', + minute: '', + second: '', + am: 'до полудня', + pm: 'после полудня', + confirm: 'подтвердить', + cancel: 'отменить', +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/tc.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/tc.js new file mode 100644 index 0000000..58e7cbf --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/tc.js @@ -0,0 +1,12 @@ +export default { + year: '年', + month: '月', + date: '日', + hour: '時', + minute: '分', + second: '秒', + am: '上午', + pm: '下午', + confirm: '確定', + cancel: '取消', +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/locale/zh.js b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/zh.js new file mode 100644 index 0000000..6656674 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/locale/zh.js @@ -0,0 +1,12 @@ +export default { + year: '年', + month: '月', + date: '日', + hour: '时', + minute: '分', + second: '秒', + am: '上午', + pm: '下午', + confirm: '确定', + cancel: '取消', +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/props.ts b/uni_modules/tdesign-uniapp/components/date-time-picker/props.ts new file mode 100644 index 0000000..c5eb351 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/props.ts @@ -0,0 +1,114 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdDateTimePickerProps } from './type'; +export default { + /** 自动关闭;在确认、取消、点击遮罩层自动关闭,不需要手动设置 visible */ + autoClose: Boolean, + /** 取消按钮文字 */ + cancelBtn: { + type: String, + default: '取消', + }, + /** 确定按钮文字 */ + confirmBtn: { + type: String, + default: '', + }, + /** 组件国际化语言,目前支持: 简体中文(zh)、(tc)、英文(en)、日语(ja)、韩语(ko)、俄语(ru)等六种语言 */ + customLocale: { + type: String, + default: 'zh', + }, + /** 选择器的最大可选时间,默认为当前时间+10年 */ + end: { + type: [String, Number], + }, + /** 列选项过滤函数,支持自定义列内容。(type 值可为: year, month, date, hour, minute, second) */ + filter: { + type: Function, + }, + /** 用于格式化 pick、change、confirm 事件返回的值,[详细文档](https://day.js.org/docs/en/display/format) */ + format: { + type: String, + default: 'YYYY-MM-DD HH:mm:ss', + }, + /** 格式化标签 */ + formatter: { + type: Function, + }, + /** 头部内容。值为 true 显示空白头部,值为 false 不显示任何内容 */ + header: { + type: Boolean, + default: true as TdDateTimePickerProps['header'], + }, + /** year = 年;month = 年月;date = 年月日;hour = 年月日时; minute = 年月日时分;当类型为数组时,第一个值控制年月日,第二个值控制时分秒 */ + mode: { + type: [String, Array], + default: 'date' as TdDateTimePickerProps['mode'], + }, + /** 透传 Popup 组件全部属性 */ + popupProps: { + type: Object, + default: () => ({}), + }, + /** 是否在日期旁边显示周几(如周一,周二,周日等) */ + showWeek: Boolean, + /** 选择器的最小可选时间,默认为当前时间-10年 */ + start: { + type: [String, Number], + }, + /** 时间间隔步数,示例:`{ minute: 5 }` */ + steps: { + type: Object, + default: () => ({}), + }, + /** 标题 */ + title: { + type: String, + default: '', + }, + /** 是否使用弹出层包裹 */ + usePopup: { + type: Boolean, + default: true, + }, + /** 选中值 */ + value: { + type: [String, Number], + }, + /** 选中值,非受控属性 */ + defaultValue: { + type: [String, Number], + }, + /** 是否显示 */ + visible: Boolean, + /** 取消按钮点击时触发 */ + onCancel: { + type: Function, + default: () => ({}), + }, + /** 确认按钮点击时触发 */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 关闭时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 确认按钮点击时触发 */ + onConfirm: { + type: Function, + default: () => ({}), + }, + /** 选中值发生变化时触发 */ + onPick: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/date-time-picker/type.ts b/uni_modules/tdesign-uniapp/components/date-time-picker/type.ts new file mode 100644 index 0000000..dda2a98 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/date-time-picker/type.ts @@ -0,0 +1,134 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdPopupProps as PopupProps } from '../popup/type'; + +export interface TdDateTimePickerProps { + /** + * 自动关闭;在确认、取消、点击遮罩层自动关闭,不需要手动设置 visible + * @default false + */ + autoClose?: boolean; + /** + * 取消按钮文字 + * @default 取消 + */ + cancelBtn?: string; + /** + * 确定按钮文字 + * @default '' + */ + confirmBtn?: string; + /** + * 组件国际化语言,目前支持: 简体中文(zh)、(tc)、英文(en)、日语(ja)、韩语(ko)、俄语(ru)等六种语言 + * @default zh + */ + customLocale?: string; + /** + * 选择器的最大可选时间,默认为当前时间+10年 + */ + end?: string | number; + /** + * 列选项过滤函数,支持自定义列内容。(type 值可为: year, month, date, hour, minute, second) + */ + filter?: (type: TimeModeValues, columns: DateTimePickerColumn) => DateTimePickerColumn; + /** + * 用于格式化 pick、change、confirm 事件返回的值,[详细文档](https://day.js.org/docs/en/display/format) + * @default 'YYYY-MM-DD HH:mm:ss' + */ + format?: string; + /** + * 格式化标签 + */ + formatter?: (option: DateTimePickerColumnItem, columnIndex: number) => DateTimePickerColumnItem; + /** + * 头部内容。值为 true 显示空白头部,值为 false 不显示任何内容 + * @default true + */ + header?: boolean; + /** + * year = 年;month = 年月;date = 年月日;hour = 年月日时; minute = 年月日时分;当类型为数组时,第一个值控制年月日,第二个值控制时分秒 + * @default 'date' + */ + mode?: DateTimePickerMode; + /** + * 透传 Popup 组件全部属性 + * @default {} + */ + popupProps?: PopupProps; + /** + * 是否在日期旁边显示周几(如周一,周二,周日等) + * @default false + */ + showWeek?: boolean; + /** + * 选择器的最小可选时间,默认为当前时间-10年 + */ + start?: string | number; + /** + * 时间间隔步数,示例:`{ minute: 5 }` + * @default {} + */ + steps?: { [key in TimeModeValues]?: number }; + /** + * 标题 + * @default '' + */ + title?: string; + /** + * 是否使用弹出层包裹 + * @default true + */ + usePopup?: boolean; + /** + * 选中值 + */ + value?: DateValue; + /** + * 选中值,非受控属性 + */ + defaultValue?: DateValue; + /** + * 是否显示 + * @default false + */ + visible?: boolean; + /** + * 取消按钮点击时触发 + */ + onCancel?: () => void; + /** + * 确认按钮点击时触发 + */ + onChange?: (context: { value: DateValue }) => void; + /** + * 关闭时触发 + */ + onClose?: (context: { trigger: DateTimePickerTriggerSource }) => void; + /** + * 确认按钮点击时触发 + */ + onConfirm?: (context: { value: DateValue }) => void; + /** + * 选中值发生变化时触发 + */ + onPick?: (context: { value: DateValue }) => void; +} + +export type DateTimePickerColumn = DateTimePickerColumnItem[]; + +export interface DateTimePickerColumnItem { + label: string; + value: string; +} + +export type DateTimePickerMode = TimeModeValues | Array; + +export type TimeModeValues = 'year' | 'month' | 'date' | 'hour' | 'minute' | 'second' | 'null'; + +export type DateValue = string | number; + +export type DateTimePickerTriggerSource = 'overlay' | 'cancel-btn' | 'confirm-btn'; diff --git a/uni_modules/tdesign-uniapp/components/demo-header/demo-header.vue b/uni_modules/tdesign-uniapp/components/demo-header/demo-header.vue new file mode 100644 index 0000000..e1cf5dc --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/demo-header/demo-header.vue @@ -0,0 +1,60 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/demo-header/index.css b/uni_modules/tdesign-uniapp/components/demo-header/index.css new file mode 100644 index 0000000..e69de29 diff --git a/uni_modules/tdesign-uniapp/components/demo-navbar/demo-navbar.vue b/uni_modules/tdesign-uniapp/components/demo-navbar/demo-navbar.vue new file mode 100644 index 0000000..19d7753 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/demo-navbar/demo-navbar.vue @@ -0,0 +1,42 @@ + + diff --git a/uni_modules/tdesign-uniapp/components/demo/demo.vue b/uni_modules/tdesign-uniapp/components/demo/demo.vue new file mode 100644 index 0000000..407c45e --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/demo/demo.vue @@ -0,0 +1,97 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/demo/index.css b/uni_modules/tdesign-uniapp/components/demo/index.css new file mode 100644 index 0000000..5365e6f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/demo/index.css @@ -0,0 +1,46 @@ +.demo-block { + margin: var(--td-spacer-4, 64rpx) 0 0; +} +.demo-block__header { + color: var(--bg-color-demo-title); + margin: 0 var(--td-spacer-2, 32rpx); +} +.demo-block__header-title { + font-weight: 700; + font-size: 36rpx; + line-height: 52rpx; +} +.demo-block__header-desc { + margin-top: var(--td-spacer, 16rpx); + font-size: var(--td-font-size-base, 28rpx); + white-space: pre-line; + color: var(--bg-color-demo-desc); + line-height: 22px; +} +.demo-block__oper { + margin-top: var(--td-spacer, 16rpx); +} +.demo-block__oper-subtitle { + font-size: var(--td-font-size-s, 24rpx); + margin-bottom: var(--td-spacer-2, 32rpx); + opacity: 0.4; +} +.demo-block__oper-btn { + margin: 0 0 var(--td-spacer-2, 32rpx) 0; + height: 96rpx; +} +.demo-block__slot { + margin-top: var(--td-spacer-2, 32rpx); +} +.demo-block__slot.with-padding { + margin-top: var(--td-spacer-2, 32rpx); + margin-left: var(--td-spacer-2, 32rpx); + margin-right: var(--td-spacer-2, 32rpx); + margin-bottom: 0; +} +.demo-block_notitle { + margin-top: 0px; +} +.demo-block_notitle .demo-block_subtitle { + margin-top: var(--td-spacer-3, 48rpx); +} diff --git a/uni_modules/tdesign-uniapp/components/dialog/README.en-US.md b/uni_modules/tdesign-uniapp/components/dialog/README.en-US.md new file mode 100644 index 0000000..4ca4aea --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/README.en-US.md @@ -0,0 +1,69 @@ +:: BASE_DOC :: + +## API + +### Dialog Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +actions | Array | - | Typescript:`Array`,[Button API Documents](./button?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +button-layout | String | horizontal | options: horizontal/vertical | N +cancel-btn | String / Object | - | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +close-btn | Boolean / Object | false | Typescript:`boolean \| ButtonProps \| null`,[Button API Documents](./button?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +close-on-overlay-click | Boolean | false | \- | N +confirm-btn | String / Object | - | \- | N +content | String | - | \- | N +overlay-props | Object | {} | Typescript:`OverlayProps`,[Overlay API Documents](./overlay?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +prevent-scroll-through | Boolean | true | \- | N +show-overlay | Boolean | true | \- | N +title | String | - | \- | N +using-custom-navbar | Boolean | false | \- | N +visible | Boolean | - | \- | N +z-index | Number | 11500 | \- | N + +### Dialog Events + +name | params | description +-- | -- | -- +action | `(context: { index: number })` | \- +cancel | `(context: { e: MouseEvent })` | \- +close | `(context: { trigger: DialogEventSource })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts)。
`type DialogEventSource = 'cancel' \| 'overlay' \| 'close-btn'`
+confirm | `(context: { e: MouseEvent })` | \- +overlay-click | `(context: { e: MouseEvent })` | \- + +### Dialog Slots + +name | Description +-- | -- +actions | \- +cancel-btn | \- +confirm-btn | \- +content | \- +middle | \- +title | \- +top | \- + +### Dialog External Classes + +className | Description +-- | -- +t-class | \- +t-class-action | \- +t-class-cancel | \- +t-class-confirm | \- +t-class-content | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-dialog-body-max-height | 912rpx | - +--td-dialog-border-radius | @radius-extraLarge | - +--td-dialog-close-color | @text-color-placeholder | - +--td-dialog-content-color | @text-color-secondary | - +--td-dialog-content-font | @font-body-large | - +--td-dialog-title-color | @text-color-primary | - +--td-dialog-title-font | @font-title-large | - +--td-dialog-width | 622rpx | - diff --git a/uni_modules/tdesign-uniapp/components/dialog/README.md b/uni_modules/tdesign-uniapp/components/dialog/README.md new file mode 100644 index 0000000..be102f3 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/README.md @@ -0,0 +1,120 @@ +--- +title: Dialog 对话框 +description: 用于显示重要提示或请求用户进行重要操作,一种打断当前操作的模态视图。 +spline: message +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TDialog from '@tdesign/uniapp/dialog/dialog.vue'; +``` + +### 组件类型 + +按钮的样式,默认使用 `variant = text`,如果任意按钮改变了 `variant`,那么全部按钮都改变成这个。 + +#### 反馈类对话框 + +{{ base }} + +> 使用这种方式,对话框的 `visible` 是受控的,需要手动设置额 `visible` 为 `false` 才会关闭对话框。 + +#### 确认类对话框 + +{{ confirm }} + +#### 输入类对话框 + +{{ with-input }} + +#### 带图片对话框 + +{{ with-image }} + +### 组件状态 + +{{ status }} + +### 组件用法 +#### 命令调用 + +{{ command }} + +#### 开放能力按钮 + +当传入的按钮类型为对象时,整个对象都将透传至 `t-button`,因此按钮可以直接使用开放能力 + +{{ button }} + +## API + +### Dialog Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +actions | Array | - | 操作栏。TS 类型:`Array`,[Button API Documents](./button?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +button-layout | String | horizontal | 多按钮排列方式。可选项:horizontal/vertical | N +cancel-btn | String / Object | - | 取消按钮,可自定义。值为 null 则不显示取消按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 Slot 自定义按钮时,需自行控制取消事件。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +close-btn | Boolean / Object | false | 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;使用 Object 时透传至图标组件。TS 类型:`boolean \| ButtonProps \| null`,[Button API Documents](./button?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +close-on-overlay-click | Boolean | false | 点击蒙层时是否触发关闭事件 | N +confirm-btn | String / Object | - | 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 Slot 自定义按钮时,需自行控制确认事件 | N +content | String | - | 内容 | N +overlay-props | Object | {} | 透传至 Overlay 组件。TS 类型:`OverlayProps`,[Overlay API Documents](./overlay?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts) | N +prevent-scroll-through | Boolean | true | 防止滚动穿透 | N +show-overlay | Boolean | true | 是否显示遮罩层 | N +title | String | - | 标题 | N +using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N +visible | Boolean | - | 控制对话框是否显示 | N +z-index | Number | 11500 | 对话框层级,Web 侧样式默认为 2500,移动端样式默认 2500,小程序样式默认为 11500 | N + +### Dialog Events + +名称 | 参数 | 描述 +-- | -- | -- +action | `(context: { index: number })` | 点击多按钮中的其中一个时触发 +cancel | `(context: { e: MouseEvent })` | 如果“取消”按钮存在,则点击“取消”按钮时触发,同时触发关闭事件 +close | `(context: { trigger: DialogEventSource })` | 关闭事件,点击 取消按钮 或 点击蒙层 时触发。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dialog/type.ts)。
`type DialogEventSource = 'cancel' \| 'overlay' \| 'close-btn'`
+confirm | `(context: { e: MouseEvent })` | 如果“确认”按钮存在,则点击“确认”按钮时触发 +overlay-click | `(context: { e: MouseEvent })` | 如果蒙层存在,点击蒙层时触发 + +### Dialog Slots + +名称 | 描述 +-- | -- +actions | 自定义 `actions` 显示内容 +cancel-btn | 自定义 `cancel-btn` 显示内容 +confirm-btn | 自定义 `confirm-btn` 显示内容 +content | 自定义 `content` 显示内容 +middle | 中间自定义内容 +title | 自定义 `title` 显示内容 +top | 顶部自定义内容 + +### Dialog External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-action | 操作样式类 +t-class-cancel | 取消样式类 +t-class-confirm | 确认样式类 +t-class-content | 内容样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-dialog-body-max-height | 912rpx | - +--td-dialog-border-radius | @radius-extraLarge | - +--td-dialog-close-color | @text-color-placeholder | - +--td-dialog-content-color | @text-color-secondary | - +--td-dialog-content-font | @font-body-large | - +--td-dialog-title-color | @text-color-primary | - +--td-dialog-title-font | @font-title-large | - +--td-dialog-width | 622rpx | - diff --git a/uni_modules/tdesign-uniapp/components/dialog/computed.js b/uni_modules/tdesign-uniapp/components/dialog/computed.js new file mode 100644 index 0000000..777c714 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/computed.js @@ -0,0 +1,15 @@ +import { coalesce } from '../common/utils'; + +export const getTypeof = function (obj) { + return typeof obj; +}; + +export const getActionClass = function (prefix, buttonLayout, actionItem, tClassAction) { + const cls = [`${prefix}__button`, `${prefix}__button--action`]; + + if (buttonLayout) { + cls.push(`${prefix}__button--${buttonLayout}`); + } + + return `${cls.join(' ')} ${coalesce(actionItem.tClass, tClassAction)}`; +}; diff --git a/uni_modules/tdesign-uniapp/components/dialog/dialog.css b/uni_modules/tdesign-uniapp/components/dialog/dialog.css new file mode 100644 index 0000000..dd676e4 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/dialog.css @@ -0,0 +1,102 @@ +.t-dialog { + overflow: hidden; + width: var(--td-dialog-width, 622rpx); + border-radius: var(--td-dialog-border-radius, var(--td-radius-extraLarge, 24rpx)); + background-color: var(--td-bg-color-container, var(--td-font-white-1, #ffffff)); +} +.t-dialog__wrapper { + --td-popup-border-radius: var(--td-dialog-border-radius, var(--td-radius-extraLarge, 24rpx)); +} +.t-dialog__close-btn { + position: absolute; + top: var(--td-spacer, 16rpx); + right: var(--td-spacer, 16rpx); + color: var(--td-dialog-close-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + z-index: 1; + /* skyline适配新增 */ +} +.t-dialog__content { + padding-top: var(--td-spacer-3, 48rpx); + padding-right: var(--td-spacer-3, 48rpx); + padding-bottom: 0; + padding-left: var(--td-spacer-3, 48rpx); + max-height: var(--td-dialog-body-max-height, 912rpx); + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + font-size: var(--td-font-size-m, 32rpx); +} +.t-dialog__content:empty { + display: none; +} +.t-dialog__header { + text-align: center; + font: var(--td-dialog-title-font, var(--td-font-title-large, 600 36rpx / 52rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-dialog-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-dialog__header + .t-dialog__body { + margin-top: var(--td-spacer, 16rpx); +} +.t-dialog__body { + overflow-y: scroll; + text-align: center; + -webkit-overflow-scrolling: touch; + font: var(--td-dialog-content-font, var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-dialog-content-color, var(--td-text-color-secondary, var(--td-font-gray-2, rgba(0, 0, 0, 0.6)))); +} +.t-dialog__body-text { + word-wrap: break-word; +} +.t-dialog__body--left { + text-align: left; +} +.t-dialog__body--right { + text-align: right; +} +.t-dialog__footer { + display: flex; + padding: var(--td-spacer-3, 48rpx); +} +.t-dialog__footer--column { + flex-flow: column-reverse; +} +.t-dialog__footer--column :deep(.t-dialog__button) { + width: 100%; +} +.t-dialog__footer--full { + padding: var(--td-spacer-4, 64rpx) 0 0; +} +:deep(.t-dialog__button) { + position: relative; + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +:deep(.t-dialog__button)--horizontal:not(:first-child) { + margin-left: var(--td-spacer-1, 24rpx); +} +:deep(.t-dialog__button)--vertical:not(:first-child) { + margin-bottom: var(--td-spacer-1, 24rpx); +} +:deep(.t-dialog__button)--text { + flex: 1; + --td-button-border-radius: 0; + --td-button-medium-height: 112rpx; + border-radius: 0; +} +:deep(.t-dialog__button)--text:before { + content: ' '; + position: absolute; + box-sizing: border-box; + top: 0; + left: 0; + border-top: 1px solid var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7)); + border-left: 1px solid var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7)); + transform: scale(0.5); + transform-origin: 0 0; + width: 200%; + height: 200%; + border-radius: 0; +} diff --git a/uni_modules/tdesign-uniapp/components/dialog/dialog.vue b/uni_modules/tdesign-uniapp/components/dialog/dialog.vue new file mode 100644 index 0000000..8b741c7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/dialog.vue @@ -0,0 +1,422 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/dialog/index.d.ts b/uni_modules/tdesign-uniapp/components/dialog/index.d.ts new file mode 100644 index 0000000..28a3150 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/index.d.ts @@ -0,0 +1,42 @@ +declare type Context = any; +interface DialogAlertOptionsType { + context?: Context; + selector?: string; + title?: string; + content?: string; + zIndex?: number; + asyncClose?: boolean; + confirmButtonText?: string; + textAlign?: string; + cancelBtn?: string | object; + confirmBtn?: string | object; + showOverlay?: boolean; + closeOnOverlayClick?: boolean; + preventScrollThrough?: boolean; +} +interface DialogConfirmOptionsType extends DialogAlertOptionsType { + cancelButtonText?: string; +} +interface Action { + content: string; + theme?: 'default' | 'primary' | 'danger' | 'light'; +} +interface DialogActionOptionsType { + context?: Context; + selector?: string; + title?: string; + content: string; + zIndex?: number; + asyncClose?: boolean; + actions?: Action[]; + buttonLayout?: 'vertical' | 'horizontal'; +} +declare const Handler: { + alert(options: DialogAlertOptionsType): Promise; + confirm(options: DialogConfirmOptionsType): Promise; + close(options?: DialogConfirmOptionsType): Promise; + action(options: DialogActionOptionsType): Promise<{ + index: number; + }>; +}; +export default Handler; diff --git a/uni_modules/tdesign-uniapp/components/dialog/index.js b/uni_modules/tdesign-uniapp/components/dialog/index.js new file mode 100644 index 0000000..ffe6afb --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/index.js @@ -0,0 +1,90 @@ +import props from './props'; +import { getInstance } from '../common/utils'; + + +const defaultOptions = { + actions: [], + buttonLayout: props.buttonLayout.default, + cancelBtn: props.cancelBtn.default, + closeOnOverlayClick: props.closeOnOverlayClick.default, + confirmBtn: props.confirmBtn.value, + content: '', + preventScrollThrough: props.preventScrollThrough.default, + showOverlay: props.showOverlay.default, + title: '', + visible: props.visible.default, +}; + +export default { + alert(options) { + const { context, selector = '#t-dialog', ...otherOptions } = { ...options }; + const instance = getInstance(context, selector); + if (!instance) return Promise.reject(); + + return new Promise((resolve) => { + const mergedOptions = { + ...defaultOptions, + ...instance.properties, + ...otherOptions, + }; + instance.setData({ + cancelBtn: '', + ...mergedOptions, + visible: true, + }); + instance._onConfirm = resolve; + }); + }, + confirm(options) { + const { context, selector = '#t-dialog', ...otherOptions } = { ...options }; + const instance = getInstance(context, selector); + if (!instance) return Promise.reject(); + + return new Promise((resolve, reject) => { + const mergedOptions = { + ...defaultOptions, + ...instance.properties, + ...otherOptions, + }; + instance.setData({ + ...mergedOptions, + visible: true, + }); + instance._onConfirm = resolve; + instance._onCancel = reject; + }); + }, + close(options) { + const { context, selector = '#t-dialog' } = { ...options }; + const instance = getInstance(context, selector); + if (instance) { + instance.close(); + return Promise.resolve(); + } + return Promise.reject(); + }, + action(options) { + const { context, selector = '#t-dialog', ...otherOptions } = { ...options }; + const instance = getInstance(context, selector); + if (!instance) return Promise.reject(); + const { buttonLayout = 'vertical', actions = instance.properties.actions } = options; + const maxLengthSuggestion = buttonLayout === 'vertical' ? 7 : 3; + if (!actions || (typeof actions === 'object' && (actions.length === 0 || actions.length > maxLengthSuggestion))) { + console.warn(`action 数量建议控制在1至${maxLengthSuggestion}个`); + } + + return new Promise((resolve) => { + const mergedOptions = { + ...defaultOptions, + ...instance.properties, + ...otherOptions, + }; + instance.setData({ + ...mergedOptions, + buttonLayout, + visible: true, + }); + instance._onAction = resolve; + }); + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/dialog/props.ts b/uni_modules/tdesign-uniapp/components/dialog/props.ts new file mode 100644 index 0000000..d822c4d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/props.ts @@ -0,0 +1,94 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdDialogProps } from './type'; +export default { + /** 操作栏 */ + actions: { + type: Array, + }, + /** 多按钮排列方式 */ + buttonLayout: { + type: String, + default: 'horizontal' as TdDialogProps['buttonLayout'], + validator(val: TdDialogProps['buttonLayout']): boolean { + if (!val) return true; + return ['horizontal', 'vertical'].includes(val); + }, + }, + /** 取消按钮,可自定义。值为 null 则不显示取消按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 Slot 自定义按钮时,需自行控制取消事件 */ + cancelBtn: { + type: [String, Object], + }, + /** 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;使用 Object 时透传至图标组件 */ + closeBtn: { + type: [Boolean, Object], + default: false as TdDialogProps['closeBtn'], + }, + /** 点击蒙层时是否触发关闭事件 */ + closeOnOverlayClick: Boolean, + /** 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 Slot 自定义按钮时,需自行控制确认事件 */ + confirmBtn: { + type: [String, Object], + }, + /** 内容 */ + content: { + type: String, + }, + /** 透传至 Overlay 组件 */ + overlayProps: { + type: Object, + default: () => ({}), + }, + /** 防止滚动穿透 */ + preventScrollThrough: { + type: Boolean, + default: true, + }, + /** 是否显示遮罩层 */ + showOverlay: { + type: Boolean, + default: true, + }, + /** 标题 */ + title: { + type: String, + }, + /** 是否使用了自定义导航栏 */ + usingCustomNavbar: Boolean, + /** 控制对话框是否显示 */ + visible: Boolean, + /** 对话框层级,Web 侧样式默认为 2500,移动端样式默认 2500,小程序样式默认为 11500 */ + zIndex: { + type: Number, + default: 11500, + }, + /** 点击多按钮中的其中一个时触发 */ + onAction: { + type: Function, + default: () => ({}), + }, + /** 如果“取消”按钮存在,则点击“取消”按钮时触发,同时触发关闭事件 */ + onCancel: { + type: Function, + default: () => ({}), + }, + /** 关闭事件,点击 取消按钮 或 点击蒙层 时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 如果“确认”按钮存在,则点击“确认”按钮时触发 */ + onConfirm: { + type: Function, + default: () => ({}), + }, + /** 如果蒙层存在,点击蒙层时触发 */ + onOverlayClick: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/dialog/type.ts b/uni_modules/tdesign-uniapp/components/dialog/type.ts new file mode 100644 index 0000000..f07a26e --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dialog/type.ts @@ -0,0 +1,97 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdButtonProps as ButtonProps } from '../button/type'; +import type { TdOverlayProps as OverlayProps } from '../overlay/type'; + +export interface TdDialogProps { + /** + * 操作栏 + */ + actions?: Array; + /** + * 多按钮排列方式 + * @default horizontal + */ + buttonLayout?: 'horizontal' | 'vertical'; + /** + * 取消按钮,可自定义。值为 null 则不显示取消按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 Slot 自定义按钮时,需自行控制取消事件 + */ + cancelBtn?: string | ButtonProps | null; + /** + * 是否展示关闭按钮,值为 `true` 显示默认关闭按钮;值为 `false` 则不显示关闭按钮;使用 Object 时透传至图标组件 + * @default false + */ + closeBtn?: boolean | ButtonProps | null; + /** + * 点击蒙层时是否触发关闭事件 + * @default false + */ + closeOnOverlayClick?: boolean; + /** + * 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。使用 Slot 自定义按钮时,需自行控制确认事件 + */ + confirmBtn?: string | ButtonProps | null; + /** + * 内容 + */ + content?: string; + /** + * 透传至 Overlay 组件 + * @default {} + */ + overlayProps?: OverlayProps; + /** + * 防止滚动穿透 + * @default true + */ + preventScrollThrough?: boolean; + /** + * 是否显示遮罩层 + * @default true + */ + showOverlay?: boolean; + /** + * 标题 + */ + title?: string; + /** + * 是否使用了自定义导航栏 + * @default false + */ + usingCustomNavbar?: boolean; + /** + * 控制对话框是否显示 + */ + visible?: boolean; + /** + * 对话框层级,Web 侧样式默认为 2500,移动端样式默认 2500,小程序样式默认为 11500 + * @default 11500 + */ + zIndex?: number; + /** + * 点击多按钮中的其中一个时触发 + */ + onAction?: (context: { index: number }) => void; + /** + * 如果“取消”按钮存在,则点击“取消”按钮时触发,同时触发关闭事件 + */ + onCancel?: (context: { e: MouseEvent }) => void; + /** + * 关闭事件,点击 取消按钮 或 点击蒙层 时触发 + */ + onClose?: (context: { trigger: DialogEventSource }) => void; + /** + * 如果“确认”按钮存在,则点击“确认”按钮时触发 + */ + onConfirm?: (context: { e: MouseEvent }) => void; + /** + * 如果蒙层存在,点击蒙层时触发 + */ + onOverlayClick?: (context: { e: MouseEvent }) => void; +} + +export type DialogEventSource = 'cancel' | 'overlay' | 'close-btn'; diff --git a/uni_modules/tdesign-uniapp/components/divider/README.en-US.md b/uni_modules/tdesign-uniapp/components/divider/README.en-US.md new file mode 100644 index 0000000..e21874c --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/divider/README.en-US.md @@ -0,0 +1,40 @@ +:: BASE_DOC :: + +## API + +### Divider Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +align | String | center | options: left/right/center | N +content | String | - | \- | N +dashed | Boolean | false | \- | N +layout | String | horizontal | options: horizontal/vertical | N + +### Divider Slots + +name | Description +-- | -- +content | \- + +### Divider External Classes + +className | Description +-- | -- +t-class | \- +t-class-content | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-divider-border-width | 2rpx | - +--td-divider-color | @bg-color-component | - +--td-divider-content-color | @text-color-placeholder | - +--td-divider-content-font | @font-body-small | - +--td-divider-content-line-style | solid | - +--td-divider-content-margin | @spacer-1 | - +--td-divider-horizontal-margin | 20rpx | - +--td-divider-vertical-margin | @spacer | - diff --git a/uni_modules/tdesign-uniapp/components/divider/README.md b/uni_modules/tdesign-uniapp/components/divider/README.md new file mode 100644 index 0000000..10fd486 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/divider/README.md @@ -0,0 +1,64 @@ +--- +title: Divider 分割线 +description: 用于分割、组织、细化有一定逻辑的组织元素内容和页面结构。 +spline: message +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TDivider from '@tdesign/uniapp/divider/divider.vue'; +``` + +### 基础分割符 + +分割符主要是由直线和文字组成,通过`slot`传入分割线文案或者其他自定义内容,通过`layout`控制分隔符是垂直还是横向 + +{{ base }} + +### 虚线样式 + +{{ theme }} + +## API + +### Divider Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +align | String | center | 文本位置(仅在水平分割线有效)。可选项:left/right/center | N +content | String | - | 子元素 | N +dashed | Boolean | false | 是否虚线(仅在水平分割线有效) | N +layout | String | horizontal | 分隔线类型有两种:水平和垂直。可选项:horizontal/vertical | N + +### Divider Slots + +名称 | 描述 +-- | -- +content | 自定义 `content` 显示内容 + +### Divider External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-content | 内容样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-divider-border-width | 2rpx | - +--td-divider-color | @bg-color-component | - +--td-divider-content-color | @text-color-placeholder | - +--td-divider-content-font | @font-body-small | - +--td-divider-content-line-style | solid | - +--td-divider-content-margin | @spacer-1 | - +--td-divider-horizontal-margin | 20rpx | - +--td-divider-vertical-margin | @spacer | - diff --git a/uni_modules/tdesign-uniapp/components/divider/divider.css b/uni_modules/tdesign-uniapp/components/divider/divider.css new file mode 100644 index 0000000..f7b9fe9 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/divider/divider.css @@ -0,0 +1,56 @@ +.t-divider { + display: flex; + color: var(--td-divider-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + border-color: var(--td-divider-color, var(--td-bg-color-component, var(--td-gray-color-3, #e7e7e7))); + border-style: var(--td-divider-content-line-style, solid); + border-width: 0; +} +.t-divider::before, +.t-divider::after { + content: ''; + display: block; + flex: 1; + box-sizing: border-box; + border: inherit; + border-color: inherit; + border-style: inherit; +} +.t-divider--horizontal { + align-items: center; + margin: var(--td-divider-horizontal-margin, 20rpx) 0; +} +.t-divider--horizontal::before, +.t-divider--horizontal::after { + border-top-width: var(--td-divider-border-width, 2rpx); + transform: scaleY(0.5); + transform-origin: center; +} +.t-divider--horizontal .t-divider__content:not(:empty) { + margin: 0 var(--td-divider-content-margin, var(--td-spacer-1, 24rpx)); +} +.t-divider--vertical { + flex-direction: column; + height: 28rpx; + margin: 0 var(--td-divider-vertical-margin, var(--td-spacer, 16rpx)); +} +.t-divider--vertical::before, +.t-divider--vertical::after { + border-left-width: var(--td-divider-border-width, 2rpx); + transform: scaleX(0.5); + transform-origin: center; +} +.t-divider--vertical-center { + align-items: center; + height: 100%; +} +.t-divider--dashed { + border-style: dashed; +} +.t-divider__content { + font: var(--td-divider-content-font, var(--td-font-body-small, 24rpx / 40rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-divider-content-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); +} +.t-divider--left::before, +.t-divider--right::after { + max-width: 60rpx; +} diff --git a/uni_modules/tdesign-uniapp/components/divider/divider.vue b/uni_modules/tdesign-uniapp/components/divider/divider.vue new file mode 100644 index 0000000..944b15f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/divider/divider.vue @@ -0,0 +1,79 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/divider/props.ts b/uni_modules/tdesign-uniapp/components/divider/props.ts new file mode 100644 index 0000000..0474070 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/divider/props.ts @@ -0,0 +1,33 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdDividerProps } from './type'; +export default { + /** 文本位置(仅在水平分割线有效) */ + align: { + type: String, + default: 'center' as TdDividerProps['align'], + validator(val: TdDividerProps['align']): boolean { + if (!val) return true; + return ['left', 'right', 'center'].includes(val); + }, + }, + /** 子元素 */ + content: { + type: String, + }, + /** 是否虚线(仅在水平分割线有效) */ + dashed: Boolean, + /** 分隔线类型有两种:水平和垂直 */ + layout: { + type: String, + default: 'horizontal' as TdDividerProps['layout'], + validator(val: TdDividerProps['layout']): boolean { + if (!val) return true; + return ['horizontal', 'vertical'].includes(val); + }, + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/divider/type.ts b/uni_modules/tdesign-uniapp/components/divider/type.ts new file mode 100644 index 0000000..22acac7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/divider/type.ts @@ -0,0 +1,27 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdDividerProps { + /** + * 文本位置(仅在水平分割线有效) + * @default center + */ + align?: 'left' | 'right' | 'center'; + /** + * 子元素 + */ + content?: string; + /** + * 是否虚线(仅在水平分割线有效) + * @default false + */ + dashed?: boolean; + /** + * 分隔线类型有两种:水平和垂直 + * @default horizontal + */ + layout?: 'horizontal' | 'vertical'; +} diff --git a/uni_modules/tdesign-uniapp/components/draggable/draggable.css b/uni_modules/tdesign-uniapp/components/draggable/draggable.css new file mode 100644 index 0000000..84c77e0 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/draggable/draggable.css @@ -0,0 +1,16 @@ +.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-draggable { + position: fixed; +} diff --git a/uni_modules/tdesign-uniapp/components/draggable/draggable.vue b/uni_modules/tdesign-uniapp/components/draggable/draggable.vue new file mode 100644 index 0000000..1a46bd1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/draggable/draggable.vue @@ -0,0 +1,92 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/draggable/props.js b/uni_modules/tdesign-uniapp/components/draggable/props.js new file mode 100644 index 0000000..9542dae --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/draggable/props.js @@ -0,0 +1,19 @@ +const props = { + direction: { + type: String, + value: 'all', + }, + tClass: { + type: String, + default: '', + }, + tClassButton: { + type: String, + default: '', + }, + customStyle: { + type: [String, Object], + default: '', + }, +}; +export default props; diff --git a/uni_modules/tdesign-uniapp/components/drawer/README.en-US.md b/uni_modules/tdesign-uniapp/components/drawer/README.en-US.md new file mode 100644 index 0000000..fe75fc2 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/drawer/README.en-US.md @@ -0,0 +1,54 @@ +:: BASE_DOC :: + +## API + +### Drawer Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +close-on-overlay-click | Boolean | true | \- | N +destroy-on-close | Boolean | false | \- | N +items | Array | - | Typescript:`DrawerItem[]` `interface DrawerItem { title: string; icon: string; }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/drawer/type.ts) | N +overlay-props | Object | {} | Typescript:`OverlayProps`,[Overlay API Documents](./overlay?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/drawer/type.ts) | N +placement | String | right | options: left/right | N +show-overlay | Boolean | true | \- | N +title | String | - | \- | N +using-custom-navbar | Boolean | false | \- | N +visible | Boolean | false | \- | N +z-index | Number | 11500 | \- | N + +### Drawer Events + +name | params | description +-- | -- | -- +close | `(context: { trigger: DrawerTriggerSource })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/drawer/type.ts)。
`type DrawerTriggerSource = 'overlay'`
+item-click | `(context: { index: number; item: DrawerItem })` | \- +overlay-click | \- | \- +update-visible | `(context: { visible: boolean })` | \- + +### Drawer Slots + +name | Description +-- | -- +\- | \- +footer | \- +title | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-drawer-bg-color | @bg-color-container | - +--td-drawer-border-color | @border-level-1-color | - +--td-drawer-footer-padding-bottom | 40rpx | - +--td-drawer-hover-color | @bg-color-secondarycontainer | - +--td-drawer-item-icon-color | @drawer-title-color | - +--td-drawer-item-icon-size | 48rpx | - +--td-drawer-item-padding | 32rpx | - +--td-drawer-sidebar-height | 70vh | - +--td-drawer-title-color | @text-color-primary | - +--td-drawer-title-font | @font-title-large | - +--td-drawer-title-padding | 48rpx 32rpx 16rpx | - +--td-drawer-width | 560rpx | - diff --git a/uni_modules/tdesign-uniapp/components/drawer/README.md b/uni_modules/tdesign-uniapp/components/drawer/README.md new file mode 100644 index 0000000..eed12c6 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/drawer/README.md @@ -0,0 +1,80 @@ +--- +title: Drawer 抽屉 +description: 用作一组平行关系页面/内容的切换器,相较于Tab,同屏可展示更多的选项数量。 +spline: message +isComponent: true +--- + + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TDrawer from '@tdesign/uniapp/drawer/drawer.vue'; +``` + + +### 基础抽屉 +{{ base }} + +### 带图标的抽屉 +{{ icon-drawer }} + +>Drawer的 `visible` 是受控的,需要手动设置 `visible` 为 `true` 才会开启抽屉 + + + +## API + +### Drawer Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +close-on-overlay-click | Boolean | true | 点击蒙层时是否触发抽屉关闭事件 | N +destroy-on-close | Boolean | false | 抽屉关闭时是否销毁节点 | N +items | Array | - | 抽屉里的列表项。TS 类型:`DrawerItem[]` `interface DrawerItem { title: string; icon: string; }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/drawer/type.ts) | N +overlay-props | Object | {} | 遮罩层的属性,透传至 overlay。TS 类型:`OverlayProps`,[Overlay API Documents](./overlay?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/drawer/type.ts) | N +placement | String | right | 抽屉方向。可选项:left/right | N +show-overlay | Boolean | true | 是否显示遮罩层 | N +title | String | - | 抽屉的标题 | N +using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N +visible | Boolean | false | 组件是否可见。支持语法糖 `v-model:visible` | N +z-index | Number | 11500 | 抽屉层级,样式默认为 11500 | N + +### Drawer Events + +名称 | 参数 | 描述 +-- | -- | -- +close | `(context: { trigger: DrawerTriggerSource })` | 关闭时触发。。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/drawer/type.ts)。
`type DrawerTriggerSource = 'overlay'`
+item-click | `(context: { index: number; item: DrawerItem })` | 点击抽屉里的列表项 +overlay-click | \- | 如果蒙层存在,点击蒙层时触发 +update-visible | `(context: { visible: boolean })` | 更新可见性 + +### Drawer Slots + +名称 | 描述 +-- | -- +\- | 自定义抽屉的底部 +footer | 抽屉的底部 +title | 自定义 `title` 显示内容 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-drawer-bg-color | @bg-color-container | - +--td-drawer-border-color | @border-level-1-color | - +--td-drawer-footer-padding-bottom | 40rpx | - +--td-drawer-hover-color | @bg-color-secondarycontainer | - +--td-drawer-item-icon-color | @drawer-title-color | - +--td-drawer-item-icon-size | 48rpx | - +--td-drawer-item-padding | 32rpx | - +--td-drawer-sidebar-height | 70vh | - +--td-drawer-title-color | @text-color-primary | - +--td-drawer-title-font | @font-title-large | - +--td-drawer-title-padding | 48rpx 32rpx 16rpx | - +--td-drawer-width | 560rpx | - diff --git a/uni_modules/tdesign-uniapp/components/drawer/drawer.css b/uni_modules/tdesign-uniapp/components/drawer/drawer.css new file mode 100644 index 0000000..127da13 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/drawer/drawer.css @@ -0,0 +1,62 @@ +.t-drawer { + background: var(--td-drawer-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + width: var(--td-drawer-width, 560rpx); + height: 100%; + display: flex; + flex-direction: column; +} +.t-drawer--hover { + background-color: var(--td-drawer-hover-color, var(--td-bg-color-secondarycontainer, var(--td-gray-color-1, #f3f3f3))); +} +.t-drawer__title { + font: var(--td-drawer-title-font, var(--td-font-title-large, 600 36rpx / 52rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + padding: 48rpx 32rpx 16rpx; + color: var(--td-drawer-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-drawer__sidebar { + height: var(--td-drawer-sidebar-height, 70vh); +} +.t-drawer__sidebar-item { + display: flex; + align-items: center; + position: relative; + padding-top: var(--td-drawer-item-padding, 32rpx); + padding-right: 0; + padding-bottom: var(--td-drawer-item-padding, 32rpx); + padding-left: var(--td-drawer-item-padding, 32rpx); + font: var(--td-font-body-large, 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)); +} +.t-drawer__sidebar-item::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-drawer-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7))); +} +.t-drawer__sidebar-item::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-drawer__sidebar-item::after { + left: var(--td-drawer-item-padding, 32rpx); +} +.t-drawer__sidebar-item-title { + flex: 1; + color: var(--td-drawer-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-drawer__sidebar-item-icon { + padding-right: 16rpx; + color: var(--td-drawer-item-icon-color, var(--td-drawer-title-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))))); + font-size: var(--td-drawer-item-icon-size, 48rpx); +} +.t-drawer__footer { + flex: 1; + display: flex; + flex-direction: column; + padding-bottom: var(--td-drawer-footer-padding-bottom, 40rpx); +} diff --git a/uni_modules/tdesign-uniapp/components/drawer/drawer.vue b/uni_modules/tdesign-uniapp/components/drawer/drawer.vue new file mode 100644 index 0000000..c314a1b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/drawer/drawer.vue @@ -0,0 +1,144 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/drawer/props.ts b/uni_modules/tdesign-uniapp/components/drawer/props.ts new file mode 100644 index 0000000..ee0b693 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/drawer/props.ts @@ -0,0 +1,72 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdDrawerProps } from './type'; +export default { + /** 点击蒙层时是否触发抽屉关闭事件 */ + closeOnOverlayClick: { + type: Boolean, + default: true, + }, + /** 抽屉关闭时是否销毁节点 */ + destroyOnClose: Boolean, + /** 抽屉里的列表项 */ + items: { + type: Array, + }, + /** 遮罩层的属性,透传至 overlay */ + overlayProps: { + type: Object, + default: () => ({}), + }, + /** 抽屉方向 */ + placement: { + type: String, + default: 'right' as TdDrawerProps['placement'], + validator(val: TdDrawerProps['placement']): boolean { + if (!val) return true; + return ['left', 'right'].includes(val); + }, + }, + /** 是否显示遮罩层 */ + showOverlay: { + type: Boolean, + default: true, + }, + /** 抽屉的标题 */ + title: { + type: String, + }, + /** 是否使用了自定义导航栏 */ + usingCustomNavbar: Boolean, + /** 组件是否可见。支持语法糖 `v-model:visible` */ + visible: Boolean, + /** 抽屉层级,样式默认为 11500 */ + zIndex: { + type: Number, + default: 11500, + }, + /** 关闭时触发。 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 点击抽屉里的列表项 */ + onItemClick: { + type: Function, + default: () => ({}), + }, + /** 如果蒙层存在,点击蒙层时触发 */ + onOverlayClick: { + type: Function, + default: () => ({}), + }, + /** 更新可见性 */ + onUpdateVisible: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/drawer/type.ts b/uni_modules/tdesign-uniapp/components/drawer/type.ts new file mode 100644 index 0000000..837d783 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/drawer/type.ts @@ -0,0 +1,81 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdOverlayProps as OverlayProps } from '../overlay/type'; + +export interface TdDrawerProps { + /** + * 点击蒙层时是否触发抽屉关闭事件 + * @default true + */ + closeOnOverlayClick?: boolean; + /** + * 抽屉关闭时是否销毁节点 + * @default false + */ + destroyOnClose?: boolean; + /** + * 抽屉里的列表项 + */ + items?: DrawerItem[]; + /** + * 遮罩层的属性,透传至 overlay + * @default {} + */ + overlayProps?: OverlayProps; + /** + * 抽屉方向 + * @default right + */ + placement?: 'left' | 'right'; + /** + * 是否显示遮罩层 + * @default true + */ + showOverlay?: boolean; + /** + * 抽屉的标题 + */ + title?: string; + /** + * 是否使用了自定义导航栏 + * @default false + */ + usingCustomNavbar?: boolean; + /** + * 组件是否可见。支持语法糖 `v-model:visible` + * @default false + */ + visible?: boolean; + /** + * 抽屉层级,样式默认为 11500 + * @default 11500 + */ + zIndex?: number; + /** + * 关闭时触发。 + */ + onClose?: (context: { trigger: DrawerTriggerSource }) => void; + /** + * 点击抽屉里的列表项 + */ + onItemClick?: (context: { index: number; item: DrawerItem }) => void; + /** + * 如果蒙层存在,点击蒙层时触发 + */ + onOverlayClick?: () => void; + /** + * 更新可见性 + */ + onUpdateVisible?: (context: { visible: boolean }) => void; +} + +export interface DrawerItem { + title: string; + icon: string; +} + +export type DrawerTriggerSource = 'overlay'; diff --git a/uni_modules/tdesign-uniapp/components/dropdown-item/computed.js b/uni_modules/tdesign-uniapp/components/dropdown-item/computed.js new file mode 100644 index 0000000..e8886ef --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-item/computed.js @@ -0,0 +1,6 @@ +export function getStyles(top, zIndex) { + const topStyle = top ? `top:${top}px;` : ''; + const zIndexStyle = zIndex ? `z-index:${zIndex};` : ''; + return topStyle + zIndexStyle; +} + diff --git a/uni_modules/tdesign-uniapp/components/dropdown-item/dropdown-item.css b/uni_modules/tdesign-uniapp/components/dropdown-item/dropdown-item.css new file mode 100644 index 0000000..e38e9fd --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-item/dropdown-item.css @@ -0,0 +1,97 @@ +/** dropdown-item */ +.t-dropdown-item { + position: fixed; + right: 0; + left: 0; + top: 0; + overflow: hidden; + bottom: 0; +} +.t-dropdown-item__content { + display: flex; + flex-direction: column; + z-index: 11600; + overflow: hidden; +} +.t-dropdown-item__popup-host { + display: block; + width: 100%; + overflow: hidden; + position: absolute; + left: 0; + top: 0; +} +.t-dropdown-item__body { + flex: 1; + background: var(--td-dropdown-menu-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + overflow: auto; + max-height: var(--td-dropdown-body-max-height, 560rpx); +} +.t-dropdown-item__body--tree { + display: flex; + overflow: hidden; +} +.t-dropdown-item__body--multi { + padding-top: var(--td-spacer, 16rpx); + padding-bottom: var(--td-spacer, 16rpx); + overflow-y: auto; +} +.t-dropdown-item__scroll { + max-height: var(--td-dropdown-body-max-height, 560rpx); +} +.t-dropdown-item__footer { + display: flex; + background: var(--td-dropdown-menu-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + padding: 32rpx; + position: relative; +} +.t-dropdown-item__footer::after { + content: ''; + display: block; + position: absolute; + top: 0; + bottom: unset; + left: unset; + right: unset; + background-color: var(--td-component-border, var(--td-gray-color-4, #dcdcdc)); +} +.t-dropdown-item__footer::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +:deep(.t-dropdown-item__footer-btn) { + flex: 1; +} +:deep(.t-dropdown-item__footer-btn + .t-dropdown-item__footer-btn) { + margin-left: 32rpx; +} +.t-dropdown-item__body:empty, +.t-dropdown-item__footer:empty { + display: none; +} +.t-dropdown-item__radio, +.t-dropdown-item__checkbox { + width: 100%; + overflow: scroll; + box-sizing: border-box; +} +.t-dropdown-item__radio-group, +.t-dropdown-item__checkbox-group { + display: grid; + grid-gap: 24rpx; +} +.t-dropdown-item__radio-group { + display: grid; + grid-gap: 0rpx; +} +.t-dropdown-item__checkbox-group { + padding: 32rpx; +} +.t-dropdown-item__mask { + position: fixed; + width: 100vh; + top: 0; + left: 0; +} diff --git a/uni_modules/tdesign-uniapp/components/dropdown-item/dropdown-item.vue b/uni_modules/tdesign-uniapp/components/dropdown-item/dropdown-item.vue new file mode 100644 index 0000000..71e3a02 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-item/dropdown-item.vue @@ -0,0 +1,403 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/dropdown-item/props.ts b/uni_modules/tdesign-uniapp/components/dropdown-item/props.ts new file mode 100644 index 0000000..7ba6c44 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-item/props.ts @@ -0,0 +1,71 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdDropdownItemProps } from './type'; +export default { + /** 是否禁用操作项 */ + disabled: Boolean, + /** 用来定义 value / label / disabled 在 `options` 中对应的字段别名 */ + keys: { + type: Object, + }, + /** 标题 */ + label: { + type: String, + default: '', + }, + /** 是否多选 */ + multiple: Boolean, + /** 选项数据 */ + options: { + type: Array, + default: (): TdDropdownItemProps['options'] => [], + }, + /** 选项分栏(1-3) */ + optionsColumns: { + type: [String, Number], + default: 1 as TdDropdownItemProps['optionsColumns'], + }, + /** 复选框和内容相对位置,仅单选菜单栏有效 */ + placement: { + type: String, + default: 'left' as TdDropdownItemProps['placement'], + validator(val: TdDropdownItemProps['placement']): boolean { + if (!val) return true; + return ['left', 'right'].includes(val); + }, + }, + /** 选中值 */ + value: { + type: [String, Number, Array], + default: undefined as TdDropdownItemProps['value'], + }, + /** 选中值,非受控属性 */ + defaultValue: { + type: [String, Number, Array], + default: undefined as TdDropdownItemProps['defaultValue'], + }, + /** 值改变时触发 */ + onChange: { + type: Function, + default: () => ({}), + }, + /** 关闭时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 点击确认时触发 */ + onConfirm: { + type: Function, + default: () => ({}), + }, + /** 点击重置时触发 */ + onReset: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/dropdown-item/type.ts b/uni_modules/tdesign-uniapp/components/dropdown-item/type.ts new file mode 100644 index 0000000..c4ba0ce --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-item/type.ts @@ -0,0 +1,76 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { KeysType } from '../common/common'; + +export interface TdDropdownItemProps { + /** + * 是否禁用操作项 + * @default false + */ + disabled?: boolean; + /** + * 用来定义 value / label / disabled 在 `options` 中对应的字段别名 + */ + keys?: KeysType; + /** + * 标题 + * @default '' + */ + label?: string; + /** + * 是否多选 + * @default false + */ + multiple?: boolean; + /** + * 选项数据 + * @default [] + */ + options?: Array; + /** + * 选项分栏(1-3) + * @default 1 + */ + optionsColumns?: string | number; + /** + * 复选框和内容相对位置,仅单选菜单栏有效 + * @default left + */ + placement?: 'left' | 'right'; + /** + * 选中值 + */ + value?: DropdownValue; + /** + * 选中值,非受控属性 + */ + defaultValue?: DropdownValue; + /** + * 值改变时触发 + */ + onChange?: (context: { value: DropdownValue }) => void; + /** + * 关闭时触发 + */ + onClose?: () => void; + /** + * 点击确认时触发 + */ + onConfirm?: (context: { value: DropdownValue }) => void; + /** + * 点击重置时触发 + */ + onReset?: () => void; +} + +export interface DropdownOption { + label: string; + disabled: boolean; + value: DropdownValue; +} + +export type DropdownValue = string | number | Array; diff --git a/uni_modules/tdesign-uniapp/components/dropdown-menu/README.en-US.md b/uni_modules/tdesign-uniapp/components/dropdown-menu/README.en-US.md new file mode 100644 index 0000000..b5681ea --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-menu/README.en-US.md @@ -0,0 +1,95 @@ +:: BASE_DOC :: + +## API + +### DropdownMenu Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +arrow-icon | String / Object | 'caret-down-small' | \- | N +close-on-click-overlay | Boolean | true | \- | N +duration | String / Number | 200 | \- | N +show-overlay | Boolean | true | \- | N +z-index | Number | 11600 | \- | N + +### DropdownMenu Events + +name | params | description +-- | -- | -- +close | \- | \- +open | \- | \- + +### DropdownMenu Slots + +name | Description +-- | -- +\- | \- + +### DropdownMenu External Classes + +className | Description +-- | -- +t-class | \- +t-class-icon | \- +t-class-item | \- +t-class-label | \- + + +### DropdownItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +disabled | Boolean | false | \- | N +keys | Object | - | Typescript:`KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +label | String | - | \- | N +multiple | Boolean | false | \- | N +options | Array | [] | Typescript:`Array` `interface DropdownOption { label: string; disabled: boolean; value: DropdownValue; }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dropdown-item/type.ts) | N +options-columns | String / Number | 1 | \- | N +options-layout | String | columns | `deprecated` | N +placement | String | left | options: left/right | N +value | String / Number / Array | undefined | `v-model:value` is supported。Typescript:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dropdown-item/type.ts) | N +default-value | String / Number / Array | undefined | uncontrolled property。Typescript:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dropdown-item/type.ts) | N + +### DropdownItem Events + +name | params | description +-- | -- | -- +change | `(context: { value: DropdownValue })` | \- +close | \- | \- +confirm | `(context: { value: DropdownValue })` | \- +reset | \- | \- + +### DropdownItem Slots + +name | Description +-- | -- +\- | \- +footer | \- + +### DropdownItem External Classes + +className | Description +-- | -- +t-class | \- +t-class-column | \- +t-class-column-item | \- +t-class-column-item-label | \- +t-class-content | \- +t-class-footer | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-dropdown-menu-active-color | @brand-color | - +--td-dropdown-menu-bg-color | @bg-color-container | - +--td-dropdown-menu-border-width | 1px | - +--td-dropdown-menu-color | @text-color-primary | - +--td-dropdown-menu-disabled-color | @text-color-disabled | - +--td-dropdown-menu-font-size | 28rpx | - +--td-dropdown-menu-height | 96rpx | - +--td-dropdown-menu-icon-size | 40rpx | - +--td-dropdown-body-max-height | 560rpx | - diff --git a/uni_modules/tdesign-uniapp/components/dropdown-menu/README.md b/uni_modules/tdesign-uniapp/components/dropdown-menu/README.md new file mode 100644 index 0000000..3114c5d --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-menu/README.md @@ -0,0 +1,125 @@ +--- +title: DropdownMenu 下拉菜单 +description: 菜单呈现数个并列的选项类目,用于整个页面的内容筛选,由菜单面板和菜单选项组成。 +spline: message +isComponent: true +--- + + + +## 引入 + +### 引入组件 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import DropdownMenu from '@tdesign/uniapp/dropdown-menu/dropdown-menu.vue'; +import DropdownItem from '@tdesign/uniapp/dropdown-item/dropdown-item.vue'; +``` + +### 单选下拉菜单 + +{{ single }} + +### 多列下拉菜单 + +{{ multi }} + +### 树形下拉菜单 + +{{ tree }} + +## API + +### DropdownMenu Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +arrow-icon | String / Object | 'caret-down-small' | 自定义箭头图标 | N +close-on-click-overlay | Boolean | true | 是否在点击遮罩层后关闭菜单 | N +duration | String / Number | 200 | 动画时长 | N +show-overlay | Boolean | true | 是否显示遮罩层 | N +z-index | Number | 11600 | 菜单栏 z-index 层级 | N + +### DropdownMenu Events + +名称 | 参数 | 描述 +-- | -- | -- +close | \- | 菜单关闭时触发 +open | \- | 菜单展开时触发 + +### DropdownMenu Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 + +### DropdownMenu External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-icon | 图标样式类 +t-class-item | 选项样式类 +t-class-label | 标签样式类 + + +### DropdownItem Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +disabled | Boolean | false | 是否禁用操作项 | N +keys | Object | - | 用来定义 value / label / disabled 在 `options` 中对应的字段别名。TS 类型:`KeysType`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) | N +label | String | - | 标题 | N +multiple | Boolean | false | 是否多选 | N +options | Array | [] | 选项数据。TS 类型:`Array` `interface DropdownOption { label: string; disabled: boolean; value: DropdownValue; }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dropdown-item/type.ts) | N +options-columns | String / Number | 1 | 选项分栏(1-3) | N +options-layout | String | columns | 已废弃。选项排列;不再支持 tree 布局,可与 treeSelect 配合使用 | N +placement | String | left | 复选框和内容相对位置,仅单选菜单栏有效。可选项:left/right | N +value | String / Number / Array | undefined | 选中值。支持语法糖 `v-model:value`。TS 类型:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dropdown-item/type.ts) | N +default-value | String / Number / Array | undefined | 选中值。非受控属性。TS 类型:`DropdownValue ` `type DropdownValue = string \| number \| Array;`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/dropdown-item/type.ts) | N + +### DropdownItem Events + +名称 | 参数 | 描述 +-- | -- | -- +change | `(context: { value: DropdownValue })` | 值改变时触发 +close | \- | 关闭时触发 +confirm | `(context: { value: DropdownValue })` | 点击确认时触发 +reset | \- | 点击重置时触发 + +### DropdownItem Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,自定义内容区域内容 +footer | 底部 + +### DropdownItem External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-column | 菜单列样式类 +t-class-column-item | 菜单列选项样式类 +t-class-column-item-label | 菜单列选项标签样式类 +t-class-content | 内容样式类 +t-class-footer | 底部样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-dropdown-menu-active-color | @brand-color | - +--td-dropdown-menu-bg-color | @bg-color-container | - +--td-dropdown-menu-border-width | 1px | - +--td-dropdown-menu-color | @text-color-primary | - +--td-dropdown-menu-disabled-color | @text-color-disabled | - +--td-dropdown-menu-font-size | 28rpx | - +--td-dropdown-menu-height | 96rpx | - +--td-dropdown-menu-icon-size | 40rpx | - +--td-dropdown-body-max-height | 560rpx | - diff --git a/uni_modules/tdesign-uniapp/components/dropdown-menu/dropdown-menu.css b/uni_modules/tdesign-uniapp/components/dropdown-menu/dropdown-menu.css new file mode 100644 index 0000000..4919ba2 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-menu/dropdown-menu.css @@ -0,0 +1,59 @@ +.t-dropdown-menu { + display: flex; + height: var(--td-dropdown-menu-height, 96rpx); + background: var(--td-dropdown-menu-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))); + position: relative; +} +.t-dropdown-menu::after { + content: ''; + display: block; + position: absolute; + top: unset; + bottom: 0; + left: unset; + right: unset; + background-color: var(--td-component-border, var(--td-gray-color-4, #dcdcdc)); +} +.t-dropdown-menu::after { + height: 1px; + left: 0; + right: 0; + transform: scaleY(0.5); +} +.t-dropdown-menu:after { + height: var(--td-dropdown-menu-border-width, 1px); +} +.t-dropdown-menu__item { + display: flex; + flex: 1; + align-items: center; + justify-content: center; + padding: 0 var(--td-spacer, 16rpx); + position: relative; + overflow: hidden; + color: var(--td-dropdown-menu-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9)))); +} +.t-dropdown-menu__item--active { + color: var(--td-dropdown-menu-active-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); +} +.t-dropdown-menu__item--disabled { + color: var(--td-dropdown-menu-disabled-color, var(--td-text-color-disabled, var(--td-font-gray-4, rgba(0, 0, 0, 0.26)))); +} +:deep(.t-dropdown-menu__icon) { + font-size: var(--td-dropdown-menu-icon-size, 40rpx); + padding: 4rpx; + box-sizing: border-box; + transition: transform 240ms ease; +} +:deep(.t-dropdown-menu__icon)--active { + transform: rotate(180deg); +} +:deep(.t-dropdown-menu__icon):not(:empty) { + margin-left: 8rpx; +} +.t-dropdown-menu__title { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: var(--td-dropdown-menu-font-size, 28rpx); +} diff --git a/uni_modules/tdesign-uniapp/components/dropdown-menu/dropdown-menu.vue b/uni_modules/tdesign-uniapp/components/dropdown-menu/dropdown-menu.vue new file mode 100644 index 0000000..bca222b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-menu/dropdown-menu.vue @@ -0,0 +1,188 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/dropdown-menu/props.ts b/uni_modules/tdesign-uniapp/components/dropdown-menu/props.ts new file mode 100644 index 0000000..d23729b --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-menu/props.ts @@ -0,0 +1,44 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdDropdownMenuProps } from './type'; +export default { + /** 自定义箭头图标 */ + arrowIcon: { + type: [String, Object], + default: 'caret-down-small' as TdDropdownMenuProps['arrowIcon'], + }, + /** 是否在点击遮罩层后关闭菜单 */ + closeOnClickOverlay: { + type: Boolean, + default: true, + }, + /** 动画时长 */ + duration: { + type: [String, Number], + default: 200 as TdDropdownMenuProps['duration'], + }, + /** 是否显示遮罩层 */ + showOverlay: { + type: Boolean, + default: true, + }, + /** 菜单栏 z-index 层级 */ + zIndex: { + type: Number, + default: 11600, + }, + /** 菜单关闭时触发 */ + onClose: { + type: Function, + default: () => ({}), + }, + /** 菜单展开时触发 */ + onOpen: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/dropdown-menu/type.ts b/uni_modules/tdesign-uniapp/components/dropdown-menu/type.ts new file mode 100644 index 0000000..12be3fe --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/dropdown-menu/type.ts @@ -0,0 +1,41 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdDropdownMenuProps { + /** + * 自定义箭头图标 + * @default 'caret-down-small' + */ + arrowIcon?: string | object; + /** + * 是否在点击遮罩层后关闭菜单 + * @default true + */ + closeOnClickOverlay?: boolean; + /** + * 动画时长 + * @default 200 + */ + duration?: string | number; + /** + * 是否显示遮罩层 + * @default true + */ + showOverlay?: boolean; + /** + * 菜单栏 z-index 层级 + * @default 11600 + */ + zIndex?: number; + /** + * 菜单关闭时触发 + */ + onClose?: () => void; + /** + * 菜单展开时触发 + */ + onOpen?: () => void; +} diff --git a/uni_modules/tdesign-uniapp/components/empty/README.en-US.md b/uni_modules/tdesign-uniapp/components/empty/README.en-US.md new file mode 100644 index 0000000..26cbf89 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/empty/README.en-US.md @@ -0,0 +1,39 @@ +:: BASE_DOC :: + +## API + +### Empty Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +description | String | - | empty component description | N +icon | String / Object | - | \- | N +image | String | - | image url, or Image component props, or custom any node you need | N + +### Empty Slots + +name | Description +-- | -- +action | action block +description | empty component description +image | image url, or Image component props, or custom any node you need + +### Empty External Classes + +className | Description +-- | -- +t-class | \- +t-class-description | \- +t-class-image | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-empty-action-margin-top | @spacer-4 | - +--td-empty-description-color | @text-color-placeholder | - +--td-empty-description-font | @font-body-medium | - +--td-empty-description-margin-top | @spacer-2 | - +--td-empty-icon-color | @text-color-placeholder | - diff --git a/uni_modules/tdesign-uniapp/components/empty/README.md b/uni_modules/tdesign-uniapp/components/empty/README.md new file mode 100644 index 0000000..ac5c1b1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/empty/README.md @@ -0,0 +1,69 @@ +--- +title: Empty 空状态 +description: 用于空状态时的占位提示。 +spline: data +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TEmpty from '@tdesign/uniapp/empty/empty.vue'; +``` + +### 类型 + +图标空状态 + +{{ base }} + +自定义图片空状态 + +{{ imageEmpty }} + +带操作空状态 + +{{ buttonEmpty }} + + + +## API + +### Empty Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +description | String | - | 描述文字 | N +icon | String / Object | - | 图标名称。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` | N +image | String | - | 图片地址 | N + +### Empty Slots + +名称 | 描述 +-- | -- +action | 操作按钮 +description | 自定义 `description` 显示内容 +image | 自定义 `image` 显示内容 + +### Empty External Classes + +类名 | 描述 +-- | -- +t-class | 根节点样式类 +t-class-description | 描述样式类 +t-class-image | 图片样式类 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-empty-action-margin-top | @spacer-4 | - +--td-empty-description-color | @text-color-placeholder | - +--td-empty-description-font | @font-body-medium | - +--td-empty-description-margin-top | @spacer-2 | - +--td-empty-icon-color | @text-color-placeholder | - diff --git a/uni_modules/tdesign-uniapp/components/empty/empty.css b/uni_modules/tdesign-uniapp/components/empty/empty.css new file mode 100644 index 0000000..f480240 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/empty/empty.css @@ -0,0 +1,21 @@ +.t-empty { + display: flex; + flex-direction: column; + align-items: center; +} +:deep(.t-empty__icon) { + font-size: 192rpx; + color: var(--td-empty-icon-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); +} +.t-empty__thumb + .t-empty__description:not(:empty) { + margin-top: var(--td-empty-description-margin-top, var(--td-spacer-2, 32rpx)); +} +.t-empty__description { + text-align: center; + color: var(--td-empty-description-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + font: var(--td-empty-description-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + white-space: pre-wrap; +} +.t-empty__description + .t-empty__actions:not(:empty) { + margin-top: var(--td-empty-action-margin-top, var(--td-spacer-4, 64rpx)); +} diff --git a/uni_modules/tdesign-uniapp/components/empty/empty.vue b/uni_modules/tdesign-uniapp/components/empty/empty.vue new file mode 100644 index 0000000..d3691b1 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/empty/empty.vue @@ -0,0 +1,140 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/empty/props.ts b/uni_modules/tdesign-uniapp/components/empty/props.ts new file mode 100644 index 0000000..3baf404 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/empty/props.ts @@ -0,0 +1,20 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export default { + /** 描述文字 */ + description: { + type: String, + }, + /** 图标名称。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` */ + icon: { + type: [String, Object], + }, + /** 图片地址 */ + image: { + type: String, + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/empty/type.ts b/uni_modules/tdesign-uniapp/components/empty/type.ts new file mode 100644 index 0000000..d63f873 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/empty/type.ts @@ -0,0 +1,20 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdEmptyProps { + /** + * 描述文字 + */ + description?: string; + /** + * 图标名称。值为字符串表示图标名称,值为 `Object` 类型,表示透传至 `icon` + */ + icon?: string | object; + /** + * 图片地址 + */ + image?: string; +} diff --git a/uni_modules/tdesign-uniapp/components/fab/README.en-US.md b/uni_modules/tdesign-uniapp/components/fab/README.en-US.md new file mode 100644 index 0000000..ebe5778 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/fab/README.en-US.md @@ -0,0 +1,37 @@ +:: BASE_DOC :: + +## API + +### Fab Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +button-props | Object | - | Typescript:`ButtonProps`,[Button API Documents](./button?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/fab/type.ts) | N +draggable | String / Boolean | false | Typescript:`boolean \| FabDirectionEnum ` `type FabDirectionEnum = 'all' \| 'vertical' \| 'horizontal'`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/fab/type.ts) | N +icon | String | - | \- | N +style | String | right: 16px; bottom: 32px; | \- | N +text | String | - | \- | N +using-custom-navbar | Boolean | false | \- | N +y-bounds | Array | - | Typescript:`Array` | N + +### Fab Events + +name | params | description +-- | -- | -- +click | `(context: {e: Event})` | \- +drag-end | `(context: { e: TouchEvent })` | \- +drag-start | `(context: { e: TouchEvent })` | \- + +### Fab Slots + +name | Description +-- | -- +\- | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-fab-shadow | @shadow-2 | - diff --git a/uni_modules/tdesign-uniapp/components/fab/README.md b/uni_modules/tdesign-uniapp/components/fab/README.md new file mode 100644 index 0000000..35dac06 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/fab/README.md @@ -0,0 +1,81 @@ +--- +title: Fab 悬浮按钮 +description: 当功能使用图标即可表意清楚时,可使用纯图标悬浮按钮,例如:添加、发布。 +spline: form +isComponent: true +--- + + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TFab from '@tdesign/uniapp/fab/fab.vue'; +``` + +### 基础使用 + +{{ base }} + +### 进阶使用 + +{{ advance }} + +### 可移动悬浮按钮 + +{{ draggable }} + +### 带自动收缩功能 + +{{ collapsible }} + +## FAQ + +### 为什么通过 style/customStyle 设置 top/left 调整初试定位后,会使页面内容无法点击以及拖拽异常? + +由于 `position: fixed;` 会使得元素脱离文档流,它将悬浮于页面上方。同时,元素没有设置宽高,当同时使用 `top`、`right`、`bottom` 和 `left` 属性时,浏览器会根据给定的 `top`、`right`、`bottom` 和 `left` 创建一个矩形框来容纳元素及其内容,所以会出现元素覆盖页面内容及拖拽异常等问题。 + +Fab 组件默认定位 `right: 16px; bottom: 32px;`,且拖拽功能也是通过调整 `right` 与 `bottom` 属性值实现,因此在使用 `Fab` 组件时,仅支持通过 `style/customStyle` 属性设置 `right/bottom` 来调整初试位置, 避免使用 `top/left`。 + +### 开启 Skyline 渲染引擎后,组件所在页面崩溃? + +因为 Skyline 还不支持多层阴影,要等微信官方处理。当下可参考 [#2865](https://github.com/Tencent/tdesign-miniprogram/issues/2865) 进行规避处理 + + +## API + +### Fab Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +button-props | Object | - | 透传至 Button 组件。TS 类型:`ButtonProps`,[Button API Documents](./button?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/fab/type.ts) | N +draggable | String / Boolean | false | 是否可拖拽。`true` / `'all'`可拖动
`'vertical'`可垂直拖动
`'horizontal'`可水平拖动
`false`禁止拖动。TS 类型:`boolean \| FabDirectionEnum ` `type FabDirectionEnum = 'all' \| 'vertical' \| 'horizontal'`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/fab/type.ts) | N +icon | String | - | 图标 | N +style | String | right: 16px; bottom: 32px; | 悬浮按钮的样式,常用于调整位置(即将废弃,建议使用 `style`) | N +text | String | - | 文本内容 | N +using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N +y-bounds | Array | - | 设置垂直方向边界限制,示例:[48, 48] 或 ['96px', 80]。TS 类型:`Array` | N + +### Fab Events + +名称 | 参数 | 描述 +-- | -- | -- +click | `(context: {e: Event})` | 悬浮按钮点击事件 +drag-end | `(context: { e: TouchEvent })` | 结束拖拽时触发 +drag-start | `(context: { e: TouchEvent })` | 开始拖拽时触发 + +### Fab Slots + +名称 | 描述 +-- | -- +\- | 默认插槽,按钮内容 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-fab-shadow | @shadow-2 | - diff --git a/uni_modules/tdesign-uniapp/components/fab/fab.css b/uni_modules/tdesign-uniapp/components/fab/fab.css new file mode 100644 index 0000000..d68b87a --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/fab/fab.css @@ -0,0 +1,9 @@ +.t-fab { + position: fixed; +} +.t-fab__button { + box-shadow: var(--td-fab-shadow, var(--td-shadow-2, 0 3px 14px 2px rgba(0, 0, 0, 0.05), 0 8px 10px 1px rgba(0, 0, 0, 0.06), 0 5px 5px -3px rgba(0, 0, 0, 0.1))); +} +.t-fab__draggable { + position: fixed; +} diff --git a/uni_modules/tdesign-uniapp/components/fab/fab.vue b/uni_modules/tdesign-uniapp/components/fab/fab.vue new file mode 100644 index 0000000..95f3aff --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/fab/fab.vue @@ -0,0 +1,238 @@ + + + + diff --git a/uni_modules/tdesign-uniapp/components/fab/props.ts b/uni_modules/tdesign-uniapp/components/fab/props.ts new file mode 100644 index 0000000..ffc95a7 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/fab/props.ts @@ -0,0 +1,54 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdFabProps } from './type'; +export default { + /** 透传至 Button 组件 */ + buttonProps: { + type: Object, + }, + /** 是否可拖拽。`true` / `'all'`可拖动
`'vertical'`可垂直拖动
`'horizontal'`可水平拖动
`false`禁止拖动 */ + draggable: { + type: [String, Boolean], + default: false as TdFabProps['draggable'], + }, + /** 图标 */ + icon: { + type: String, + default: '', + }, + /** 悬浮按钮的样式,常用于调整位置(即将废弃,建议使用 `style`) */ + style: { + type: String, + default: 'right: 16px; bottom: 32px;', + }, + /** 文本内容 */ + text: { + type: String, + default: '', + }, + /** 是否使用了自定义导航栏 */ + usingCustomNavbar: Boolean, + /** 设置垂直方向边界限制,示例:[48, 48] 或 ['96px', 80] */ + yBounds: { + type: Array, + }, + /** 悬浮按钮点击事件 */ + onClick: { + type: Function, + default: () => ({}), + }, + /** 结束拖拽时触发 */ + onDragEnd: { + type: Function, + default: () => ({}), + }, + /** 开始拖拽时触发 */ + onDragStart: { + type: Function, + default: () => ({}), + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/fab/type.ts b/uni_modules/tdesign-uniapp/components/fab/type.ts new file mode 100644 index 0000000..856fe76 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/fab/type.ts @@ -0,0 +1,57 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdButtonProps as ButtonProps } from '../button/type'; + +export interface TdFabProps { + /** + * 透传至 Button 组件 + */ + buttonProps?: ButtonProps; + /** + * 是否可拖拽。`true` / `'all'`可拖动
`'vertical'`可垂直拖动
`'horizontal'`可水平拖动
`false`禁止拖动 + * @default false + */ + draggable?: boolean | FabDirectionEnum; + /** + * 图标 + * @default '' + */ + icon?: string; + /** + * 悬浮按钮的样式,常用于调整位置(即将废弃,建议使用 `style`) + * @default right: 16px; bottom: 32px; + */ + style?: string; + /** + * 文本内容 + * @default '' + */ + text?: string; + /** + * 是否使用了自定义导航栏 + * @default false + */ + usingCustomNavbar?: boolean; + /** + * 设置垂直方向边界限制,示例:[48, 48] 或 ['96px', 80] + */ + yBounds?: Array; + /** + * 悬浮按钮点击事件 + */ + onClick?: (context: { e: Event }) => void; + /** + * 结束拖拽时触发 + */ + onDragEnd?: (context: { e: TouchEvent }) => void; + /** + * 开始拖拽时触发 + */ + onDragStart?: (context: { e: TouchEvent }) => void; +} + +export type FabDirectionEnum = 'all' | 'vertical' | 'horizontal'; diff --git a/uni_modules/tdesign-uniapp/components/footer/README.en-US.md b/uni_modules/tdesign-uniapp/components/footer/README.en-US.md new file mode 100644 index 0000000..da371c6 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/footer/README.en-US.md @@ -0,0 +1,33 @@ +:: BASE_DOC :: + +## API + +### Footer Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +copyright | String | '' | `deprecated` | N +links | Array | [] | Typescript:`Array` `interface LinkObj { name: string; url?: string; openType?: 'navigate' \| 'redirect' \| 'relaunch' \| 'switchTab' \| 'navigateBack' }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/footer/type.ts) | N +logo | Object | - | Typescript:`FooterLogo` `interface FooterLogo { icon: string; title?: string; url?: string }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/footer/type.ts) | N +text | String | '' | \- | N +text-link-list | Array | [] | `deprecated`。Typescript:`Array` `interface LinkObj { name: string; url?: string; openType?: 'navigate' \| 'redirect' \| 'relaunch' \| 'switchTab' \| 'navigateBack' }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/footer/type.ts) | N +theme | String | 'text' | `deprecated`。options: text/logo | N + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-footer-link-color | @brand-color | - +--td-footer-link-dividing-line-color | @text-color-placeholder | - +--td-footer-link-dividing-line-padding | @spacer-1 | - +--td-footer-link-font | @font-body-medium | - +--td-footer-logo-icon-height | 48rpx | - +--td-footer-logo-icon-margin-right | @spacer | - +--td-footer-logo-icon-width | 48rpx | - +--td-footer-logo-title-font | @font-title-medium | - +--td-footer-logo-title-url-width | 256rpx | - +--td-footer-text-color | @text-color-placeholder | - +--td-footer-text-font | @font-body-small | - +--td-footer-text-margin-top | 8rpx | - diff --git a/uni_modules/tdesign-uniapp/components/footer/README.md b/uni_modules/tdesign-uniapp/components/footer/README.md new file mode 100644 index 0000000..72d7fff --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/footer/README.md @@ -0,0 +1,61 @@ +--- +title: Footer 页脚 +description: 用于基础列表展示,可附带文字、品牌 logo、操作,常用商详、个人中心、设置等页面。 +spline: data +isComponent: true +--- + + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TFooter from '@tdesign/uniapp/footer/footer.vue'; +``` + +### 类型 + +基础页脚 + +{{ base }} + +基础加链接页脚 + +{{ link }} + +品牌页脚 + +{{ logo }} + +## API + +### Footer Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +copyright | String | '' | 已废弃。版权信息,type 为`text`生效 | N +links | Array | [] | 链接列表。name 表示链接名称, url 表示链接 page 路径,目前只支持小程序内部跳转,openType 表示跳转方式。TS 类型:`Array` `interface LinkObj { name: string; url?: string; openType?: 'navigate' \| 'redirect' \| 'relaunch' \| 'switchTab' \| 'navigateBack' }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/footer/type.ts) | N +logo | Object | - | 图标配置。`logo.icon` 表示图标链接地址,`logo.title` 表示标题文本,`logo.url` 表示链接。TS 类型:`FooterLogo` `interface FooterLogo { icon: string; title?: string; url?: string }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/footer/type.ts) | N +text | String | '' | 版权信息 | N +text-link-list | Array | [] | 已废弃。链接列表,type 为`text`生效。name 表示链接名称, url 表示链接 page 路径,目前只支持小程序内部跳转,openType 表示跳转方式。TS 类型:`Array` `interface LinkObj { name: string; url?: string; openType?: 'navigate' \| 'redirect' \| 'relaunch' \| 'switchTab' \| 'navigateBack' }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/footer/type.ts) | N +theme | String | 'text' | 已废弃。页脚展示类型。可选项:text/logo | N + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-footer-link-color | @brand-color | - +--td-footer-link-dividing-line-color | @text-color-placeholder | - +--td-footer-link-dividing-line-padding | @spacer-1 | - +--td-footer-link-font | @font-body-medium | - +--td-footer-logo-icon-height | 48rpx | - +--td-footer-logo-icon-margin-right | @spacer | - +--td-footer-logo-icon-width | 48rpx | - +--td-footer-logo-title-font | @font-title-medium | - +--td-footer-logo-title-url-width | 256rpx | - +--td-footer-text-color | @text-color-placeholder | - +--td-footer-text-font | @font-body-small | - +--td-footer-text-margin-top | 8rpx | - diff --git a/uni_modules/tdesign-uniapp/components/footer/footer.css b/uni_modules/tdesign-uniapp/components/footer/footer.css new file mode 100644 index 0000000..1f3b78f --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/footer/footer.css @@ -0,0 +1,47 @@ +.t-footer { + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; +} +.t-footer__text { + font: var(--td-footer-text-font, var(--td-font-body-small, 24rpx / 40rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + color: var(--td-footer-text-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); +} +.t-footer__link-list + .t-footer__text:not(:empty) { + margin-top: var(--td-footer-text-margin-top, 8rpx); +} +.t-footer__link-list { + display: flex; + justify-content: center; + align-items: center; +} +.t-footer__link-item { + color: var(--td-footer-link-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9))); + font: var(--td-footer-link-font, var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + text-decoration: underline; +} +.t-footer__link-line { + font-size: 24rpx; + color: var(--td-footer-link-dividing-line-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4)))); + display: inline-block; + padding: 0 var(--td-footer-link-dividing-line-padding, var(--td-spacer-1, 24rpx)); +} +.t-footer__logo { + display: flex; + justify-content: center; + align-items: center; +} +.t-footer__icon { + width: var(--td-footer-logo-icon-width, 48rpx); + height: var(--td-footer-logo-icon-height, 48rpx); + margin-right: var(--td-footer-logo-icon-margin-right, var(--td-spacer, 16rpx)); +} +.t-footer__title { + color: var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))); + font: var(--td-footer-logo-title-font, var(--td-font-title-medium, 600 32rpx / 48rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular))); + font-style: italic; +} +.t-footer__title-url { + width: var(--td-footer-logo-title-url-width, 256rpx); +} diff --git a/uni_modules/tdesign-uniapp/components/footer/footer.vue b/uni_modules/tdesign-uniapp/components/footer/footer.vue new file mode 100644 index 0000000..13926af --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/footer/footer.vue @@ -0,0 +1,117 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/footer/props.ts b/uni_modules/tdesign-uniapp/components/footer/props.ts new file mode 100644 index 0000000..90084d5 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/footer/props.ts @@ -0,0 +1,23 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdFooterProps } from './type'; +export default { + /** 链接列表。name 表示链接名称, url 表示链接 page 路径,目前只支持小程序内部跳转,openType 表示跳转方式 */ + links: { + type: Array, + default: (): TdFooterProps['links'] => [], + }, + /** 图标配置。`logo.icon` 表示图标链接地址,`logo.title` 表示标题文本,`logo.url` 表示链接 */ + logo: { + type: Object, + }, + /** 版权信息 */ + text: { + type: String, + default: '', + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/footer/type.ts b/uni_modules/tdesign-uniapp/components/footer/type.ts new file mode 100644 index 0000000..d9881ba --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/footer/type.ts @@ -0,0 +1,34 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export interface TdFooterProps { + /** + * 链接列表。name 表示链接名称, url 表示链接 page 路径,目前只支持小程序内部跳转,openType 表示跳转方式 + * @default [] + */ + links?: Array; + /** + * 图标配置。`logo.icon` 表示图标链接地址,`logo.title` 表示标题文本,`logo.url` 表示链接 + */ + logo?: FooterLogo; + /** + * 版权信息 + * @default '' + */ + text?: string; +} + +export interface LinkObj { + name: string; + url?: string; + openType?: 'navigate' | 'redirect' | 'relaunch' | 'switchTab' | 'navigateBack'; +} + +export interface FooterLogo { + icon: string; + title?: string; + url?: string; +} diff --git a/uni_modules/tdesign-uniapp/components/form-item/README.en-US.md b/uni_modules/tdesign-uniapp/components/form-item/README.en-US.md new file mode 100644 index 0000000..74cba68 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/README.en-US.md @@ -0,0 +1,37 @@ +:: BASE_DOC :: + +## API + +### FormItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +arrow | Boolean | false | \- | N +content-align | String | - | options: left/right | N +for | String | - | \- | N +help | String | - | \- | N +label | String | '' | \- | N +label-align | String | - | options: left/right/top | N +label-width | String / Number | - | \- | N +name | String | - | \- | N +required-mark | Boolean | undefined | \- | N +rules | Array | - | Typescript: `Array` | N +show-error-message | Boolean | undefined | \- | N + +### FormItem Slots + +name | Description +-- | -- +help | \- +label | \- + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-form-item-horizontal-padding | 32rpx | - +--td-form-item-justify-content | space-between | - +--td-form-item-label-width | 160rpx | - +--td-form-item-vertical-padding | 32rpx | - diff --git a/uni_modules/tdesign-uniapp/components/form-item/README.md b/uni_modules/tdesign-uniapp/components/form-item/README.md new file mode 100644 index 0000000..ea0a629 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/README.md @@ -0,0 +1,37 @@ +:: BASE_DOC :: + +## API + +### FormItem Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +arrow | Boolean | false | 是否显示右侧箭头 | N +content-align | String | - | 表单内容对齐方式,优先级高于 Form.contentAlign。可选项:left/right | N +for | String | - | label 原生属性 | N +help | String | - | 表单项说明内容 | N +label | String | '' | 字段标签名称 | N +label-align | String | - | 表单字段标签对齐方式:左对齐、右对齐、顶部对齐。默认使用 Form 的对齐方式,优先级高于 Form.labelAlign。可选项:left/right/top | N +label-width | String / Number | - | 可以整体设置标签宽度,优先级高于 Form.labelWidth | N +name | String | - | 表单字段名称 | N +required-mark | Boolean | undefined | 是否显示必填符号(*),优先级高于 Form.requiredMark | N +rules | Array | - | 表单字段校验规则。TS 类型:`Array` | N +show-error-message | Boolean | undefined | 校验不通过时,是否显示错误提示信息,优先级高于 `Form.showErrorMessage` | N + +### FormItem Slots + +名称 | 描述 +-- | -- +help | 自定义 `help` 显示内容 +label | 自定义 `label` 显示内容 + +### CSS Variables + +组件提供了下列 CSS 变量,可用于自定义样式。 +名称 | 默认值 | 描述 +-- | -- | -- +--td-form-item-horizontal-padding | 32rpx | - +--td-form-item-justify-content | space-between | - +--td-form-item-label-width | 160rpx | - +--td-form-item-vertical-padding | 32rpx | - diff --git a/uni_modules/tdesign-uniapp/components/form-item/form-item.css b/uni_modules/tdesign-uniapp/components/form-item/form-item.css new file mode 100644 index 0000000..3845630 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/form-item.css @@ -0,0 +1,120 @@ +/* Less 支持测试 - 监听功能验证 */ +.t-form-item { + position: relative; + display: flex; + align-items: flex-start; + justify-content: var(--td-form-item-justify-content, space-between); + width: 100%; + padding: var(--td-form-item-horizontal-padding, 32rpx) var(--td-form-item-vertical-padding, 32rpx); + background-color: #ffffff; + --td-input-vertical-padding: 0rpx; + --td-textarea-vertical-padding: 0rpx; + --td-textarea-horizontal-padding: 0rpx; + box-sizing: border-box; +} +.t-form-item--top { + display: flex; + flex-direction: column; +} +.t-form-item--bordered { + border-bottom: 1rpx solid var(--td-border-color, var(--td-gray-color-3, #e7e7e7)); +} +.t-form-item--error .t-form-item__error { + color: var(--td-error-color, var(--td-error-color-6, #d54941)); +} +.t-form-item--success .t-form-item__success { + color: var(--td-success-color, var(--td-success-color-5, #2ba471)); +} +.t-form-item__label { + position: relative; + display: flex; + align-items: center; + width: var(--td-form-item-label-width, 160rpx); + margin-right: 16rpx; + font-size: var(--td-font-size-m, 32rpx); + color: var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))); + line-height: 1.5; +} +.t-form-item__label--required { + color: var(--td-error-color, var(--td-error-color-6, #d54941)); + margin-right: 4rpx; +} +.t-form-item__label--colon { + margin-left: 4rpx; +} +.t-form-item__label--left { + display: flex; + align-items: center; + justify-content: flex-start; +} +.t-form-item__label--right { + display: flex; + align-items: center; + justify-content: flex-end; +} +.t-form-item__label--top { + position: relative; + margin-right: 0; + width: 100%; +} +.t-form-item__controls { + flex: 1; + width: 100%; + margin-top: 8rpx; +} +.t-form-item__controls--left { + text-align: left; +} +.t-form-item__controls--right { + text-align: right; +} +.t-form-item__controls-content { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; +} +.t-form-item__help { + font-size: var(--td-font-size-m, 32rpx); + color: var(--td-font-gray-1, rgba(0, 0, 0, 0.9)); + line-height: 1.4; +} +.t-form-item__desc-link { + margin-top: 8rpx; + color: var(--td-primary-color-7, #0052d9); + font-size: var(--td-font-size-s, 24rpx); + line-height: 1.4; +} +.t-form-item__error { + margin-top: 4rpx; + font-size: var(--td-font-size-s, 24rpx); + line-height: 1.4; +} +.t-form-item__error--error { + color: var(--td-error-color, var(--td-error-color-6, #d54941)); +} +.t-form-item__error--warning { + color: var(--td-warning-color, var(--td-warning-color-5, #e37318)); +} +.t-form-item__success { + margin-top: 4rpx; + font-size: var(--td-font-size-s, 24rpx); + color: var(--td-success-color, var(--td-success-color-5, #2ba471)); + line-height: 1.4; +} +.t-form-item__arrow { + display: flex; + align-items: center; + margin-left: 8rpx; + color: var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4))); +} +.t-form-item__extra { + margin-left: 16rpx; +} +.desc-wrapper { + display: flex; + align-items: center; + gap: 12rpx; + height: 50rpx; + --td-button-font-weight: 400; +} diff --git a/uni_modules/tdesign-uniapp/components/form-item/form-item.vue b/uni_modules/tdesign-uniapp/components/form-item/form-item.vue new file mode 100644 index 0000000..5160ebc --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/form-item.vue @@ -0,0 +1,396 @@ + + + diff --git a/uni_modules/tdesign-uniapp/components/form-item/form-model.ts b/uni_modules/tdesign-uniapp/components/form-item/form-model.ts new file mode 100644 index 0000000..dd57bae --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/form-model.ts @@ -0,0 +1,198 @@ +// 验证结果接口 +interface ValidateResult { + result: boolean; + message?: string; + type?: string; +} + +// 工具函数:安全获取rule属性的实际值 +function getRuleValue(field: any, key?: string): any { + if (field && typeof field === 'object' && 'type' in field) { + // 针对type字段,过滤掉'error'和'warning' + if (key === 'type') { + const { value } = field; + if (value === 'error' || value === 'warning') { + return undefined; + } + return value; + } + return field.value; + } + // type字段只允许string + if (key === 'type') { + if (field === 'error' || field === 'warning') { + return undefined; + } + } + return field; +} + +// 验证状态枚举 +export const ValidateStatus = { + TO_BE_VALIDATED: 0, + SUCCESS: 1, + FAIL: 2, +}; + +// 执行单个验证规则 +async function executeRule(value: any, rule: any): Promise { + const result: ValidateResult = { + result: true, + }; + + // 必填验证 + const required = getRuleValue(rule.required); + if (required && (value === undefined || value === null || value === '' || value.length === 0)) { + result.result = false; + result.message = getRuleValue(rule.message) || '此字段为必填项'; + result.type = 'error'; + return result; + } + + // 如果值为空且不是必填,则跳过其他验证 + if (value === undefined || value === null || value === '' || value.length === 0) { + return result; + } + + // 列表最小值验证 + const min = getRuleValue(rule.min); + if (min !== undefined) { + const val = Array.isArray(value) ? value.length : Number(value); + if (val < Number(min)) { + result.result = false; + result.message = getRuleValue(rule.message) || `不能小于 ${min}`; + result.type = 'error'; + return result; + } + } + + // 列表最大值验证 + const max = getRuleValue(rule.max); + if (max !== undefined) { + const val = Array.isArray(value) ? value.length : Number(value); + if (val > Number(max)) { + result.result = false; + result.message = getRuleValue(rule.message) || `不能大于 ${max}`; + result.type = 'error'; + return result; + } + } + // 字符串最大长度验证 + const maxLength = getRuleValue(rule.maxLength); + if (maxLength !== undefined) { + const len = String(value).length; + if (len > Number(maxLength)) { + result.result = false; + result.message = getRuleValue(rule.message) || `长度不能超过 ${maxLength}`; + result.type = 'error'; + return result; + } + } + + // 字符串最小长度验证 + const minLength = getRuleValue(rule.minLength); + if (minLength !== undefined) { + const len = String(value).length; + if (len < Number(minLength)) { + result.result = false; + result.message = getRuleValue(rule.message) || `长度不能少于 ${minLength}`; + result.type = 'error'; + return result; + } + } + + // 固定长度验证 + const len = getRuleValue(rule.len); + if (len !== undefined) { + const strValue = String(value); + if (strValue.length !== Number(len)) { + result.result = false; + result.message = getRuleValue(rule.message) || `长度必须为 ${len}`; + result.type = 'error'; + return result; + } + } + + // 正则验证 + const pattern = getRuleValue(rule.pattern); + if (pattern) { + let regex; + if (pattern instanceof RegExp) { + regex = pattern; + } else if (typeof pattern === 'string') { + regex = new RegExp(pattern); + } else if (pattern && typeof pattern === 'object' && 'test' in pattern) { + regex = pattern; + } else { + result.result = false; + result.message = getRuleValue(rule.message) || '格式不正确'; + result.type = 'error'; + return result; + } + if (!regex.test(String(value))) { + result.result = false; + result.message = getRuleValue(rule.message) || '格式不正确'; + result.type = 'error'; + return result; + } + } + + // 类型验证 + const type = getRuleValue(rule.type, 'type'); + if (typeof type === 'string') { + let isValid = true; + if (type === 'email') { + isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value)); + if (!isValid) { + result.result = false; + result.message = getRuleValue(rule.message) || `${type} 格式不正确`; + result.type = 'error'; + return result; + } + } else if (type === 'url') { + try { + const url = new URL(String(value)); + isValid = !!url; + } catch (err) { + isValid = false; + } + if (!isValid) { + result.result = false; + result.message = getRuleValue(rule.message) || `${type} 格式不正确`; + result.type = 'error'; + return result; + } + } else if (type === 'number') { + isValid = !Number.isNaN(Number(value)); + if (!isValid) { + result.result = false; + result.message = getRuleValue(rule.message) || `${type} 格式不正确`; + result.type = 'error'; + return result; + } + } + } + // 自定义验证 + const validator = getRuleValue(rule.validator); + if (validator) { + const validateResult = await validator(value); + if (!validateResult) { + result.result = false; + result.message = getRuleValue(rule.message) || '验证失败'; + result.type = 'error'; + return result; + } + } + return result; +} + +// 验证函数 +export async function validate(value: any, rules: any[]): Promise { + const results: ValidateResult[] = []; + + const promises = rules.map(rule => executeRule(value, rule)); + const ruleResults = await Promise.all(promises); + results.push(...ruleResults); + + return results; +} diff --git a/uni_modules/tdesign-uniapp/components/form-item/props.ts b/uni_modules/tdesign-uniapp/components/form-item/props.ts new file mode 100644 index 0000000..8827384 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/props.ts @@ -0,0 +1,64 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +import type { TdFormItemProps } from './type'; +export default { + /** 是否显示右侧箭头 */ + arrow: Boolean, + /** 表单内容对齐方式,优先级高于 Form.contentAlign */ + contentAlign: { + type: String, + validator(val: TdFormItemProps['contentAlign']): boolean { + if (!val) return true; + return ['left', 'right'].includes(val); + }, + }, + /** label 原生属性 */ + for: { + type: String, + default: '', + }, + /** 表单项说明内容 */ + help: { + type: String, + }, + /** 字段标签名称 */ + label: { + type: String, + default: '' as TdFormItemProps['label'], + }, + /** 表单字段标签对齐方式:左对齐、右对齐、顶部对齐。默认使用 Form 的对齐方式,优先级高于 Form.labelAlign */ + labelAlign: { + type: String, + validator(val: TdFormItemProps['labelAlign']): boolean { + if (!val) return true; + return ['left', 'right', 'top'].includes(val); + }, + }, + /** 可以整体设置标签宽度,优先级高于 Form.labelWidth */ + labelWidth: { + type: [String, Number], + }, + /** 表单字段名称 */ + name: { + type: String, + default: '', + }, + /** 是否显示必填符号(*),优先级高于 Form.requiredMark */ + requiredMark: { + type: Boolean, + default: undefined, + }, + /** 表单字段校验规则 */ + rules: { + type: Array, + }, + /** 校验不通过时,是否显示错误提示信息,优先级高于 `Form.showErrorMessage` */ + showErrorMessage: { + type: Boolean, + default: undefined, + }, +}; diff --git a/uni_modules/tdesign-uniapp/components/form-item/type.ts b/uni_modules/tdesign-uniapp/components/form-item/type.ts new file mode 100644 index 0000000..8977529 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form-item/type.ts @@ -0,0 +1,7 @@ +/* eslint-disable */ + +/** + * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC + * */ + +export type { TdFormItemProps} from '../form/type'; diff --git a/uni_modules/tdesign-uniapp/components/form/README.en-US.md b/uni_modules/tdesign-uniapp/components/form/README.en-US.md new file mode 100644 index 0000000..30725b3 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form/README.en-US.md @@ -0,0 +1,137 @@ +--- +title: Form +description: Form consists of input, radio, select, checkbox and so on. With form, you can collect, verify and submit data. +spline: base +isComponent: true +toc: false +--- + +### 01 Component Type + +Base form + +{{ horizontal }} + +{{ vertical }} + +## API + +### Form Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +colon | Boolean | false | \- | N +content-align | String | left | options: left/right | N +data | Object | {} | Typescript: `FormData` | N +disabled | Boolean | undefined | \- | N +error-message | Object | - | Typescript: `FormErrorMessage` | N +label-align | String | right | options: left/right/top | N +label-width | String / Number | '81px' | \- | N +readonly | Boolean | undefined | \- | N +required-mark | Boolean | undefined | \- | N +required-mark-position | String | - | Display position of required symbols。options: left/right | N +reset-type | String | empty | options: empty/initial | N +rules | Object | - | Typescript: `FormRules` `type FormRules = { [field in keyof T]?: Array }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts) | N +scroll-to-first-error | String | - | options: ''/smooth/auto | N +show-error-message | Boolean | true | \- | N +submit-with-warning-message | Boolean | false | \- | N + +### Form Events + +name | params | description +-- | -- | -- +reset | `(context: { e?: FormResetEvent })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) +submit | `(context: SubmitContext)` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`interface SubmitContext { e?: FormSubmitEvent; validateResult: FormValidateResult; firstError?: string; fields?: any }`

`type FormValidateResult = boolean \| ValidateResultObj`

`type ValidateResultObj = { [key in keyof T]: boolean \| ValidateResultList }`

`type ValidateResultList = Array`

`type AllValidateResult = CustomValidateObj \| ValidateResultType`

`interface ValidateResultType extends FormRule { result: boolean }`

`type ValidateResult = { [key in keyof T]: boolean \| ErrorList }`

`type ErrorList = Array`
+validate | `(result: ValidateResultContext)` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`type ValidateResultContext = Omit, 'e'>`
+ +### FormInstanceFunctions 组件实例方法 + +name | params | return | description +-- | -- | -- | -- +clear-validate | `(fields?: Array)` | \- | required +reset | `(params?: FormResetParams)` | \- | required。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`interface FormResetParams { type?: 'initial' \| 'empty'; fields?: Array }`
+set-validate-message | `(message: FormValidateMessage)` | \- | required。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`type FormValidateMessage = { [field in keyof FormData]: FormItemValidateMessage[] }`

`interface FormItemValidateMessage { type: 'warning' \| 'error'; message: string }`
+submit | `(params?: { showErrorMessage?: boolean })` | \- | required +validate | `(params?: FormValidateParams)` | `Promise>` | required。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`interface FormValidateParams { fields?: Array; showErrorMessage?: boolean; trigger?: ValidateTriggerType }`

`type ValidateTriggerType = 'blur' \| 'change' \| 'submit' \| 'all'`
+ + +### FormItem Props + +name | type | default | description | required +-- | -- | -- | -- | -- +custom-style | Object | - | CSS(Cascading Style Sheets) | N +arrow | Boolean | false | \- | N +content-align | String | - | options: left/right | N +for | String | - | \- | N +help | String | - | \- | N +label | String | '' | \- | N +label-align | String | - | options: left/right/top | N +label-width | String / Number | - | \- | N +name | String | - | \- | N +required-mark | Boolean | undefined | \- | N +rules | Array | - | Typescript: `Array` | N +show-error-message | Boolean | undefined | \- | N + +### FormItem Slots + +name | Description +-- | -- +help | \- +label | \- + +### FormRule + +name | type | default | description | required +-- | -- | -- | -- | -- +boolean | Boolean | - | \- | N +date | Boolean / Object | - | Typescript: `boolean \| IsDateOptions` `interface IsDateOptions { format: string; strictMode: boolean; delimiters: string[] }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts) | N +email | Boolean / Object | - | Typescript: `boolean \| IsEmailOptions` `import type { IsEmailOptions } from '../common/common'`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts) | N +enum | Array | - | Typescript: `Array` | N +idcard | Boolean | - | \- | N +len | Number / Boolean | - | \- | N +max | Number / Boolean | - | \- | N +message | String | - | \- | N +min | Number / Boolean | - | \- | N +number | Boolean | - | \- | N +pattern | String / Object | - | Typescript: `RegExp \| string` | N +required | Boolean | - | \- | N +telnumber | Boolean | - | \- | N +trigger | String | change | Typescript: `ValidateTriggerType` | N +type | String | error | options: error/warning | N +url | Boolean / Object | - | Typescript: `boolean \| IsURLOptions` `import type { IsURLOptions } from '../common/common'`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts) | N +validator | Function | - | Typescript: `CustomValidator` `type CustomValidator = (val: ValueType) => CustomValidateResolveType \| Promise` `type CustomValidateResolveType = boolean \| CustomValidateObj` `interface CustomValidateObj { result: boolean; message: string; type?: 'error' \| 'warning' \| 'success' }` `type ValueType = any`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts) | N +whitespace | Boolean | - | \- | N + +### FormErrorMessage + +name | type | default | description | required +-- | -- | -- | -- | -- +boolean | String | - | \- | N +date | String | - | \- | N +enum | String | - | \- | N +idcard | String | - | \- | N +len | String | - | \- | N +max | String | - | \- | N +min | String | - | \- | N +number | String | - | \- | N +pattern | String | - | \- | N +required | String | - | \- | N +telnumber | String | - | \- | N +url | String | - | \- | N +validator | String | - | \- | N +whitespace | String | - | \- | N + +### CSS Variables + +The component provides the following CSS variables, which can be used to customize styles. +Name | Default Value | Description +-- | -- | -- +--td-form-bg-color | @bg-color-container | - +--td-form-border-radius | 0 | - +--td-form-padding | 0 | - +--td-form-readonly-bg-color | @bg-color-secondarycontainer | - +--td-form-item-horizontal-padding | 32rpx | - +--td-form-item-justify-content | space-between | - +--td-form-item-label-width | 160rpx | - +--td-form-item-vertical-padding | 32rpx | - diff --git a/uni_modules/tdesign-uniapp/components/form/README.md b/uni_modules/tdesign-uniapp/components/form/README.md new file mode 100644 index 0000000..212c134 --- /dev/null +++ b/uni_modules/tdesign-uniapp/components/form/README.md @@ -0,0 +1,146 @@ +--- +title: Form 表单 +description: 用以收集、校验和提交数据,一般由输入框、单选框、复选框、选择器等控件组成。 +spline: base +isComponent: true +toc: false +--- + +## 引入 + +可在 `main.ts` 或在需要使用的页面或组件中引入。 + +```js +import TForm from '@tdesign/uniapp/form/form.vue'; +import TFormItem from '@tdesign/uniapp/form-item/form-item.vue'; +``` + +### 01 组件类型 + +基础表单 + +{{ horizontal }} + +{{ vertical }} + +## API + +### Form Props + +名称 | 类型 | 默认值 | 描述 | 必传 +-- | -- | -- | -- | -- +custom-style | Object | - | 自定义样式 | N +colon | Boolean | false | 是否在表单标签字段右侧显示冒号 | N +content-align | String | left | 表单内容对齐方式:左对齐、右对齐。可选项:left/right | N +data | Object | {} | 表单数据。TS 类型:`FormData` | N +disabled | Boolean | undefined | 是否禁用整个表单 | N +error-message | Object | - | 表单错误信息配置,示例:`{ idcard: '请输入正确的身份证号码', max: '字符长度不能超过 ${max}' }`。TS 类型:`FormErrorMessage` | N +label-align | String | right | 表单字段标签对齐方式:左对齐、右对齐、顶部对齐。可选项:left/right/top | N +label-width | String / Number | '81px' | 可以整体设置label标签宽度,默认为81px | N +readonly | Boolean | undefined | 是否整个表单只读 | N +required-mark | Boolean | undefined | 是否显示必填符号(*),默认显示 | N +required-mark-position | String | - | 表单必填符号(*)显示位置。可选项:left/right | N +reset-type | String | empty | 重置表单的方式,值为 empty 表示重置表单为空,值为 initial 表示重置表单数据为初始值。可选项:empty/initial | N +rules | Object | - | 表单字段校验规则。TS 类型:`FormRules` `type FormRules = { [field in keyof T]?: Array }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts) | N +scroll-to-first-error | String | - | 表单校验不通过时,是否自动滚动到第一个校验不通过的字段,平滑滚动或是瞬间直达。值为空则表示不滚动。可选项:''/smooth/auto | N +show-error-message | Boolean | true | 校验不通过时,是否显示错误提示信息,统一控制全部表单项。如果希望控制单个表单项,请给 FormItem 设置该属性 | N +submit-with-warning-message | Boolean | false | 【讨论中】当校验结果只有告警信息时,是否触发 `submit` 提交事件 | N + +### Form Events + +名称 | 参数 | 描述 +-- | -- | -- +reset | `(context: { e?: FormResetEvent })` | 表单重置时触发。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/common/common.ts) +submit | `(context: SubmitContext)` | 表单提交时触发。其中 `context.validateResult` 表示校验结果,`context.firstError` 表示校验不通过的第一个规则提醒。`context.validateResult` 值为 `true` 表示校验通过;如果校验不通过,`context.validateResult` 值为校验结果列表。
【注意】⚠️ 默认情况,输入框按下 Enter 键会自动触发提交事件,如果希望禁用这个默认行为,可以给输入框添加 enter 事件,并在事件中设置 `e.preventDefault()`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`interface SubmitContext { e?: FormSubmitEvent; validateResult: FormValidateResult; firstError?: string; fields?: any }`

`type FormValidateResult = boolean \| ValidateResultObj`

`type ValidateResultObj = { [key in keyof T]: boolean \| ValidateResultList }`

`type ValidateResultList = Array`

`type AllValidateResult = CustomValidateObj \| ValidateResultType`

`interface ValidateResultType extends FormRule { result: boolean }`

`type ValidateResult = { [key in keyof T]: boolean \| ErrorList }`

`type ErrorList = Array`
+validate | `(result: ValidateResultContext)` | 校验结束后触发,result 值为 true 表示校验通过;如果校验不通过,result 值为校验结果列表。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/form/type.ts)。
`type ValidateResultContext = Omit, 'e'>`
+ +### FormInstanceFunctions 组件实例方法 + +名称 | 参数 | 返回值 | 描述 +-- | -- | -- | -- +clear-validate | `(fields?: Array)` | \- | 必需。清空校验结果。可使用 fields 指定清除部分字段的校验结果,fields 值为空则表示清除所有字段校验结果。清除邮箱校验结果示例:`clearValidate(['email'])` +reset | `(params?: FormResetParams)` | \- | 必需。重置表单,表单里面没有重置按钮`