pull/1/head
张宇 2 years ago
parent 1ccbf64d4a
commit 853daaf00a

8
.gitignore vendored

@ -0,0 +1,8 @@
.project
.hbuilderx
.mini-ide
.idea
unpackage
.DS_Store
node_modules

@ -12,6 +12,12 @@
} }
</script> </script>
<style> <style lang="scss">
/*每个页面公共css */ /*每个页面公共css */
@import "tailwindcss/base";
@import "tailwindcss/utilities";
page {
min-height: 100vh;
}
</style> </style>

@ -0,0 +1,13 @@
const files = require.context("./modules", false, /\.js$/),
api = {};
files.keys().forEach((key) => {
const last = key.indexOf(".js"),
name = key.slice(2, last);
api[name] = files(key).default;
});
export default {
...api,
};

@ -0,0 +1,14 @@
import {
request
} from "@/utils/request";
export default {
// 登录
login(data) {
return request({
url: "/admin/auth/login",
method: "POST",
data,
});
},
};

@ -0,0 +1,26 @@
<template>
<view class="myTable">
<wd-pagination v-model="value" :total="total" :page-size="page" @change="handleChange" show-icon show-message />
</view>
</template>
<script setup>
import { ref } from 'vue'
const props = defineProps({
apiObj: {
type: Function,
default: () => (() => { })
}
})
const value = ref("")
const total = ref(0)
const page = ref(1)
function handleChange(e) {
console.log(e);
}
</script>
<style lang="scss"></style>

@ -0,0 +1,4 @@
export default {
host: "https://saasdemo.byin.vip",
NETWORK_TIME_OUT: 15000,
};

@ -0,0 +1,12 @@
{
"devDependencies": {
"autoprefixer": "^10.4.8",
"postcss": "^8.4.14",
"postcss-rem-to-responsive-pixel": "^5.1.3",
"tailwindcss": "^3.1.7",
"weapp-tailwindcss-webpack-plugin": "^1.6.10"
},
"dependencies": {
"js-md5": "^0.8.3"
}
}

@ -1,10 +1,59 @@
{ {
"pages": [ //pageshttps://uniapp.dcloud.io/collocation/pages "pages": [
{ {
"path": "pages/index/index", "path": "pages/index/index",
"style": { "style": {
"navigationBarTitleText": "uni-app" "navigationBarTitleText": "uni-app"
} }
},
{
"path": "pages/login/index",
"style": {
"navigationBarTitleText": "登录"
}
}
],
"subPackages": [
{
"root": "store",
"pages": [
{
"path": "index/index",
"style": {
"navigationBarTitleText": "商城首页",
"enablePullDownRefresh": false,
"mp-alipay": {
"transparentTitle": "always",
"titlePenetrate": "YES",
"gestureBack": "YES"
}
}
},
{
"path": "goods/index",
"style": {
"navigationBarTitleText": "商品管理",
"enablePullDownRefresh": false,
"mp-alipay": {
"transparentTitle": "always",
"titlePenetrate": "YES",
"gestureBack": "YES"
}
}
},
{
"path": "order/index",
"style": {
"navigationBarTitleText": "订单管理",
"enablePullDownRefresh": false,
"mp-alipay": {
"transparentTitle": "always",
"titlePenetrate": "YES",
"gestureBack": "YES"
}
}
}
]
} }
], ],
"globalStyle": { "globalStyle": {

@ -1,25 +1,18 @@
<template> <template>
<view class="content"> <view class="content">
<image class="logo" src="/static/logo.png"></image> <wd-button>主要按钮</wd-button>
<view class="text-area"> <wd-button type="success">成功按钮</wd-button>
<text class="title">{{title}}</text> <wd-button type="info">信息按钮</wd-button>
</view> <wd-button type="warning">警告按钮</wd-button>
<wd-button type="error">危险按钮</wd-button>
</view> </view>
</template> </template>
<script> <script setup>
export default { import utils from '@/utils/utils.js'
data() { const user_info = uni.getStorageSync("user_info");
return { if (user_info.type == 1) {
title: 'Hello' utils.toUrl("/store/index/index")
}
},
onLoad() {
},
methods: {
}
} }
</script> </script>

@ -0,0 +1,91 @@
<template>
<view class="content min-h-dvh flex items-center justify-center">
<wd-form class="mb-36" ref="form" :model="model">
<wd-cell-group border>
<wd-input label="用户名" label-width="100px" prop="username" clearable v-model="model.username" placeholder="请输入用户名"
:rules="[{ required: true, message: '请填写用户名' }]" />
<wd-input label="密码" label-width="100px" prop="password" show-password clearable v-model="model.password"
placeholder="请输入密码" :rules="[{ required: true, message: '请填写密码' }]" />
</wd-cell-group>
<view class="footer mt-4">
<wd-button type="primary" size="medium" @click="handleSubmit" block>登录</wd-button>
</view>
</wd-form>
</view>
</template>
<script setup>
import { ref } from 'vue'
import user from '@/api/modules/user.js'
import utils from '@/utils/utils.js'
import md5 from 'js-md5';
const model = ref({
username: "",
password: "",
})
const form = ref(null)
function handleSubmit() {
form.value
.validate()
.then(({ valid, errors }) => {
if (valid) {
user.login({
...model.value,
password: md5(model.value.password)
}).then(res => {
if (res.code == 0) {
uni.setStorageSync("token", res.data.access_token);
uni.setStorageSync("user_info", res.data.user_info);
uni.showToast({
icon: "none",
title: "登录成功",
});
if (res.data.type == 1) {
utils.toUrl("/store/index/index")
}
}
})
}
})
.catch((error) => {
console.log(error, 'error')
})
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>

@ -0,0 +1,11 @@
<template>
<view class="content p-4">
</view>
</template>
<script setup>
import utils from '@/utils/utils.js'
</script>
<style></style>

@ -0,0 +1,59 @@
<template>
<view class="content p-4">
<view class="w-full grid grid-cols-2 gap-4">
<view @click="utils.toUrl('/store/order/index')"
class="text-white bg-gradient-to-r from-orange-200 to-orange-400 shadow rounded-md p-4">
<wd-icon name="circle1" size="24px"></wd-icon>
<view class="mt-2 text-end font-bold ">订单中心</view>
</view>
<view @click="utils.toUrl('/store/goods/index')"
class="text-white bg-gradient-to-r from-orange-200 to-orange-400 shadow rounded-md p-4">
<wd-icon name="goods" size="24px"></wd-icon>
<view class="mt-2 text-end font-bold">商品管理</view>
</view>
<!-- <view class="bg-dark shadow-md rounded-md p-4">
<wd-icon name="goods" size="22px"></wd-icon>
<view class="mt-2 text-end font-bold">订单中心</view>
</view>
<view class="bg-dark shadow-md rounded-md p-4">
<wd-icon name="goods" size="22px"></wd-icon>
<view class="mt-2 text-end font-bold">订单中心</view>
</view> -->
</view>
</view>
</template>
<script setup>
import utils from '@/utils/utils.js'
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>

@ -0,0 +1,11 @@
<template>
<view class="content p-4">
</view>
</template>
<script setup>
import utils from '@/utils/utils.js'
</script>
<style></style>

@ -0,0 +1,15 @@
const path = require("path");
const resolve = (p) => {
return path.resolve(__dirname, p);
};
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./index.html", "./**/*.vue"].map(resolve),
theme: {
extend: {},
},
plugins: [],
corePlugins: {
preflight: false,
},
};

@ -0,0 +1,8 @@
// tsconfig.json
{
"compilerOptions": {
"types": [
"wot-design-uni/global.d.ts"
]
}
}

@ -0,0 +1,777 @@
## 0.2.32023-12-27
## 0.2版本正式发布支持Form自定义表单校验
> 请注意Input组件有破坏性更新
### [0.2.3](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.2.2...v0.2.3) (2023-12-27)
### ✏️ Documentation | 文档
* ✏️ 修复动态表单演示页面标题错误的问题 ([7c65359](https://github.com/Moonofweisheng/wot-design-uni/commit/7c65359c88614ae53c4800d0946d8e09ef547a26))
# 更新日志
### [0.2.3](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.2.2...v0.2.3) (2023-12-27)
### ✏️ Documentation | 文档
* ✏️ 修复动态表单演示页面标题错误的问题 ([7c65359](https://github.com/Moonofweisheng/wot-design-uni/commit/7c65359c88614ae53c4800d0946d8e09ef547a26))
### [0.2.2](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.2.1...v0.2.2) (2023-12-26)
### 🐛 Bug Fixes | Bug 修复
* 🐛 移除 Swiper 的list中不必填的属性 ([672f680](https://github.com/Moonofweisheng/wot-design-uni/commit/672f680051009edd23a67d4e32722839f519d9ba))
### ✏️ Documentation | 文档
* ✏️ 修复 Textarea 文档示例错误的问题 ([7da3a4c](https://github.com/Moonofweisheng/wot-design-uni/commit/7da3a4cdfe35f87193f57139de0819130424d6ea))
* ✏️ 优化 Form 相关组件的文档介绍 ([6a0a7a7](https://github.com/Moonofweisheng/wot-design-uni/commit/6a0a7a730fcd8904609f8a48339e1079baf3e9be))
### [0.2.1](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.2.0...v0.2.1) (2023-12-25)
### ✏️ Documentation | 文档
* ✏️ PassowrdInput 密码输入框组件增加文档标题 ([cccc4cc](https://github.com/Moonofweisheng/wot-design-uni/commit/cccc4cc0d4952f594e5387bad32a7248009ebd9e))
## [0.2.0](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.70...v0.2.0) (2023-12-24)
### ✨ Features | 新功能
* ✨ 新增 Form 表单组件 ([c8086d6](https://github.com/Moonofweisheng/wot-design-uni/commit/c8086d624c01bfbae3b9dfc324d33d6bfe46e041))
* ✨ 新增 PasswordInput 密码输入框 ([b8c68f9](https://github.com/Moonofweisheng/wot-design-uni/commit/b8c68f92f7ddbf3ae2a58bf36593a0cd0340f225))
* ✨ 新增 textarea 文本域组件 ([ed56bcd](https://github.com/Moonofweisheng/wot-design-uni/commit/ed56bcdb0337c198e8834182d47d1cd83513091d))
* ✨ 增加父子组件 Provide/Inject 的hooks ([eb971d4](https://github.com/Moonofweisheng/wot-design-uni/commit/eb971d4e86733b0337de0c63f26b373424a842f0))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Input 组件在支付宝平台存在一个默认padding的问题 ([921c906](https://github.com/Moonofweisheng/wot-design-uni/commit/921c90606e05ea1b29cdee39cde7858907b2cd4d))
### ✏️ Documentation | 文档
* ✏️ 调整主题定制相关文档 ([2d5b1af](https://github.com/Moonofweisheng/wot-design-uni/commit/2d5b1af2787d33ec24eb6e6ab9db6ff941d80c2b))
* ✏️ 增加 textarea 文本域组件功能的迁移说明 ([61ebc7d](https://github.com/Moonofweisheng/wot-design-uni/commit/61ebc7db1e1db3e8634a87c24fae0869f4b6f17b))
* ✏️ PasswordInput 密码输入框文档增加版本介绍 ([56ccf60](https://github.com/Moonofweisheng/wot-design-uni/commit/56ccf6005e502237e1ff45b5b52e852b402a10c9))
### [0.1.70](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.69...v0.1.70) (2023-12-23)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 picker 的列value为0时回显异常的问题 ([2ba93c3](https://github.com/Moonofweisheng/wot-design-uni/commit/2ba93c328c54c41b6e1e9ead946e9c46e278ebb9)), closes [#155](https://github.com/Moonofweisheng/wot-design-uni/issues/155)
### [0.1.69](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.68...v0.1.69) (2023-12-13)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Notify 使用文档错误的问题 ([3fe6423](https://github.com/Moonofweisheng/wot-design-uni/commit/3fe6423c9c6727eaa3655029bf862b9be182b70b)), closes [#148](https://github.com/Moonofweisheng/wot-design-uni/issues/148)
### [0.1.68](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.67...v0.1.68) (2023-12-07)
### ✏️ Documentation | 文档
* ✏️ 增加 Gap 组件的文档 ([9e3a8c1](https://github.com/Moonofweisheng/wot-design-uni/commit/9e3a8c1506f9ef93f9f0155ac948c8bc4d215ea8))
### [0.1.67](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.66...v0.1.67) (2023-12-06)
### ✨ Features | 新功能
* ✨ 新增 Gap 间隔槽组件 ([#147](https://github.com/Moonofweisheng/wot-design-uni/issues/147)) ([fa7cd16](https://github.com/Moonofweisheng/wot-design-uni/commit/fa7cd163810cbcd63fc2a99a6a92ccd8a803b2f1))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复Picker组件v-model数据不更新的问题 ([e60f475](https://github.com/Moonofweisheng/wot-design-uni/commit/e60f47538130625a8c8f93e3a6795b7e71588002)), closes [#146](https://github.com/Moonofweisheng/wot-design-uni/issues/146)
### [0.1.66](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.65...v0.1.66) (2023-12-01)
### ✨ Features | 新功能
* ✨ Tag 组件type增加属性值default ([#143](https://github.com/Moonofweisheng/wot-design-uni/issues/143)) ([48f0e4b](https://github.com/Moonofweisheng/wot-design-uni/commit/48f0e4b3c484755a0da4eb11fa814c6b52be74b1))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 ActionSheet 组件设置custom-class无效的问题 ([1e5bcf7](https://github.com/Moonofweisheng/wot-design-uni/commit/1e5bcf7d0a8be66b80de81a0359d3fb429fdf52d))
### [0.1.65](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.64...v0.1.65) (2023-11-29)
### ✨ Features | 新功能
* ✨ 新增 NumberKeyboard 数字键盘组件 ([#139](https://github.com/Moonofweisheng/wot-design-uni/issues/139)) ([7bb4455](https://github.com/Moonofweisheng/wot-design-uni/commit/7bb4455c6d9eca179ba174b8ad0da6ae8a09defc))
* Tag新增类型标签添加slot ([7977dbb](https://github.com/Moonofweisheng/wot-design-uni/commit/7977dbbe3565eb06b700f2c55a31803647760fa9))
### ✏️ Documentation | 文档
* ✏️ 新增关于 Tag 组件的add插槽的介绍 ([537fa5e](https://github.com/Moonofweisheng/wot-design-uni/commit/537fa5ebf8dcdc14ee12fc1f1e5a5f829653a14a))
### [0.1.64](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.63...v0.1.64) (2023-11-25)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复DateTimePicker标题展示和模式为区间时before-confirm参数错误的问题 ([7bcd12b](https://github.com/Moonofweisheng/wot-design-uni/commit/7bcd12ba633224dd42b160f0b43fadc99ac76707))
### [0.1.63](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.62...v0.1.63) (2023-11-23)
### ✨ Features | 新功能
* ✨ CountDown 倒计时组件支持custom-class和custom-style ([dd60d6d](https://github.com/Moonofweisheng/wot-design-uni/commit/dd60d6df855cc49eba36beb7bcfb9eeb96ed0c82))
* ✨ Search 搜索组件支持自动聚焦和清空后自动聚焦 ([2e3ab63](https://github.com/Moonofweisheng/wot-design-uni/commit/2e3ab63d3dad582c44d9d9f8d2b57e21ee22f380))
### ✏️ Documentation | 文档
* ✏️ 修复文档中config-provider等页面的错误 ([f71e641](https://github.com/Moonofweisheng/wot-design-uni/commit/f71e6412e770f2043e4cb9a35950a68aacf0e542))
### [0.1.62](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.61...v0.1.62) (2023-11-22)
### ✨ Features | 新功能
* ✨ Badge 徽标组件支持控制是否显示0值 ([65cfb2c](https://github.com/Moonofweisheng/wot-design-uni/commit/65cfb2ca15c29783f6de48de0bda4ddc3d43664a))
* ✨ Upload 上传组件支持h5端获取文件后缀名 ([59fe70c](https://github.com/Moonofweisheng/wot-design-uni/commit/59fe70c0d0c55c7ecca26902b41599be94e34fe3))
### ✏️ Documentation | 文档
* ✏️ 优化 Upload 文档中关于云存储内容的样式 ([607ce05](https://github.com/Moonofweisheng/wot-design-uni/commit/607ce05100ffef9c5c98b503e580a718551f5552))
### [0.1.61](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.60...v0.1.61) (2023-11-20)
### ✏️ Documentation | 文档
* ✏️ 修复 Tag 标签组件文档错乱的问题 ([502203b](https://github.com/Moonofweisheng/wot-design-uni/commit/502203bebf08d7e6851b8dd2f94768f6c620da26))
* ✏️ ImgCropper 组件演示页面增加微信小程序隐私协议弹框 ([3af9246](https://github.com/Moonofweisheng/wot-design-uni/commit/3af9246d1f1876ba3c83f6829a9716ad37ae5829))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Calendar 日历组件存在未定义属性导致警告的问题 ([d0d3152](https://github.com/Moonofweisheng/wot-design-uni/commit/d0d3152a5ca1a3e06135b12f4565211258619d1f))
* 🐛 修复 CountDown 倒计时组件文档手动控制示例错误的问题 ([3c373fb](https://github.com/Moonofweisheng/wot-design-uni/commit/3c373fbf3af02c8a73ddd3a4063f9408ec4f33ff))
* 🐛 修复 SelectPicker 组件loading状态仍可以滚动的问题 ([c5a1bff](https://github.com/Moonofweisheng/wot-design-uni/commit/c5a1bfff576aad8a6f03288a6a12a8ed24f776f2))
### ✨ Features | 新功能
* ✨ Upload 上传组件支持上传至云存储 ([523a6be](https://github.com/Moonofweisheng/wot-design-uni/commit/523a6be40b065c4cc28cfe81fdcb200142d9a455))
### [0.1.60](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.59...v0.1.60) (2023-11-20)
### ✏️ Documentation | 文档
* ✏️ 修复 CountDown 倒计时组件默认slot用法的介绍错误的问题 ([2fbb88f](https://github.com/Moonofweisheng/wot-design-uni/commit/2fbb88fcd9fe21de09637d48149278d9aef5e1d9))
### [0.1.59](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.58...v0.1.59) (2023-11-19)
### [0.1.58](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.57...v0.1.58) (2023-11-19)
### ✨ Features | 新功能
* ✨ 新增 CountDown 倒计时组件 ([a805d04](https://github.com/Moonofweisheng/wot-design-uni/commit/a805d04827600525c08fbc1848cb3a524b48e81a))
* ✨ CountDown 倒计时组件支持小程序 ([22f249a](https://github.com/Moonofweisheng/wot-design-uni/commit/22f249ae5bdedea1ecebfe31350c7b5a1e394f2e))
### [0.1.57](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.56...v0.1.57) (2023-11-17)
### ✨ Features | 新功能
* ✨ 新增 Fab 悬浮动作按钮组件 ([3c526fe](https://github.com/Moonofweisheng/wot-design-uni/commit/3c526feb102ccf17da0678a6e1c004bb82d57f0f))
### ✏️ Documentation | 文档
* ✏️ 修复 Cell 组件点击反馈的介绍错误的问题 ([9096d81](https://github.com/Moonofweisheng/wot-design-uni/commit/9096d81f514348d6103f33590387d0406b217198))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Table 表格组件数字英文换行失效的问题 ([47ac339](https://github.com/Moonofweisheng/wot-design-uni/commit/47ac3390cb58d250129a9db6ade8d90f980855a1))
### [0.1.56](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.55...v0.1.56) (2023-11-12)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Tabbar 标签栏组件 bordered 属性无效的问题 ([69c2656](https://github.com/Moonofweisheng/wot-design-uni/commit/69c265638c6fc36a1479a37c052a572da333006f))
### [0.1.55](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.54...v0.1.55) (2023-11-12)
### 🐛 Bug Fixes | Bug 修复
* 修正README.md 中[贡献指南]链接错误,跳转失败的问题。 ([071de88](https://github.com/Moonofweisheng/wot-design-uni/commit/071de88f98fa90492032193606941fbcdfe9283d))
### ✨ Features | 新功能
* ✨ Swiper 轮播图组件增加imageMode属性支持自定义图片裁剪缩放模式 ([4205c01](https://github.com/Moonofweisheng/wot-design-uni/commit/4205c0149d3224e318ef21563e96f0a843ad13bc))
### [0.1.54](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.53...v0.1.54) (2023-11-10)
### 🐛 Bug Fixes | Bug 修复
* 修正ColPicker多列选择器v-model类型提示使用Record<string, any>[]实际上的数据是单维数组的问题 ([c490ac6](https://github.com/Moonofweisheng/wot-design-uni/commit/c490ac607e6f9eb7207b90943b83ce1696a30fd4))
### [0.1.53](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.52...v0.1.53) (2023-11-09)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Input 输入框组件包含无用显示内容的问题 ([13a3106](https://github.com/Moonofweisheng/wot-design-uni/commit/13a3106055d66083d640912bfff67f2e08781f03))
### [0.1.52](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.51...v0.1.52) (2023-11-09)
### ✨ Features | 新功能
* ✨ Input绑定值为null时的警告从error改为warn ([70f1407](https://github.com/Moonofweisheng/wot-design-uni/commit/70f1407eec057668da0117c9861b523ab667da70))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Badge 徽标组件暗黑模式下边框颜色未兼容的问题 ([c0a0b5a](https://github.com/Moonofweisheng/wot-design-uni/commit/c0a0b5aaea238f144978a3a9b2ab6db202dfdc21)), closes [#115](https://github.com/Moonofweisheng/wot-design-uni/issues/115)
### [0.1.51](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.50...v0.1.51) (2023-11-06)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复引入错误依赖的问题 ([15bc876](https://github.com/Moonofweisheng/wot-design-uni/commit/15bc876a3026319c88ce4b757593243f9869ce39))
### [0.1.50](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.49...v0.1.50) (2023-11-06)
### ✨ Features | 新功能
* ✨ SidebarItem、TabbarItem、GridItem组件增加徽标自定义属性badge-props ([7143098](https://github.com/Moonofweisheng/wot-design-uni/commit/71430989bff81a137fc5199eeeefe0a4da3ce31d))
### [0.1.49](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.48...v0.1.49) (2023-11-05)
### ✨ Features | 新功能
* ✨ 新增 Sidebar 侧边栏组件 ([#113](https://github.com/Moonofweisheng/wot-design-uni/issues/113)) ([d189378](https://github.com/Moonofweisheng/wot-design-uni/commit/d189378a02aeb36c275218358849a187e88ce687))
### [0.1.48](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.47...v0.1.48) (2023-11-04)
### ✨ Features | 新功能
* ✨ Input 输入框增加支持微信小程序原生属性always-embed ([57d00ec](https://github.com/Moonofweisheng/wot-design-uni/commit/57d00ececd9849a2d21f59fc863b6ad8933419d8))
### [0.1.47](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.46...v0.1.47) (2023-11-02)
### ✏️ Documentation | 文档
* ✏️ 修复 Image 图片组件演示demo样式不友好的问题 ([cbdab19](https://github.com/Moonofweisheng/wot-design-uni/commit/cbdab1959efff642a60a351ffd6454dcd173b9a4))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Input 组件为textarea类型时show-confirm-bar不生效的问题 ([eebec8a](https://github.com/Moonofweisheng/wot-design-uni/commit/eebec8a74fa9399bfd305cadb1a9b6a526e88568))
* 🐛 img 预览demo布局错位 ([0397809](https://github.com/Moonofweisheng/wot-design-uni/commit/0397809d81010e109c238eefea56c82bd06a50cb))
### ✨ Features | 新功能
* ✨ 图片添加圆角大小设置 ([98edaed](https://github.com/Moonofweisheng/wot-design-uni/commit/98edaed6cb1c81b9ed6e05561814aaa51b31b2b3))
* ✨ Badge 徽标组件增加customStyle自定义样式 ([d194106](https://github.com/Moonofweisheng/wot-design-uni/commit/d194106f07fb46f6a979967ba327dee1b41ca70e))
### [0.1.46](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.45...v0.1.46) (2023-11-02)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Tabs 组件在微信小程序端有概率不绘制下划线的问题 ([d70ec65](https://github.com/Moonofweisheng/wot-design-uni/commit/d70ec65998d1ca1fbdbd3b4ef1af4a292a597f55))
### [0.1.45](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.44...v0.1.45) (2023-11-01)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Switch 开关组件自定义大小无效的问题 ([15ffa1f](https://github.com/Moonofweisheng/wot-design-uni/commit/15ffa1f9172d3c11722c71ca370c1016e25f2b41))
### [0.1.44](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.43...v0.1.44) (2023-11-01)
### ✨ Features | 新功能
* ✨ 使用 useQueue hook替换clickOut实现关闭多个气泡等组件的功能 ([3a24999](https://github.com/Moonofweisheng/wot-design-uni/commit/3a24999c1e28414b12806cad3c3bc6f7a445593f))
### ✏️ Documentation | 文档
* ✏️ 优化演示项目首页样式 ([e98f30e](https://github.com/Moonofweisheng/wot-design-uni/commit/e98f30e0125e287d2bc5cdbcfff69a35cb7436d8))
### [0.1.43](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.42...v0.1.43) (2023-10-31)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Tooltip 文字提示组件微信端关闭按钮样式错误的问题 ([ec1b949](https://github.com/Moonofweisheng/wot-design-uni/commit/ec1b9490b9517d0e4ebe4a5130a418a684b1a589))
### [0.1.42](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.41...v0.1.42) (2023-10-31)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Popover、Tooltip 组件展开过程中无法遮盖图片的问题 ([db009c1](https://github.com/Moonofweisheng/wot-design-uni/commit/db009c1229413f7ecc446e3e3a1db4d7678268b3))
### [0.1.41](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.40...v0.1.41) (2023-10-31)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Popover、Tooltip 组件展开切换动画不流畅的问题 ([c765b08](https://github.com/Moonofweisheng/wot-design-uni/commit/c765b085a6018433bb95fd7647c091393e2e6e7c)), closes [#101](https://github.com/Moonofweisheng/wot-design-uni/issues/101)
### [0.1.40](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.39...v0.1.40) (2023-10-30)
### ✏️ Documentation | 文档
* ✏️ 移除文档中 Search 组件未支持的插槽 ([325e140](https://github.com/Moonofweisheng/wot-design-uni/commit/325e14046de38eb7c4e792a64a290c9997af5cba))
### [0.1.39](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.38...v0.1.39) (2023-10-29)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 DateTimePicker 日期选择器组件双向绑定错误的问题 ([1b13873](https://github.com/Moonofweisheng/wot-design-uni/commit/1b13873cef4b7c760698abce7dca500cd548c660))
### ✨ Features | 新功能
* ✨ 新增 Table 表格组件 ([#98](https://github.com/Moonofweisheng/wot-design-uni/issues/98)) ([c8395f8](https://github.com/Moonofweisheng/wot-design-uni/commit/c8395f8a7e1ad041b003672081b715a7c755adc1))
* ✨ Collpase 折叠面板组件增加分割线 ([adc6633](https://github.com/Moonofweisheng/wot-design-uni/commit/adc6633ad80b74e801a48a50917c9a2d378de9e0)), closes [#97](https://github.com/Moonofweisheng/wot-design-uni/issues/97)
### [0.1.38](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.37...v0.1.38) (2023-10-25)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Upload 组件showLimitNum属性名拼写错误的问题 ([301cbff](https://github.com/Moonofweisheng/wot-design-uni/commit/301cbff20facf60b5645108972f22c8c14235f87))
### [0.1.37](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.36...v0.1.37) (2023-10-25)
### ✏️ Documentation | 文档
* ✏️ 修复Tabbar组件类型警告的问题并优化文档相关链接 ([28c3cef](https://github.com/Moonofweisheng/wot-design-uni/commit/28c3cefdfc24f3f6f2669de5f93456a0db281f72))
* ✏️ issues 模板更新 ([e34c5cd](https://github.com/Moonofweisheng/wot-design-uni/commit/e34c5cded1c850d22165c544a3c6a351701479ec))
### [0.1.36](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.35...v0.1.36) (2023-10-21)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Tabs 指定name时双向绑定值仍为index的问题 ([756cad8](https://github.com/Moonofweisheng/wot-design-uni/commit/756cad8d6fcaefb0bf73f1cd2b9f4a24f5827b37)), closes [#91](https://github.com/Moonofweisheng/wot-design-uni/issues/91)
### [0.1.35](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.34...v0.1.35) (2023-10-21)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Tabs 组件通过name匹配时抛出异常的问题 ([b36fb1f](https://github.com/Moonofweisheng/wot-design-uni/commit/b36fb1f9def0aa99c521ffc20c6cae8074b56838)), closes [#91](https://github.com/Moonofweisheng/wot-design-uni/issues/91)
### [0.1.34](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.33...v0.1.34) (2023-10-18)
### ✨ Features | 新功能
* ✨ SelectPicker 单复选选择器增加 scroll-into-view 属性支持定位到选中值 ([00b27c1](https://github.com/Moonofweisheng/wot-design-uni/commit/00b27c187438426471dab2ea15bc39d627773ab2)), closes [#68](https://github.com/Moonofweisheng/wot-design-uni/issues/68)
### [0.1.33](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.32...v0.1.33) (2023-10-17)
### ✨ Features | 新功能
* ✨ 新增 Navbar 导航栏组件 ([#86](https://github.com/Moonofweisheng/wot-design-uni/issues/86)) ([5f66f71](https://github.com/Moonofweisheng/wot-design-uni/commit/5f66f713fae9e693fda4a472bc3e0767a1a6d3e8))
### [0.1.32](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.31...v0.1.32) (2023-10-16)
### ✨ Features | 新功能
* ✨ Tabs 组件调整为受控组件 ([b2a70bc](https://github.com/Moonofweisheng/wot-design-uni/commit/b2a70bc8f32800119924b29283db760af8a3ddb1)), closes [#82](https://github.com/Moonofweisheng/wot-design-uni/issues/82)
### [0.1.31](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.30...v0.1.31) (2023-10-16)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复wd-popup组件的click-modal方法错误调用的问题 ([224e3e5](https://github.com/Moonofweisheng/wot-design-uni/commit/224e3e53f912a236e8e24f8afef64fa09f6a89aa))
### ✏️ Documentation | 文档
* ✏️ 更新文档中关于组件数量的介绍 ([4fa7355](https://github.com/Moonofweisheng/wot-design-uni/commit/4fa7355e69a8bb32d1c9924cfc717caaf3345ee5))
* ✏️ 增加 Overlay 遮罩层组件的文档 ([3bca4b6](https://github.com/Moonofweisheng/wot-design-uni/commit/3bca4b65c2bc7cd7f90e8b9513c619799f9a438b))
### [0.1.30](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.29...v0.1.30) (2023-10-14)
### ✨ Features | 新功能
* ✨ 新增 Overlay 遮罩层组件 ([41b2c4f](https://github.com/Moonofweisheng/wot-design-uni/commit/41b2c4f3677dcce9c3e18875b7a1ec32d02b3d6a))
* ✨ Popup 弹出层组件增加 lock-scroll 属性 ([a6987e6](https://github.com/Moonofweisheng/wot-design-uni/commit/a6987e65da2f121d4e95c1b5cb271ed8f9a06e58))
### [0.1.29](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.28...v0.1.29) (2023-10-13)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Tabbar 标签栏组件固定底部时不设置placeholder情况下仍占据相应空间的问题 ([b58f9df](https://github.com/Moonofweisheng/wot-design-uni/commit/b58f9dfc8230e9427f2af658c8315c5b022b232c))
### [0.1.28](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.27...v0.1.28) (2023-10-12)
### ✏️ Documentation | 文档
* ✏️ 文档样式优化 ([f21ecb9](https://github.com/Moonofweisheng/wot-design-uni/commit/f21ecb9dfbfbf0afe49a760d24ebdc08e4799717))
### [0.1.27](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.26...v0.1.27) (2023-10-12)
### ⚡ Performance Improvements | 性能优化
* ⚡ Segmented 分段器组件兼容支付宝小程序 ([3fd57a6](https://github.com/Moonofweisheng/wot-design-uni/commit/3fd57a600b537b945a8224bb97344baa9b0929e0))
### ✏️ Documentation | 文档
* ✏️ 调整文档中图片的CDN地址 ([f704568](https://github.com/Moonofweisheng/wot-design-uni/commit/f704568f0299acbd3cc2d88934bfa1641b6cc792))
* ✏️ 修复文档中根节点错写为根结点的问题 ([6da4145](https://github.com/Moonofweisheng/wot-design-uni/commit/6da414503ceb8b6de680b10332af45dbbbe7229d))
### ✨ Features | 新功能
* ✨ 新增 Tabbar 标签栏组件 ([aa0bf19](https://github.com/Moonofweisheng/wot-design-uni/commit/aa0bf194865e381af41f7afa18e46105e0c812a9))
* ✨ Icon 组件新增图标并支持第三方iconfont ([bc241ac](https://github.com/Moonofweisheng/wot-design-uni/commit/bc241acfc103309f196f4c04e08288526a056073))
### [0.1.26](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.25...v0.1.26) (2023-10-10)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Slider 滑块组件双向滑动时右边滑块百分比计算错误的问题 ([92db0f2](https://github.com/Moonofweisheng/wot-design-uni/commit/92db0f2c7fb60b04e1562c55f6296b1b2b456144))
### [0.1.25](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.24...v0.1.25) (2023-10-10)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Silder 滑块组件双向滑动时自定义最大值小于100无法滚动到最大值的问题 ([a70afa4](https://github.com/Moonofweisheng/wot-design-uni/commit/a70afa43239bc233ee0250e26227dbacd5c7021b)), closes [#69](https://github.com/Moonofweisheng/wot-design-uni/issues/69)
### [0.1.24](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.23...v0.1.24) (2023-10-10)
### 🐛 Bug Fixes | Bug 修复
* 🐛 优化 Badge 徽标组件超出max的显示并修复分段器组件错误依赖的问题 ([a48adc8](https://github.com/Moonofweisheng/wot-design-uni/commit/a48adc8395ab167bb88a7a647420b467d7e4c46d))
### [0.1.23](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.22...v0.1.23) (2023-09-28)
### ✨ Features | 新功能
* ✨ 增加 segmented 分段器组件 ([00f52c8](https://github.com/Moonofweisheng/wot-design-uni/commit/00f52c89e0aebc86f70aa8a7391ce1f17412333d))
### [0.1.22](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.21...v0.1.22) (2023-09-25)
### ✨ Features | 新功能
* ✨ 新增 Swiper 轮播图组件 ([cf9fe98](https://github.com/Moonofweisheng/wot-design-uni/commit/cf9fe9807efe863e786bc56eedf506647ac143d2))
### [0.1.21](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.20...v0.1.21) (2023-09-22)
### [0.1.20](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.19...v0.1.20) (2023-09-20)
### ✏️ Documentation | 文档
* ✏️ 恢复展示 Circle 组件的演示Demo ([c2cba05](https://github.com/Moonofweisheng/wot-design-uni/commit/c2cba056578593172998f03592652ab63ed23aef))
* ✏️ 文档演示项目 Circle 页面文件调整为大写 ([fff8de8](https://github.com/Moonofweisheng/wot-design-uni/commit/fff8de8f8d5f829c5b054d863905a7bb116f45f4))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Circle 组件重复创建canvas导致警告的问题 ([6917201](https://github.com/Moonofweisheng/wot-design-uni/commit/69172016ba840458dd40755050929a8231fd4cd5))
* 🐛 修复 Skeleton 骨架屏组件编译到APP端异常的问题([#52](https://github.com/Moonofweisheng/wot-design-uni/issues/52)) ([7a9a31b](https://github.com/Moonofweisheng/wot-design-uni/commit/7a9a31bd5db76419408a114fbed02c108b972049))
* 🐛 修复 Tag 组件编译到微信小程序平台样式错误的问题([#53](https://github.com/Moonofweisheng/wot-design-uni/issues/53)) ([6aec241](https://github.com/Moonofweisheng/wot-design-uni/commit/6aec2414a384c68e7b4b09f57d028173be942f7b))
### [0.1.19](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.18...v0.1.19) (2023-09-20)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Cell、Grid 组件跳转方法在编译到H5端失效的问题([#49](https://github.com/Moonofweisheng/wot-design-uni/issues/49)) ([51425b1](https://github.com/Moonofweisheng/wot-design-uni/commit/51425b107a8b262bf9c1f0c7ee41bb8d56a12837))
### ✨ Features | 新功能
* ✨ 新增 Circle 环形进度条组件,支持进度渐变动画 ([7c38a6f](https://github.com/Moonofweisheng/wot-design-uni/commit/7c38a6f03e76d91d82a80d2a4cdd875acfb7290b))
### [0.1.18](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.17...v0.1.18) (2023-09-19)
### ✏️ Documentation | 文档
* ✏️ README中增加展示 star-history ([e7cd794](https://github.com/Moonofweisheng/wot-design-uni/commit/e7cd794201435ae8109b562daf70def74ab4ac98))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 PickerView 组件选中框被遮盖的问题([#46](https://github.com/Moonofweisheng/wot-design-uni/issues/46)) ([a60440b](https://github.com/Moonofweisheng/wot-design-uni/commit/a60440b8c52723e4c194b103e9b1b236a7cb29fe))
### [0.1.17](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.16...v0.1.17) (2023-09-19)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复 Slider 滑块组件最大值和最小值不生效的问题([#43](https://github.com/Moonofweisheng/wot-design-uni/issues/43)) ([0fa7f46](https://github.com/Moonofweisheng/wot-design-uni/commit/0fa7f46718e918007491a3d9494292a003924243))
### [0.1.16](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.15...v0.1.16) (2023-09-18)
### ✨ Features | 新功能
* ✨ 新增 WaterMark 水印组件 ([70d8c25](https://github.com/Moonofweisheng/wot-design-uni/commit/70d8c2546135fce1709edb0e1ba0c3b66c1e9e2e))
### [0.1.15](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.14...v0.1.15) (2023-09-18)
### ✏️ Documentation | 文档
* ✏️ 优化DateTimePicker组件关于time类型选择器绑定值格式的介绍 ([9e958c7](https://github.com/Moonofweisheng/wot-design-uni/commit/9e958c73f1d09ee0e02097e3ca8eeabfcc1bc59b))
### [0.1.14](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.13...v0.1.14) (2023-09-14)
### ✏️ Documentation | 文档
* ✏️ 增加QQ群沟通渠道 ([80efad9](https://github.com/Moonofweisheng/wot-design-uni/commit/80efad950613d65e7e65d305ad6efe7cd0e201d8))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复SelectPicker 单复选选择器单选可搜索状态搜索报错的问题([#38](https://github.com/Moonofweisheng/wot-design-uni/issues/38)) ([01cf01d](https://github.com/Moonofweisheng/wot-design-uni/commit/01cf01d46fbc4ac35f0c09e3db6f6d18b2d3455e))
### [0.1.13](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.12...v0.1.13) (2023-09-12)
### ✨ Features | 新功能
* ✨ Tabs组件增加animated属性支持切换动画 ([2572ea4](https://github.com/Moonofweisheng/wot-design-uni/commit/2572ea4c31f834bb9c8776322c24148ca2bda4e2))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复Popover组件content属性必填警告的问题 ([4d8f8e6](https://github.com/Moonofweisheng/wot-design-uni/commit/4d8f8e640742b0588b91b0a5b82a062b976f8306))
### [0.1.12](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.11...v0.1.12) (2023-09-12)
### ✏️ Documentation | 文档
* ✏️ 修复文档中定制主题和反馈组件404的问题 ([c0302ad](https://github.com/Moonofweisheng/wot-design-uni/commit/c0302ada89ea15039ef516f4a425b5e18512c2d5))
* ✏️ 修复Button组件文档中loading属性重复的问题 ([b26da5a](https://github.com/Moonofweisheng/wot-design-uni/commit/b26da5aba7d698adc75c3e1d143660a698ce5de8))
### [0.1.11](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.10...v0.1.11) (2023-09-08)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复文档中部分页面存在死链问题 ([b3e5cbe](https://github.com/Moonofweisheng/wot-design-uni/commit/b3e5cbef1116d9a356f7c9c18dffc1caec20ea22))
### [0.1.10](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.9...v0.1.10) (2023-09-08)
### ✏️ Documentation | 文档
* ✏️ 将驼峰命名的文档文件改为短横线命名便于爬虫爬取及搜索 ([497e991](https://github.com/Moonofweisheng/wot-design-uni/commit/497e991903559c9d62dcb1b00842ee377d70b445))
* ✏️ 优化icon组件文档的体验支持点击图标复制使用示例 ([ce0526b](https://github.com/Moonofweisheng/wot-design-uni/commit/ce0526b99dcd203d3f1081a8fd6a0551da88b9b8))
### [0.1.9](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.8...v0.1.9) (2023-09-08)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复npm包未配置主入口导致在使用Hbx创建的项目中通过npm安装编译警告的问题 ([fa95a0d](https://github.com/Moonofweisheng/wot-design-uni/commit/fa95a0d19b6e13157154405ddaa2525545bd9f7e))
### [0.1.8](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.7...v0.1.8) (2023-09-07)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复ActionSheet等组件在H5端隐藏导航栏或在tabbar页面高度计算错误的问题 ([39201cb](https://github.com/Moonofweisheng/wot-design-uni/commit/39201cbeddde1c7ea883ad331c0607bced755475))
* 🐛 修复pmpm安装时ts导入类型信息在H5平台报错的问题 ([237c03c](https://github.com/Moonofweisheng/wot-design-uni/commit/237c03c6ae8d041c565ca6bb984765be4418f9e1))
### [0.1.7](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.6...v0.1.7) (2023-09-06)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复tabs组件change事件执行两次的问题 ([819059a](https://github.com/Moonofweisheng/wot-design-uni/commit/819059abe19fcf8f9646703fcc7a472b6ae62d4e))
### [0.1.6](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.5...v0.1.6) (2023-09-06)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复Input组件内置变量showClear和showWordCount定义为props的问题 ([8021a35](https://github.com/Moonofweisheng/wot-design-uni/commit/8021a35ac9d05d549909531ff4f1c7325127833e))
* 🐛 修复pnpm安装时运行到h5平台Ref导入报错的问题 ([39c68bf](https://github.com/Moonofweisheng/wot-design-uni/commit/39c68bf57720c1a12b99412c96e46b341cf536b5))
### [0.1.5](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.4...v0.1.5) (2023-09-05)
### ✏️ Documentation | 文档
* ✏️ 增加Stickty和Tabs组件关于H5端自定义导航栏的offset-top的处理方案 ([8a03c4d](https://github.com/Moonofweisheng/wot-design-uni/commit/8a03c4da64296e88a7b3aff5efed4ac04cd039b6))
### [0.1.4](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.3...v0.1.4) (2023-09-05)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复CollapseItem组件在微信小程序中展开/收起时指向图标未跟随转动的问题 ([7d0eeae](https://github.com/Moonofweisheng/wot-design-uni/commit/7d0eeae92f2f7b7664a23f7d548187ed8075d5b0))
### [0.1.3](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.2...v0.1.3) (2023-09-03)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复通过npm安装时配置自动导入Notify、Toast、Message组件无法打开的问题 ([f45b739](https://github.com/Moonofweisheng/wot-design-uni/commit/f45b73907227c25d9c2fd7b7f0018cc2ab5c47d1))
### [0.1.2](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.1...v0.1.2) (2023-09-03)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复部分js引入路径问题 ([67cdfae](https://github.com/Moonofweisheng/wot-design-uni/commit/67cdfaebf9478e286758b2ef851e1a44dc8565a6))
### [0.1.1](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.1.0...v0.1.1) (2023-09-02)
## [0.1.0](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.14...v0.1.0) (2023-09-02)
### ✏️ Documentation | 文档
* ✏️ ConfigProvider组件演示页面增加手动切换暗黑模式 ([e5f55c7](https://github.com/Moonofweisheng/wot-design-uni/commit/e5f55c72fc1ed6e603f22e501d6cff9d8212a976))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复Grid组件不展示border的问题 ([819bbbc](https://github.com/Moonofweisheng/wot-design-uni/commit/819bbbca6ab1c999096936da5e9d2dd664e480ce))
* 🐛 修复MessageBox组件取消按钮不展示的问题 ([d8563d8](https://github.com/Moonofweisheng/wot-design-uni/commit/d8563d833d75b27d0c497c6c945fae8c00ef8dc7))
### [0.0.14](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.13...v0.0.14) (2023-09-02)
### ✨ Features | 新功能
* ✨ 优化缺省状态组件StautsTip增加支持自定义url和图片大小 ([f463d32](https://github.com/Moonofweisheng/wot-design-uni/commit/f463d3258a954e64352df36004d34b0f12be9a8f))
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复util中addUnit无法处理有单位的字符串的问题 ([8967540](https://github.com/Moonofweisheng/wot-design-uni/commit/8967540ad0f3f0ad7426ee79571cfc4dee6c4d0c))
### ✏️ Documentation | 文档
* ✏️ 完善和修复文档中不详细不正确的内容 ([d1b118e](https://github.com/Moonofweisheng/wot-design-uni/commit/d1b118ea78a797499f465269d3a838c0770d993a))
### [0.0.13](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.12...v0.0.13) (2023-09-01)
### ✏️ Documentation | 文档
* ✏️ 官网地址迁移至阿里云oss ([a98868a](https://github.com/Moonofweisheng/wot-design-uni/commit/a98868ae6213c7d7002e5a88893aabe1d0f5d11b))
### [0.0.12](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.11...v0.0.12) (2023-08-30)
### [0.0.11](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.10...v0.0.11) (2023-08-30)
### ✨ Features | 新功能
* ✨ util工具类提供更好的类型提示 ([4fed439](https://github.com/Moonofweisheng/wot-design-uni/commit/4fed43926f49be6a86ebab54bb36a1a086df4ac6))
### [0.0.10](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.9...v0.0.10) (2023-08-27)
### ✨ Features | 新功能
* ✨ 新增Notify组件、演示demo、文档 ([#9](https://github.com/Moonofweisheng/wot-design-uni/issues/9)) ([996fc39](https://github.com/Moonofweisheng/wot-design-uni/commit/996fc39d708699214faf2e54224b8d38a8a706f7))
* ✨ 新增Skeleton组件、演示demo、文档 ([#12](https://github.com/Moonofweisheng/wot-design-uni/issues/12)) ([a49e22c](https://github.com/Moonofweisheng/wot-design-uni/commit/a49e22c370163ec3c93bbeb360cc89b3b3c0abc0))
### [0.0.9](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.8...v0.0.9) (2023-08-25)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复Cell 单格组件 is-link 不触发 click ([ece5568](https://github.com/Moonofweisheng/wot-design-uni/commit/ece5568ecc03fd8470c2c02120b94c1ee27d55e7))
### [0.0.8](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.7...v0.0.8) (2023-08-24)
### ✨ Features | 新功能
* ✨ 演示项目新增用户隐私保护指引的处理 ([4dd7efe](https://github.com/Moonofweisheng/wot-design-uni/commit/4dd7efe4049c4c815ea1bc57a7fd1819055f10bc))
* ✨ ActionSheet组件调整为使用v-model设置显示与隐藏 ([aa92332](https://github.com/Moonofweisheng/wot-design-uni/commit/aa92332f3913be000d1aef36a8aed7f34b736ad6))
### [0.0.7](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.6...v0.0.7) (2023-08-23)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复部分组件某些属性必填警告的问题 ([1b866c0](https://github.com/Moonofweisheng/wot-design-uni/commit/1b866c062eb7a4ab894d5b2ecd7b7b3fd50ef864))
### [0.0.6](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.5...v0.0.6) (2023-08-22)
### [0.0.5](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.4...v0.0.5) (2023-08-22)
### [0.0.4](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.3...v0.0.4) (2023-08-21)
### ✨ Features | 新功能
* ✨ Picker组件优化性能 ([24dd43f](https://github.com/Moonofweisheng/wot-design-uni/commit/24dd43f3a05b7b4ce6bb897219a215f8198e64ac))
### [0.0.3](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.2...v0.0.3) (2023-08-18)
### [0.0.2](https://github.com/Moonofweisheng/wot-design-uni/compare/v0.0.1...v0.0.2) (2023-08-18)
### 🐛 Bug Fixes | Bug 修复
* 🐛 修复CheckBoxGroup组件的disabled属性作用在子组件上失效的问题 ([1bab820](https://github.com/Moonofweisheng/wot-design-uni/commit/1bab820c0335c89e099c597caa47af16bb998d83))
### 0.0.1 (2023-08-16)
### ✨ Features | 新功能
* ✨ 适配暗黑模式 ([f5946a4](https://github.com/Moonofweisheng/wot-design-uni/commit/f5946a4b7134fed161bc123d66485e7bc91cdc68))
* ✨ 新增 50+ 组件

@ -0,0 +1,7 @@
/**
* SCSS BEM
*/
$namespace: 'wd';
$elementSeparator: '__';
$modifierSeparator: '--';
$state-prefix: 'is-';

@ -0,0 +1,80 @@
/**
*
*/
@import 'config';
$default-theme: #4d80f0 !default; //
/* 转换成字符串 */
@function selectorToString($selector) {
$selector: inspect($selector);
$selector: str-slice($selector, 2, -2);
@return $selector;
}
/* 判断是否存在 Modifier */
@function containsModifier($selector) {
$selector: selectorToString($selector);
@if str-index($selector, $modifierSeparator) {
@return true;
} @else {
@return false;
}
}
/* 判断是否存在伪类 */
@function containsPseudo($selector) {
$selector: selectorToString($selector);
@if str-index($selector, ':') {
@return true;
} @else {
@return false;
}
}
/**
*
* @params $theme-color
* @params $type dark 'light'
* @params $mix-color
*/
@function themeColor($theme-color, $type: "", $mix-color: "") {
@if $default-theme != #4d80f0 {
@if $type == "dark" {
@return darken($theme-color, 10%);
} @else if $type == "light" {
@return lighten($theme-color, 10%);
} @else {
@return $theme-color;
}
} @else {
@return $mix-color;
}
}
/**
* 线 使使
* @params $open-linear 线
* @params $deg
* @params $theme-color
* @params [Array] $set $color-list
* @params [Array] $color-list $color-list $per-list
* @params [Array] $per-list
*/
@function resultColor($open-linear, $deg, $theme-color, $set, $color-list, $per-list) {
//
@if $open-linear {
$len: length($color-list);
$arg: $deg;
@for $i from 1 through $len {
$arg: $arg + "," + themeColor($theme-color, nth($set, $i), nth($color-list, $i)) + " " + nth($per-list, $i);
}
@return linear-gradient(unquote($arg));
} @else {
// 使
@return $theme-color;
}
}

@ -0,0 +1,312 @@
/**
*
*/
@import "config";
@import "function";
/**
* BEMb)
*/
@mixin b($block) {
$B: $namespace + "-" + $block !global;
.#{$B} {
@content;
}
}
/* 定义元素e对于伪类会自动将 e 嵌套在 伪类 底下 */
@mixin e($element...) {
$selector: &;
$selectors: "";
@if containsPseudo($selector) {
@each $item in $element {
$selectors: #{$selectors + "." + $B + $elementSeparator + $item + ","};
}
@at-root {
#{$selector} {
#{$selectors} {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
}
/* 此方法用于生成穿透样式 */
/**
* BEMb)
*/
@mixin bdeep($block) {
$B: $namespace + "-" + $block !global;
.#{$B}{
@content;
}
}
/* 此方法用于生成穿透样式 */
/* 定义元素e对于伪类会自动将 e 嵌套在 伪类 底下 */
@mixin edeep($element...) {
$selector: &;
$selectors: "";
@if containsPseudo($selector) {
@each $item in $element {
$selectors: #{$selectors + "." + $B + $elementSeparator + $item + ","};
}
@at-root {
#{$selector} {
:deep(#{$selectors}) {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ","};
}
@at-root {
:deep(#{$selectors}) {
@content;
}
}
}
}
/* 定义状态m */
@mixin m($modifier...) {
$selectors: "";
@each $item in $modifier {
$selectors: #{$selectors + & + $modifierSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
/* 对于需要需要嵌套在 m 底下的 e调用这个混合宏一般在切换整个组件的状态如切换颜色的时候 */
@mixin me($element...) {
$selector: &;
$selectors: "";
@if containsModifier($selector) {
@each $item in $element {
$selectors: #{$selectors + "." + $B + $elementSeparator + $item + ","};
}
@at-root {
#{$selector} {
#{$selectors} {
@content;
}
}
}
} @else {
@each $item in $element {
$selectors: #{$selectors + $selector + $elementSeparator + $item + ","};
}
@at-root {
#{$selectors} {
@content;
}
}
}
}
/* 状态,生成 is-$state 类名 */
@mixin when($states...) {
@at-root {
@each $state in $states {
&.#{$state-prefix + $state} {
@content;
}
}
}
}
/**
*
*/
/* 单行超出隐藏 */
@mixin lineEllipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 多行超出隐藏 */
@mixin multiEllipsis($lineNumber: 3) {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: $lineNumber;
overflow: hidden;
}
/* 清除浮动 */
@mixin clearFloat {
&::after {
display: block;
content: "";
height: 0;
clear: both;
overflow: hidden;
visibility: hidden;
}
}
/* 0.5px 边框 */
@mixin halfPixelBorder($direction: "bottom", $left: 0, $color: $-color-border-light) {
&::after {
position: absolute;
display: block;
content: "";
@if ($left == 0) {
width: 100%;
} @else {
width: calc(100% - #{$left});
}
height: 1px;
left: $left;
@if ($direction == "bottom") {
bottom: 0;
} @else {
top: 0;
}
transform: scaleY(0.5);
background: $color;
}
}
@mixin buttonClear {
outline: none;
-webkit-appearance: none;
-webkit-tap-highlight-color: transparent;
background: transparent;
}
/**
*
* @param $size $size * 2
* @param $bg
*/
@mixin triangleArrow($size, $bg) {
@include e(arrow) {
position: absolute;
width: 0;
height: 0;
}
@include e(arrow-down) {
border-left: $size solid transparent;
border-right: $size solid transparent;
border-top: $size solid $bg;
transform: translateX(-50%);
bottom: calc(-1 * $size)
}
@include e(arrow-up) {
border-left: $size solid transparent;
border-right: $size solid transparent;
border-bottom: $size solid $bg;
transform: translateX(-50%);
top: calc(-1 * $size)
}
@include e(arrow-left) {
border-top: $size solid transparent;
border-bottom: $size solid transparent;
border-right: $size solid $bg;
transform: translateY(-50%);
left: calc(-1 * $size)
}
@include e(arrow-right) {
border-top: $size solid transparent;
border-bottom: $size solid transparent;
border-left: $size solid $bg;
transform: translateY(-50%);
right: calc(-1 * $size)
}
}
/**
*
* @param $size
* @param $bg
* @param $z-index z-index
* @param $box-shadow
*/
@mixin squareArrow($size, $bg, $z-index, $box-shadow) {
@include e(arrow) {
position: absolute;
width: $size;
height: $size;
z-index: $z-index;
}
@include e(arrow-down) {
transform: translateX(-50%);
bottom: 0;
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
bottom: calc(-1 * $size / 2);
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
@include e(arrow-up) {
transform: translateX(-50%);
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
top: calc(-1 * $size / 2);
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
@include e(arrow-left) {
transform: translateY(-50%);
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
left: calc(-1 * $size / 2);
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
@include e(arrow-right) {
transform: translateY(-50%);
&:after {
content: "";
width: $size;
height: $size;
background-color: $bg;
position: absolute;
right: calc(-1 * $size / 2);
transform: rotateZ(45deg);
box-shadow: $box-shadow;
}
}
}

@ -0,0 +1,911 @@
@import './function';
$open-linear: true !default;
/**
* UI
*/
/*----------------------------------------- Theme color. start ----------------------------------------*/
/* 主题颜色 */
$-color-theme: var(--wot-color-theme, $default-theme) !default; //
$-color-white: var(--wot-color-white, rgb(255, 255, 255)) !default; // mix
$-color-black: var(--wot-color-black, rgb(0, 0, 0)) !default; // mix
/* 辅助色 */
$-color-success: var(--wot-color-success, #34d19d) !default; //
$-color-warning: var(--wot-color-warning, #f0883a) !default; //
$-color-danger: var(--wot-color-danger, #fa4350) !default; //
$-color-purple: var(--wot-color-purple, #8268de) !default; //
$-color-yellow: var(--wot-color-yellow, #f0cd1d) !default; //
$-color-blue: var(--wot-color-blue, #2bb3ed) !default; //
$-color-info: var(--wot-color-info, #909399) !default;
$-color-gray-1: var(--wot-color-gray-1, #f7f8fa) !default;
$-color-gray-2: var(--wot-color-gray-2, #f2f3f5) !default;
$-color-gray-3: var(--wot-color-gray-3, #ebedf0) !default;
$-color-gray-4: var(--wot-color-gray-4, #dcdee0) !default;
$-color-gray-5: var(--wot-color-gray-5, #c8c9cc) !default;
$-color-gray-6: var(--wot-color-gray-6, #969799) !default;
$-color-gray-7: var(--wot-color-gray-7, #646566) !default;
$-color-gray-8: var(--wot-color-gray-8, #323233) !default;
$-font-gray-1: var(--wot-font-gray-1, rgba(0, 0, 0, 0.9));
$-font-gray-2: var(--wot-font-gray-2, rgba(0, 0, 0, 0.6));
$-font-gray-3: var(--wot-font-gray-3, rgba(0, 0, 0, 0.4));
$-font-gray-4: var(--wot-font-gray-4, rgba(0, 0, 0, 0.26));
$-font-white-1: var(--wot-font-white-1, rgba(255, 255, 255, 1));
$-font-white-2: var(--wot-font-white-2, rgba(255, 255, 255, 0.55));
$-font-white-3: var(--wot-font-white-3, rgba(255, 255, 255, 0.35));
$-font-white-4: var(--wot-font-white-4, rgba(255, 255, 255, 0.22));
/* 文字颜色(默认浅色背景下 */
$-color-title: var(--wot-color-title, $-color-black) !default; // / 000
$-color-content: var(--wot-color-content, #262626) !default; // 262626
$-color-secondary: var(--wot-color-secondary, #595959) !default; // // 595959
$-color-aid: var(--wot-color-aid, #8c8c8c) !default; // / 8c8c8c
$-color-tip: var(--wot-color-tip, #bfbfbf) !default; // bfbfbf
$-color-border: var(--wot-color-border, #d9d9d9) !default; // 线 d9d9d9
$-color-border-light: var(--wot-color-border-light, #e8e8e8) !default; // 线 e8e8e8
$-color-bg: var(--wot-color-bg, #f5f5f5) !default; // f5f5f5
$-color-table-bg: var(--wot-color-table-bg, #fafafa) !default; // fafafa
/* 暗黑模式 */
$-dark-background: var(--wot-dark-background, #131313) !default;
$-dark-background2: var(--wot-dark-background2, #1b1b1b) !default;
$-dark-background3: var(--wot-dark-background3, #141414) !default;
$-dark-background4: var(--wot-dark-background4, #323233) !default;
$-dark-background5: var(--wot-dark-background5, #646566) !default;
$-dark-background6: var(--wot-dark-background6, #380e08) !default;
$-dark-background7: var(--wot-dark-background7, #707070) !default;
$-dark-color: var(--wot-dark-color, $-color-white) !default;
$-dark-color2: var(--wot-dark-color2, #f2270c) !default;
$-dark-color3: var(--wot-dark-color3, rgba(232, 230, 227, 0.8)) !default;
$-dark-color-gray: var(--wot-dark-color-gray, $-color-secondary) !default;
$-dark-border-color: var(--wot-dark-border-color, #3a3a3c) !default;
/* 文字颜色-深背景 */
$-color-title-indark: var(--wot-color-title-indark, $-color-white) !default; // / fff
$-color-text-indark: var(--wot-color-text-indark, #d9d9d9) !default; // d9d9d9
$-color-secondary-indark: var(--wot-color-secondary-indark, #a6a6a6) !default; // // a6a6a6
$-color-aid-indark: var(--wot-color-aid-indark, #737373) !default; // 线 737373
$-color-tip-indark: var(--wot-color-tip-indark, #404040) !default; // 404040
$-color-border-indark: var(--wot-color-border-indark, #262626) !default; // 线 262626
$-color-border-light-indark: var(--wot-color-border-light-indark, #171717) !default; // 线 171717
$-color-bg-indark: var(--wot-color-bg-indark, #0a0a0a) !default; // 0a0a0a
$-color-table-bg-indark: var(--wot-color-table-bg-indark, #fafafa) !default; // fafafa
/* 透明度 */
$-color-inlight-title-mask: var(--wot-color-inlight-title-mask, #000000) !default; // / 000
/* 图形颜色 */
$-color-icon: var(--wot-color-icon, #d9d9d9) !default; // icon
$-color-icon-active: var(--wot-color-icon-active, #eee) !default; // iconhover
$-color-icon-disabled: var(--wot-color-icon-disabled, #a7a7a7) !default; // icondisabled
/* overlay */
$-overlay-bg: rgba(0, 0, 0, 0.65) !default;
$-overlay-bg-dark: rgba(0, 0, 0, 0.75) !default;
/*----------------------------------------- Theme color. end -------------------------------------------*/
/*-------------------------------- Theme color application size. start --------------------------------*/
/* 文字字号 */
$-fs-big: var(--wot-fs-big, 24px) !default; //
$-fs-important: var(--wot-fs-important, 19px) !default; //
$-fs-title: var(--wot-fs-title, 16px) !default; // /
$-fs-content: var(--wot-fs-content, 14px) !default; //
$-fs-secondary: var(--wot-fs-secondary, 12px) !default; // //
$-fs-aid: var(--wot-fs-aid, 10px) !default; // /
/* 文字字重 */
$-fw-medium: var(--wot-fw-medium, 500) !default; // PingFangSC-Medium
$-fw-semibold: var(--wot-fw-semibold, 600) !default; // PingFangSC-Semibold
/* 尺寸 */
$-size-side-padding: var(--wot-size-side-padding, 15px) !default; //
/*-------------------------------- Theme color application size. end --------------------------------*/
/* action-sheet */
$-action-sheet-weight: var(--wot-action-sheet-weight, 500) !default; //
$-action-sheet-radius: var(--wot-action-sheet-radius, 16px) !default; //
$-action-sheet-action-height: var(--wot-action-sheet-action-height, 48px) !default; //
$-action-sheet-color: var(--wot-action-sheet-color, rgba(0, 0, 0, 0.85)) !default; //
$-action-sheet-fs: var(--wot-action-sheet-fs, $-fs-title) !default; //
$-action-sheet-active-color: var(--wot-action-sheet-active-color, $-color-bg) !default; //
$-action-sheet-subname-fs: var(--wot-action-sheet-subname-fs, $-fs-secondary) !default; //
$-action-sheet-subname-color: var(--wot-action-sheet-subname-color, rgba(0, 0, 0, 0.45)) !default; //
$-action-sheet-disabled-color: var(--wot-action-sheet-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
$-action-sheet-bg: var(--wot-action-sheet-bg, $-color-white) !default; //
$-action-sheet-title-height: var(--wot-action-sheet-title-height, 64px) !default; //
$-action-sheet-title-fs: var(--wot-action-sheet-title-fs, $-fs-title) !default; //
$-action-sheet-close-fs: var(--wot-action-sheet-close-fs, $-fs-title) !default; //
$-action-sheet-close-color: var(--wot-action-sheet-close-color, rgba(0, 0, 0, 0.65)) !default; //
$-action-sheet-close-top: var(--wot-action-sheet-close-top, 25px) !default; //
$-action-sheet-close-right: var(--wot-action-sheet-close-right, 15px) !default; //
$-action-sheet-cancel-color: var(--wot-action-sheet-cancel-color, #131415) !default; //
$-action-sheet-cancel-height: var(--wot-action-sheet-cancel-height, 44px) !default; //
$-action-sheet-cancel-bg: var(--wot-action-sheet-cancel-bg, rgba(240, 240, 240, 1)) !default; //
$-action-sheet-cancel-radius: var(--wot-action-sheet-cancel-radius, 22px) !default; //
$-action-sheet-panel-padding: var(--wot-action-sheet-panel-padding, 12px 0 11px) !default; //
$-action-sheet-panel-img-fs: var(--wot-action-sheet-panel-img-fs, 40px) !default; //
$-action-sheet-panel-img-radius: var(--wot-action-sheet-panel-img-radius, 4px) !default; //
/* badge */
$-badge-bg: var(--wot-badge-bg, $-color-danger) !default; //
$-badge-color: var(--wot-badge-color, #fff) !default; //
$-badge-fs: var(--wot-badge-fs, 12px) !default; //
$-badge-padding: var(--wot-badge-padding, 0 5px) !default; // padding
$-badge-height: var(--wot-badge-height, 16px) !default; //
$-badge-primary: var(--wot-badge-primary, $-color-theme) !default;
$-badge-success: var(--wot-badge-success, $-color-success) !default;
$-badge-warning: var(--wot-badge-warning, $-color-warning) !default;
$-badge-danger: var(--wot-badge-danger, $-color-danger) !default;
$-badge-info: var(--wot-badge-info, $-color-info) !default;
$-badge-dot-size: var(--wot-badge-dot-size, 6px) !default; // dot
$-badge-border: var(--wot-badge-border, 2px solid $-badge-color) !default; //
/* button */
$-button-small-height: var(--wot-button-small-height, 28px) !default; //
$-button-small-padding: var(--wot-button-small-padding, 0 11px) !default; // padding
$-button-small-fs: var(--wot-button-small-fs, $-fs-secondary) !default; //
$-button-small-radius: var(--wot-button-small-radius, 2px) !default; //
$-button-small-loading: var(--wot-button-small-loading, 14px) !default; // loading
$-button-medium-height: var(--wot-button-medium-height, 36px) !default; //
$-button-medium-padding: var(--wot-button-medium-padding, 0 15px) !default; // padding
$-button-medium-fs: var(--wot-button-medium-fs, $-fs-content) !default; //
$-button-medium-radius: var(--wot-button-medium-radius, 4px) !default; //
$-button-medium-loading: var(--wot-button-medium-loading, 18px) !default; // loading
$-button-medium-box-shadow-size: var(--wot-button-medium-box-shadow-size, 0px 2px 4px 0px) !default; //
$-button-large-height: var(--wot-button-large-height, 44px) !default; //
$-button-large-padding: var(--wot-button-large-padding, 0 36px) !default; // padding
$-button-large-fs: var(--wot-button-large-fs, $-fs-title) !default; //
$-button-large-radius: var(--wot-button-large-radius, 8px) !default; //
$-button-large-loading: var(--wot-button-large-loading, 24px) !default; // loading
$-button-large-box-shadow-size: var(--wot-button-large-box-shadow-size, 0px 4px 8px 0px) !default; //
$-button-icon-fs: var(--wot-button-icon-fs, 18px) !default; //
$-button-icon-size: var(--wot-button-icon-size, 40px) !default; // icon
$-button-icon-color: var(--wot-button-icon-color, rgba(0, 0, 0, 0.65)) !default; // icon
$-button-icon-active-color: var(--wot-button-icon-active-color, $-color-icon-active) !default; // icon
$-button-icon-disabled-color: var(--wot-button-icon-disabled-color, $-color-icon-disabled) !default; // icon
$-button-normal-bg: var(--wot-button-normal-bg, $-color-white) !default; //
$-button-normal-color: var(--wot-button-normal-color, $-color-title) !default; //
$-button-normal-active-color: var(--wot-button-normal-active-color, $-color-black) !default; //
$-button-normal-active-bg: var(--wot-button-normal-active-bg, $-color-bg) !default; //
$-button-normal-border-active-color: var(--wot-button-normal-border-active-color, rgba(0, 0, 0, 0.45)) !default; //
$-button-normal-disabled-color: var(--wot-button-normal-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
$-button-normal-disabled-bg: var(--wot-button-normal-disabled-bg, $-color-white) !default; //
$-button-normal-border-disabled-color: var(--wot-button-normal-border-disabled-color, $-color-bg) !default; //
$-button-border-color: var(--wot-button-border-color, #595959) !default; //
$-button-primary-color: var(--wot-button-primary-color, $-color-theme) !default; //
$-button-primary-bg-color: var(--wot-button-primary-bg-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", #4f7cf8 #668df8, 0% 100%)) !default; //
$-button-primary-active-color: var(--wot-button-primary-active-color, resultColor($open-linear, 315deg, #2c69ed, "dark""light", #416bdf #4e79ee, 0% 100%)) !default; //
$-button-primary-plain-active-bg-color: var(--wot-button-primary-plain-active-bg-color, #eff4fe) !default; //
$-button-primary-disabled-color: var(--wot-button-primary-disabled-color, resultColor($open-linear, 315deg, rgba($-color-theme, 0.6), "dark""light", #8FADFF #9FB8FE, 0% 100%)) !default; //
$-button-primary-plain-disabled-color: var(--wot-button-primary-plain-disabled-color, themeColor($-color-theme, "light", #9DB9F6)) !default; //
$-button-primary-box-shadow-color: var(--wot-button-primary-box-shadow-color, rgba($-color-theme, 0.25)) !default; //
$-button-success-color: var(--wot-button-success-color, $-color-success) !default; //
$-button-success-active-color: var(--wot-button-success-active-color, #2bab81) !default; //
$-button-success-disabled-color: var(--wot-button-success-disabled-color, #89e4c6) !default; //
$-button-success-box-shadow-color: var(--wot-button-success-box-shadow-color, rgba($-color-success, 0.25)) !default; //
$-button-info-bg-color: var(--wot-button-info-bg-color, #F0F0F0) !default; //
$-button-info-color: var(--wot-button-info-color, $-color-title) !default; //
$-button-info-active-bg-color: var(--wot-button-info-active-bg-color, #E1E1E1) !default; //
$-button-info-active-color: var(--wot-button-info-active-color, rgba(0, 0, 0, 0.85)) !default; //
$-button-info-disabled-bg-color: var(--wot-button-info-disabled-bg-color, #F0F0F0) !default; //
$-button-info-disabled-color: var(--wot-button-info-disabled-color, rgba(0, 0, 0, 0.09)) !default; //
$-button-info-plain-border-color: var(--wot-button-info-plain-border-color, rgba(0, 0, 0, 0.45)) !default; //
$-button-info-plain-bg-color: var(--wot-button-info-plain-bg-color, $-color-white) !default; //
$-button-info-plain-disabled-bg-color: var(--wot-button-info-plain-disabled-bg-color, #F0F0F0) !default; //
$-button-info-plain-active-color: var(--wot-button-info-plain-active-color, rgba(0, 0, 0, 0.45)) !default; //
$-button-info-plain-active-bg-color: var(--wot-button-info-plain-active-bg-color, #F0F0F0) !default; //
$-button-info-plain-normal-color: var(--wot-button-info-plain-normal-color, rgba(0, 0, 0, 0.85)) !default; //
$-button-warning-color: var(--wot-button-warning-color, $-color-warning) !default; //
$-button-warning-active-color: var(--wot-button-warning-active-color, #c57030) !default; //
$-button-warning-disabled-color: var(--wot-button-warning-disabled-color, #f6ba8d) !default; //
$-button-warning-box-shadow-color: var(--wot-button-warning-box-shadow-color, rgba($-color-warning, 0.25)) !default; //
$-button-error-color: var(--wot-button-error-color, $-color-danger) !default; //
$-button-error-active-color: var(--wot-button-error-active-color, #cd3742) !default; //
$-button-error-disabled-color: var(--wot-button-error-disabled-color, #fc929a) !default; //
$-button-error-box-shadow-color: var(--wot-button-error-box-shadow-color, rgba($-color-danger, 0.25)) !default; //
$-button-suck-height: var(--wot-button-suck-height, 50px) !default; // suck
$-button-suck-active-color: var(--wot-button-suck-active-color, $-button-primary-plain-active-bg-color) !default; //
/* cell */
$-cell-padding: var(--wot-cell-padding, $-size-side-padding) !default; // cell padding
$-cell-line-height: var(--wot-cell-line-height, 24px) !default; //
$-cell-group-title-fs: var(--wot-cell-group-title-fs, $-fs-title) !default; //
$-cell-group-padding: var(--wot-cell-group-padding, 13px $-cell-padding) !default; // padding
$-cell-group-title-color: var(--wot-cell-group-title-color, rgba(0, 0, 0, 0.85)) !default; //
$-cell-group-value-fs: var(--wot-cell-group-value-fs, $-fs-content) !default; //
$-cell-group-value-color: var(--wot-cell-group-value-color, $-color-content) !default; //
$-cell-wrapper-padding: var(--wot-cell-wrapper-padding, 10px) !default; // cell padding
$-cell-wrapper-padding-large: var(--wot-cell-wrapper-padding-large, 12px) !default; // largecellpadding
$-cell-wrapper-padding-with-label: var(--wot-cell-wrapper-padding-with-label, 16px) !default; // cell paddinglabel
$-cell-icon-right: var(--wot-cell-icon-right, 4px) !default; //
$-cell-icon-size: var(--wot-cell-icon-size, 16px) !default; //
$-cell-title-fs: var(--wot-cell-title-fs, 14px) !default; //
$-cell-title-color: var(--wot-cell-title-color, rgba(0, 0, 0, 0.85)) !default; //
$-cell-label-fs: var(--wot-cell-label-fs, 12px) !default; //
$-cell-label-color: var(--wot-cell-label-color, rgba(0, 0, 0, 0.45)) !default; //
$-cell-value-fs: var(--wot-cell-value-fs, 14px) !default; //
$-cell-value-color: var(--wot-cell-value-color, rgba(0, 0, 0, 0.85)) !default; //
$-cell-arrow-size: var(--wot-cell-arrow-size, 18px) !default; //
$-cell-arrow-color: var(--wot-cell-arrow-color, rgba(0, 0, 0, 0.25)) !default; //
$-cell-tap-bg: var(--wot-cell-tap-bg, rgba(0, 0, 0, 0.06)) !default; //
$-cell-title-fs-large: var(--wot-cell-title-fs-large, 16px) !default; //
$-cell-label-fs-large: var(--wot-cell-label-fs-large, 14px) !default; //
$-cell-icon-size-large: var(--wot-cell-icon-size-large, 18px) !default; //
$-cell-required-color: var(--wot-cell-required-color, $-color-danger) !default; // *
$-cell-required-size: var(--wot-cell-required-size, 18px) !default; // *
$-cell-vertical-top: var(--wot-cell-vertical-top, 16px) !default; // -
/* calendar */
$-calendar-fs: var(--wot-calendar-fs, 16px) !default;
$-calendar-panel-padding: var(--wot-calendar-panel-padding, 0 12px) !default;
$-calendar-panel-title-fs: var(--wot-calendar-panel-title-fs, 14px) !default;
$-calendar-panel-title-color: var(--wot-calendar-panel-title-color, rgba(0, 0, 0, 0.85)) !default;
$-calendar-week-color: var(--wot-calendar-week-color, rgba(0, 0, 0, 0.85)) !default;
$-calendar-week-height: var(--wot-calendar-week-height, 36px) !default;
$-calendar-week-fs: var(--wot-calendar-week-fs, 12px) !default;
$-calendar-day-fs: var(--wot-calendar-day-fs, 16px) !default;
$-calendar-day-color: var(--wot-calendar-day-color, rgba(0, 0, 0, 0.85)) !default;
$-calendar-day-fw: var(--wot-calendar-day-fw, 500) !default;
$-calendar-day-height: var(--wot-calendar-day-height, 64px) !default;
$-calendar-month-width: var(--wot-calendar-month-width, 50px) !default;
$-calendar-active-color: var(--wot-calendar-active-color, $-color-theme) !default;
$-calendar-disabled-color: var(--wot-calendar-disabled-color, rgba(0, 0, 0, 0.25)) !default;
$-calendar-range-color: var(--wot-calendar-range-color, rgba(#4d80f0, 0.09)) !default;
$-calendar-active-border: var(--wot-calendar-active-border, 8px) !default;
$-calendar-info-fs: var(--wot-calendar-info-fs, 10px) !default;
/* checkbox */
$-checkbox-margin: var(--wot-checkbox-margin, 10px) !default; //
$-checkbox-bg: var(--wot-checkbox-bg, $-color-white) !default; //
$-checkbox-label-margin: var(--wot-checkbox-label-margin, 9px) !default; //
$-checkbox-size: var(--wot-checkbox-size, 16px) !default; //
$-checkbox-icon-size: var(--wot-checkbox-icon-size, 14px) !default; //
$-checkbox-border-color: var(--wot-checkbox-border-color, #dcdcdc) !default; //
$-checkbox-check-color: var(--wot-checkbox-check-color, $-color-white) !default; //
$-checkbox-label-fs: var(--wot-checkbox-label-fs, 14px) !default; //
$-checkbox-label-color: var(--wot-checkbox-label-color, rgba(0, 0, 0, 0.85)) !default; //
$-checkbox-checked-color: var(--wot-checkbox-checked-color, $-color-theme) !default; //
$-checkbox-disabled-color: var(--wot-checkbox-disabled-color, rgba(0, 0, 0, 0.04)) !default; //
$-checkbox-disabled-label-color: var(--wot-checkbox-disabled-label-color, rgba(0, 0, 0, 0.25)) !default; //
$-checkbox-disabled-check-color: var(--wot-checkbox-disabled-check-color, rgba(0, 0, 0, 0.15)) !default; //
$-checkbox-disabled-check-bg: var(--wot-checkbox-disabled-check-bg, rgba(0, 0, 0, 0.15)) !default; //
$-checkbox-square-radius: var(--wot-checkbox-square-radius, 4px) !default; //
$-checkbox-large-size: var(--wot-checkbox-large-size, 18px) !default; //
$-checkbox-large-label-fs: var(--wot-checkbox-large-label-fs, 16px) !default; //
$-checkbox-button-height: var(--wot-checkbox-button-height, 32px) !default; //
$-checkbox-button-min-width: var(--wot-checkbox-button-min-width, 78px) !default; //
$-checkbox-button-radius: var(--wot-checkbox-button-radius, 16px) !default; //
$-checkbox-button-bg: var(--wot-checkbox-button-bg, rgba(0, 0, 0, 0.04)) !default; //
$-checkbox-button-font-size: var(--wot-checkbox-button-font-size, 14px) !default; //
$-checkbox-button-border: var(--wot-checkbox-button-border, #f5f5f5) !default; //
$-checkbox-button-disabled-border: var(--wot-checkbox-button-disabled-border, rgba(0, 0, 0, 0.15)) !default; //
/* collapse */
$-collapse-side-padding: var(--wot-collapse-side-padding, $-size-side-padding) !default; //
$-collapse-body-padding: var(--wot-collapse-body-padding, 14px 25px) !default; // body padding
$-collapse-header-padding: var(--wot-collapse-header-padding, 13px $-size-side-padding) !default; // padding
$-collapse-title-color: var(--wot-collapse-title-color, rgba(0, 0, 0, 0.85)) !default; //
$-collapse-title-fs: var(--wot-collapse-title-fs, 16px) !default; //
$-collapse-arrow-size: var(--wot-collapse-arrow-size, 18px) !default; //
$-collapse-arrow-color: var(--wot-collapse-arrow-color, #d8d8d8) !default; //
$-collapse-body-fs: var(--wot-collapse-body-fs, 14px) !default; //
$-collapse-body-color: var(--wot-collapse-body-color, rgba(0, 0, 0, 0.65)) !default; //
$-collapse-disabled-color: var(--wot-collapse-disabled-color, rgba(0, 0, 0, 0.15)) !default; //
$-collapse-retract-fs: var(--wot-collapse-retract-fs, 14px) !default; //
$-collapse-more-color: var(--wot-collapse-more-color, $-color-theme) !default; //
/* divider */
$-divider-padding: var(--wot-divider-padding, 0 $-size-side-padding) !default; //
$-divider-color: var(--wot-divider-color, rgba(0, 0, 0, 0.45)) !default; //
$-divider-line-color: var(--wot-divider-line-color, rgba(0, 0, 0, 0.15)) !default; // 线
$-divider-fs: var(--wot-divider-fs, 14px) !default; //
/* drop-menu */
$-drop-menu-height: var(--wot-drop-menu-height, 48px) !default; //
$-drop-menu-color: var(--wot-drop-menu-color, $-color-content) !default; //
$-drop-menu-fs: var(--wot-drop-menu-fs, $-fs-content) !default; //
$-drop-menu-side-padding: var(--wot-drop-menu-side-padding, $-size-side-padding) !default; //
$-drop-menu-disabled-color: var(--wot-drop-menu-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
$-drop-menu-item-height: var(--wot-drop-menu-item-height, 48px) !default; //
$-drop-menu-item-color: var(--wot-drop-menu-item-color, $-color-content) !default; //
$-drop-menu-item-fs: var(--wot-drop-menu-item-fs, $-fs-content) !default; //
$-drop-menu-item-color-active: var(--wot-drop-menu-item-color-active, $-color-theme) !default; //
$-drop-menu-item-color-tip: var(--wot-drop-menu-item-color-tip, rgba(0, 0, 0, 0.45)) !default; //
$-drop-menu-item-fs-tip: var(--wot-drop-menu-item-fs-tip, $-fs-secondary) !default; //
$-drop-menu-option-check-size: var(--wot-drop-menu-option-check-size, 20px) !default; // check
$-drop-menu-line-color: var(--wot-drop-menu-line-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", rgba(81, 124, 240, 1) rgba(118, 158, 245, 1), 0% 100%)) !default; // 线
$-drop-menu-line-height: var(--wot-drop-menu-line-height, 3px) !default; // 线
/* input-number */
$-input-number-color: var(--wot-input-number-color, #262626) !default; //
$-input-number-border-color: var(--wot-input-number-border-color, #e8e8e8) !default; //
$-input-number-disabled-color: var(--wot-input-number-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
$-input-number-height: var(--wot-input-number-height, 24px) !default; //
$-input-number-btn-width: var(--wot-input-number-btn-width, 26px) !default; //
$-input-number-input-width: var(--wot-input-number-input-width, 36px) !default; //
$-input-number-radius: var(--wot-input-number-radius, 4px) !default; //
$-input-number-fs: var(--wot-input-number-fs, 12px) !default; //
$-input-number-icon-size: var(--wot-input-number-icon-size, 14px) !default; //
$-input-number-icon-color: var(--wot-input-number-icon-color, rgba(0, 0, 0, 0.65)) !default; // icon
/* input */
$-input-padding: var(--wot-input-padding, $-size-side-padding) !default; // input padding
$-input-border-color: var(--wot-input-border-color, #dadada) !default; // label
$-input-not-empty-border-color: var(--wot-input-not-empty-border-color, #262626) !default; // label
$-input-fs: var(--wot-input-fs, $-cell-title-fs) !default; //
$-input-fs-large: var(--wot-input-fs-large, $-cell-title-fs-large) !default; //
$-input-icon-margin: var(--wot-input-icon-margin, 8px) !default; //
$-input-color: var(--wot-input-color, #262626) !default; //
$-input-placeholder-color: var(--wot-input-placeholder-color, #bfbfbf) !default; //
$-input-disabled-color: var(--wot-input-disabled-color, #d9d9d9) !default; //
$-input-error-color: var(--wot-input-error-color, $-color-danger) !default; //
$-input-icon-color: var(--wot-input-icon-color, #bfbfbf) !default; //
$-input-clear-color: var(--wot-input-clear-color, #585858) !default; //
$-input-count-color: var(--wot-input-count-color, #bfbfbf) !default; //
$-input-count-current-color: var(--wot-input-count-current-color, #262626) !default; //
$-input-bg: var(--wot-input-bg, $-color-white) !default; //
$-input-cell-bg: var(--wot-input-cell-bg, $-color-white) !default; // cell
$-input-cell-border-color: var(--wot-input-cell-border-color, $-color-border-light) !default; // cell
$-input-cell-padding: var(--wot-input-cell-padding, 10px) !default; // cell padding
$-input-cell-padding-large: var(--wot-input-cell-padding-large, 12px) !default; // largecellpadding
$-input-cell-height: var(--wot-input-cell-height, 24px) !default; // cell
$-input-cell-label-width: var(--wot-input-cell-label-width, 33%) !default; // cell label
$-input-inner-height: var(--wot-input-inner-height, 34px) !default; // celltextarea
$-input-inner-height-no-border: var(--wot-input-inner-height-no-border, 24px) !default; //
$-input-count-fs: var(--wot-input-count-fs, 14px) !default; //
$-input-count-fs-large: var(--wot-input-count-fs-large, 14px) !default; //
$-input-icon-size: var(--wot-input-icon-size, 16px) !default; //
$-input-icon-size-large: var(--wot-input-icon-size-large, 18px) !default; //
/* textarea */
$-textarea-padding: var(--wot-textarea-padding, $-size-side-padding) !default; // textarea padding
$-textarea-border-color: var(--wot-textarea-border-color, #dadada) !default; // label
$-textarea-not-empty-border-color: var(--wot-textarea-not-empty-border-color, #262626) !default; // label
$-textarea-fs: var(--wot-textarea-fs, $-cell-title-fs) !default; //
$-textarea-fs-large: var(--wot-textarea-fs-large, $-cell-title-fs-large) !default; //
$-textarea-icon-margin: var(--wot-textarea-icon-margin, 8px) !default; //
$-textarea-color: var(--wot-textarea-color, #262626) !default; //
$-textarea-icon-color: var(--wot-textarea-icon-color, #bfbfbf) !default; //
$-textarea-clear-color: var(--wot-textarea-clear-color, #585858) !default; //
$-textarea-count-color: var(--wot-textarea-count-color, #bfbfbf) !default; //
$-textarea-count-current-color: var(--wot-textarea-count-current-color, #262626) !default; //
$-textarea-bg: var(--wot-textarea-bg, $-color-white) !default; //
$-textarea-cell-border-color: var(--wot-textarea-cell-border-color, $-color-border-light) !default; // cell
$-textarea-cell-padding: var(--wot-textarea-cell-padding, 10px) !default; // cell padding
$-textarea-cell-padding-large: var(--wot-textarea-cell-padding-large, 12px) !default; // largecellpadding
$-textarea-cell-height: var(--wot-textarea-cell-height, 24px) !default; // cell
$-textarea-count-fs: var(--wot-textarea-count-fs, 14px) !default; //
$-textarea-count-fs-large: var(--wot-textarea-count-fs-large, 14px) !default; //
$-textarea-icon-size: var(--wot-textarea-icon-size, 16px) !default; //
$-textarea-icon-size-large: var(--wot-textarea-icon-size-large, 18px) !default; //
/* loadmore */
$-loadmore-height: var(--wot-loadmore-height, 48px) !default; //
$-loadmore-color: var(--wot-loadmore-color, rgba(0, 0, 0, 0.45)) !default; //
$-loadmore-fs: var(--wot-loadmore-fs, 14px) !default; //
$-loadmore-error-color: var(--wot-loadmore-error-color, $-color-theme) !default; //
/* message-box */
$-message-box-width: var(--wot-message-box-width, 300px) !default; //
$-message-box-bg: var(--wot-message-box-bg, $-color-white) !default; //
$-message-box-radius: var(--wot-message-box-radius, 16px) !default; //
$-message-box-padding: var(--wot-message-box-padding, 25px 24px 0) !default; // padding
$-message-box-title-fs: var(--wot-message-box-title-fs, 16px) !default; //
$-message-box-title-color: var(--wot-message-box-title-color, rgba(0, 0, 0, 0.85)) !default; //
$-message-box-content-fs: var(--wot-message-box-content-fs, 14px) !default; //
$-message-box-content-color: var(--wot-message-box-content-color, #666666) !default; //
$-message-box-content-max-height: var(--wot-message-box-content-max-height, 264px) !default; //
$-message-box-content-scrollbar-width: var(--wot-message-box-content-scrollbar-width, 4px) !default; //
$-message-box-content-scrollbar-color: var(--wot-message-box-content-scrollbar-color, rgba(0, 0, 0, 0.1)) !default; //
$-message-box-input-error-color: var(--wot-message-box-input-error-color, $-input-error-color) !default; //
/* notice-bar */
$-notice-bar-fs: var(--wot-notice-bar-fs, 12px) !default; //
$-notice-bar-line-height: var(--wot-notice-bar-line-height, 18px) !default; //
$-notice-bar-border-radius: var(--wot-notice-bar-border-radius, 8px) !default; //
$-notice-bar-padding: var(--wot-notice-bar-padding, 9px 20px 9px 15px) !default; // padding
$-notice-bar-warning-bg: var(--wot-notice-bar-warning-bg, #fff6c8) !default; //
$-notice-bar-info-bg: var(--wot-notice-bar-info-bg, #f4f9ff) !default; //
$-notice-bar-danger-bg: var(--wot-notice-bar-danger-bg, #feeced) !default; //
$-notice-bar-warning-color: var(--wot-notice-bar-warning-color, $-color-warning) !default; //
$-notice-bar-info-color: var(--wot-notice-bar-info-color, $-color-theme) !default; //
$-notice-bar-danger-color: var(--wot-notice-bar-danger-color, $-color-danger) !default; //
$-notice-bar-prefix-size: var(--wot-notice-bar-prefix-size, 18px) !default; //
$-notice-bar-close-bg: var(--wot-notice-bar-close-bg, rgba(0, 0, 0, 0.15)) !default; //
$-notice-bar-close-size: var(--wot-notice-bar-close-size, 18px) !default; //
$-notice-bar-close-color: var(--wot-notice-bar-close-color, $-color-white) !default; //
$-notice-bar-close-size: var(--wot-notice-bar-close-size, 10px) !default; //
$-notice-bar-wrap-padding: var(--wot-notice-bar-wrap-padding, 14px $-size-side-padding) !default; // padding
/* pagination */
$-pagination-content-padding: var(--wot-pagination-content-padding, 10px 15px) !default;
$-pagination-message-padding: var(--wot-pagination-message-padding, 1px 0 16px 0) !default;
$-pagination-message-fs: var(--wot-pagination-message-fs, 12px) !default;
$-pagination-message-color: var(--wot-pagination-message-color, rgba(0, 0, 0, 0.69)) !default;
$-pagination-nav-border: var(--wot-pagination-nav-border, 1px solid rgba(0, 0, 0, 0.45)) !default;
$-pagination-nav-border-radius: var(--wot-pagination-nav-border-radius, 16px) !default;
$-pagination-nav-fs: var(--wot-pagination-nav-fs, 12px) !default;
$-pagination-nav-width: var(--wot-pagination-nav-width, 60px) !default;
$-pagination-nav-color: var(--wot-pagination-nav-color, rgba(0, 0, 0, 0.85)) !default;
$-pagination-nav-content-fs: var(--wot-pagination-nav-content-fs, 12px) !default;
$-pagination-nav-sepatator-padding: var(--wot-pagination-nav-sepatator-padding, 0 4px) !default;
$-pagination-nav-current-color: var(--wot-pagination-nav-current-color, $-color-theme) !default;
/* picker */
$-picker-toolbar-height: var(--wot-picker-toolbar-height, 54px) !default; // toolbar
$-picker-action-height: var(--wot-picker-action-height, 16px) !default; // toolbar
$-picker-toolbar-finish-color: var(--wot-picker-toolbar-finish-color, $-color-theme) !default; // toolbar
$-picker-toolbar-cancel-color: var(--wot-picker-toolbar-cancel-color, #666666) !default; // toolbar
$-picker-toolbar-fs: var(--wot-picker-toolbar-fs, $-fs-title) !default; // toolbar
$-picker-toolbar-title-color: var(--wot-picker-toolbar-title-color, rgba(0, 0, 0, 0.85)) !default; // toolbar
$-picker-column-fs: var(--wot-picker-column-fs, 16px) !default; //
$-picker-bg: var(--wot-picker-bg, $-color-white) !default; //
$-picker-column-active-fs: var(--wot-picker-column-active-fs, 18px) !default; //
$-picker-column-color: var(--wot-picker-column-color, rgba(0, 0, 0, 0.85)) !default; //
$-picker-column-height: var(--wot-picker-column-height, 210px) !default; //
$-picker-column-item-height: var(--wot-picker-column-item-height, 35px) !default; //
$-picker-column-select-bg: var(--wot-picker-column-select-bg, #f5f5f5) !default;
$-picker-loading-button-color: var(--wot-picker-loading-button-color, rgba(0, 0, 0, 0.25)) !default; // loading
$-picker-column-padding: var(--wot-picker-column-padding, 0 $-size-side-padding) !default; //
$-picker-column-disabled-color: var(--wot-picker-column-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
$-picker-mask: var(--wot-picker-mask, linear-gradient(180deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.25)), ) linear-gradient(0deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.25)) !default; //
$-picker-loading-bg: var(--wot-picker-loading-bg, rgba($-color-white, 0.8)) !default; // loading
$-picker-region-separator-color: var(--wot-picker-region-separator-color, rgba(0, 0, 0, 0.65)) !default; //
$-picker-cell-arrow-size-large: var(--wot-picker-cell-arrow-size-large, $-cell-icon-size) !default; // cell icon
$-picker-region-color: var(--wot-picker-region-color, rgba(0, 0, 0, 0.45)) !default; //
$-picker-region-bg-active-color: var(--wot-picker-region-bg-active-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light""light", rgba(79, 124, 248, 1) rgba(102, 141, 248, 1) rgba(102, 141, 248, 1), 0% 100% 100%)) !default; //
$-picker-region-fs: var(--wot-picker-region-fs, 14px) !default; //
/* col-picker */
$-col-picker-selected-height: var(--wot-col-picker-selected-height, 44px) !default; //
$-col-picker-selected-padding: var(--wot-col-picker-selected-padding, 0 16px) !default; //
$-col-picker-selected-fs: var(--wot-col-picker-selected-fs, 14px) !default; //
$-col-picker-selected-color: var(--wot-col-picker-selected-color, rgba(0, 0, 0, 0.85)) !default; //
$-col-picker-selected-fw: var(--wot-col-picker-selected-fw, 700) !default; //
$-col-picker-line-width: var(--wot-col-picker-line-width, 16px) !default; // 线
$-col-picker-line-height: var(--wot-col-picker-line-height, 3px) !default; // 线
$-col-picker-line-color: var(--wot-col-picker-line-color, linear-gradient(315deg, rgba(81, 124, 240, 1), rgba(118, 158, 245, 1))) !default; // 线
$-col-picker-line-box-shadow: var(--wot-col-picker-line-box-shadow, 0px 1px 2px 0px rgba(1, 87, 255, 0.2)) !default; // 线
$-col-picker-list-height: var(--wot-col-picker-list-height, 53vh) !default; //
$-col-picker-list-padding-bottom: var(--wot-col-picker-list-padding-bottom, 30px) !default; //
$-col-picker-list-color: var(--wot-col-picker-list-color, rgba(0, 0, 0, 0.85)) !default; //
$-col-picker-list-color-disabled: var(--wot-col-picker-list-color-disabled, rgba(0, 0, 0, 0.15)) !default; //
$-col-picker-list-color-tip: var(--wot-col-picker-list-color-tip, rgba(0, 0, 0, 0.45)) !default; //
$-col-picker-list-fs: var(--wot-col-picker-list-fs, 14px) !default; //
$-col-picker-list-fs-tip: var(--wot-col-picker-list-fs-tip, 12px) !default; //
$-col-picker-list-item-padding: var(--wot-col-picker-list-item-padding, 12px 15px) !default; //
$-col-picker-list-checked-icon-size: var(--wot-col-picker-list-checked-icon-size, 18px) !default; //
$-col-picker-list-color-checked: var(--wot-col-picker-list-color-checked, $-color-theme) !default; //
/* popup */
$-popup-close-size: var(--wot-popup-close-size, 24px) !default; //
$-popup-close-color: var(--wot-popup-close-color, #666) !default; //
/* progress */
$-progress-padding: var(--wot-progress-padding, 9px 0 8px) !default; //
$-progress-bg: var(--wot-progress-bg, rgba(229, 229, 229, 1)) !default; //
$-progress-danger-color: var(--wot-progress-danger-color, $-color-danger) !default; // danger
$-progress-success-color: var(--wot-progress-success-color, $-color-success) !default; // success
$-progress-color: var(--wot-progress-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", #517CF0 #769EF5, 0% 100%)) !default; //
$-progress-linear-success-color: var(--wot-progress-linear-success-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", #20B080 #2BD69D, 0% 100%)) !default; // success
$-progress-linear-danger-color: var(--wot-progress-linear-danger-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", #E04350 #FF5964, 0% 100%)) !default; // danger
$-progress-height: var(--wot-progress-height, 3px) !default; //
$-progress-label-color: var(--wot-progress-label-color, #333) !default; //
$-progress-label-fs: var(--wot-progress-label-fs, 14px) !default; //
$-progress-icon-fs: var(--wot-progress-icon-fs, 18px) !default; //
/* radio */
$-radio-margin: var(--wot-radio-margin, $-checkbox-margin) !default; //
$-radio-label-margin: var(--wot-radio-label-margin, $-checkbox-label-margin) !default; //
$-radio-size: var(--wot-radio-size, 16px) !default; //
$-radio-bg: var(--wot-radio-bg, $-checkbox-bg) !default; //
$-radio-label-fs: var(--wot-radio-label-fs, $-checkbox-label-fs) !default; //
$-radio-label-color: var(--wot-radio-label-color, $-checkbox-label-color) !default; //
$-radio-checked-color: var(--wot-radio-checked-color, $-checkbox-checked-color) !default; //
$-radio-disabled-color: var(--wot-radio-disabled-color, $-checkbox-disabled-color) !default; //
$-radio-disabled-label-color: var(--wot-radio-disabled-label-color, $-checkbox-disabled-label-color) !default; //
$-radio-large-size: var(--wot-radio-large-size, $-checkbox-large-size) !default; //
$-radio-large-label-fs: var(--wot-radio-large-label-fs, $-checkbox-large-label-fs) !default; //
$-radio-button-height: var(--wot-radio-button-height, $-checkbox-button-height) !default; //
$-radio-button-min-width: var(--wot-radio-button-min-width, 60px) !default; //
$-radio-button-max-width: var(--wot-radio-button-max-width, 144px) !default; //
$-radio-button-radius: var(--wot-radio-button-radius, $-checkbox-button-radius) !default; //
$-radio-button-bg: var(--wot-radio-button-bg, $-checkbox-button-bg) !default; //
$-radio-button-fs: var(--wot-radio-button-fs, $-checkbox-button-font-size) !default; //
$-radio-button-border: var(--wot-radio-button-border, $-checkbox-button-border) !default; //
$-radio-button-disabled-border: var(--wot-radio-button-disabled-border, $-checkbox-button-disabled-border) !default; //
$-radio-dot-size: var(--wot-radio-dot-size, 8px) !default; // dot
$-radio-dot-large-size: var(--wot-radio-dot-large-size, 10px) !default; // dot
$-radio-dot-checked-bg: var(--wot-radio-dot-checked-bg, $-color-theme) !default; // dot
$-radio-dot-checked-border-color: var(--wot-radio-dot-checked-border-color, $-color-theme) !default; // dot
$-radio-dot-border-color: var(--wot-radio-dot-border-color, #dcdcdc) !default; // dot
$-radio-dot-disabled-border: var(--wot-radio-dot-disabled-border, #d9d9d9) !default; // dot
$-radio-dot-disabled-bg: var(--wot-radio-dot-disabled-bg, #d9d9d9) !default; // dot
/* search */
$-search-side-padding: var(--wot-search-side-padding, $-size-side-padding) !default; //
$-search-padding: var(--wot-search-padding, 10px 0 10px $-search-side-padding) !default; //
$-search-input-radius: var(--wot-search-input-radius, 15px) !default; //
$-search-input-bg: var(--wot-search-input-bg, $-color-bg) !default; //
$-search-input-height: var(--wot-search-input-height, 30px) !default; //
$-search-input-padding: var(--wot-search-input-padding, 0 32px 0 42px) !default; //
$-search-input-fs: var(--wot-search-input-fs, $-fs-content) !default; //
$-search-input-color: var(--wot-search-input-color, #262626) !default; //
$-search-icon-color: var(--wot-search-icon-color, $-color-icon) !default; //
$-search-placeholder-color: var(--wot-search-placeholder-color, #bfbfbf) !default; // placeholder
$-search-cancel-padding: var(--wot-search-cancel-padding, 0 $-search-side-padding 0 10px) !default; //
$-search-cancel-fs: var(--wot-search-cancel-fs, $-fs-title) !default; //
$-search-cancel-color: var(--wot-search-cancel-color, rgba(0, 0, 0, 0.65)) !default; //
$-search-light-bg: var(--wot-search-light-bg, $-color-bg) !default; // light
/* slider */
$-slider-fs: var(--wot-slider-fs, $-fs-content) !default; //
$-slider-handle-radius: var(--wot-slider-handle-radius, 12px) !default; //
$-slider-handle-bg: var(--wot-slider-handle-bg, resultColor($open-linear, 139deg, $-color-theme, "dark""light", #FFFFFF #F7F7F7, 0% 100%)) !default; //
$-slider-axie-height: var(--wot-slider-axie-height, 3px) !default; //
$-slider-color: var(--wot-slider-color, #333) !default; //
$-slider-axie-bg: var(--wot-slider-axie-bg, #E5E5E5) !default; //
$-slider-line-color: var(--wot-slider-line-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", #517CF0 #769EF5, 0% 100%)) !default; //
$-slider-disabled-color: var(--wot-slider-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
/* sort-button */
$-sort-button-fs: var(--wot-sort-button-fs, $-fs-content) !default; //
$-sort-button-color: var(--wot-sort-button-color, $-color-content) !default; //
$-sort-button-height: var(--wot-sort-button-height, 48px) !default; //
$-sort-button-line-height: var(--wot-sort-button-line-height, 3px) !default; // 线
$-sort-button-line-color: var(--wot-sort-button-line-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", rgba(81, 124, 240, 1) rgba(118, 158, 245, 1), 0% 100%)) !default; // 线
/* steps */
$-steps-icon-size: var(--wot-steps-icon-size, 22px) !default; //
$-steps-inactive-color: var(--wot-steps-inactive-color, rgba(0, 0, 0, 0.25)) !default; //
$-steps-finished-color: var(--wot-steps-finished-color, $-color-theme) !default; //
$-steps-icon-text-fs: var(--wot-steps-icon-text-fs, $-fs-content) !default; //
$-steps-error-color: var(--wot-steps-error-color, $-color-danger) !default; //
$-steps-title-fs: var(--wot-steps-title-fs, $-fs-content) !default; //
$-steps-title-fw: var(--wot-steps-title-fw, $-fw-medium) !default; //
$-steps-label-fs: var(--wot-steps-label-fs, $-fs-secondary) !default; //
$-steps-description-color: var(--wot-steps-description-color, rgba(0, 0, 0, 0.45)) !default; //
$-steps-is-icon-width: var(--wot-steps-is-icon-width, 30px) !default; //
$-steps-line-color: var(--wot-steps-line-color, rgba(0, 0, 0, 0.15)) !default; // 线
$-steps-dot-size: var(--wot-steps-dot-size, 7px) !default; //
$-steps-dot-active-size: var(--wot-steps-dot-active-size, 9px) !default; //
/* switch */
$-switch-size: var(--wot-switch-size, 28px) !default; // switch
$-switch-width: var(--wot-switch-width, calc(1.8em + 4px)) !default; //
$-switch-height: var(--wot-switch-height, calc(1em + 4px)) !default; //
$-switch-circle-size: var(--wot-switch-circle-size, 1em) !default; //
$-switch-border-color: var(--wot-switch-border-color, #e5e5e5) !default; //
$-switch-active-color: var(--wot-switch-active-color, resultColor($open-linear, 315deg, $-color-theme, "light""dark", #4f7cf8 #668df8, 0% 100%)) !default; //
$-switch-active-shadow-color: var(--wot-switch-active-shadow-color, rgba(0, 83, 162, 0.5)) !default; // shadow
$-switch-inactive-color: var(--wot-switch-inactive-color, resultColor($open-linear, 315deg, #dadada, "light""dark""dark", #f2f2f2 #dadada #668df8, 0% 100% 100%)) !default; //
$-switch-inactive-shadow-color: var(--wot-switch-inactive-shadow-color, rgba(155, 155, 155, 0.5)) !default; // shadow
/* tabs */
$-tabs-nav-arrow-fs: var(--wot-tabs-nav-arrow-fs, 18px) !default; // Icon
$-tabs-nav-arrow-open-fs: var(--wot-tabs-nav-arrow-open-fs, 14px) !default; // Icon
$-tabs-nav-width: var(--wot-tabs-nav-width, 100vw) !default; // tabs
$-tabs-nav-height: var(--wot-tabs-nav-height, 42px) !default; //
$-tabs-nav-fs: var(--wot-tabs-nav-fs, $-fs-content) !default; //
$-tabs-nav-color: var(--wot-tabs-nav-color, rgba(0, 0, 0, 0.85)) !default; //
$-tabs-nav-bg: var(--wot-tabs-nav-bg, $-color-white) !default; //
$-tabs-nav-active-color: var(--wot-tabs-nav-active-color, $-color-theme) !default; //
$-tabs-nav-disabled-color: var(--wot-tabs-nav-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
$-tabs-nav-line-height: var(--wot-tabs-nav-line-height, 3px) !default; //
$-tabs-nav-line-bg-color: var(--wot-tabs-nav-line-bg-color, resultColor($open-linear, 315deg, $-color-theme, "dark""light", rgba(81, 124, 240, 1) rgba(118, 158, 245, 1), 0% 100%)) !default; //
$-tabs-nav-map-fs: var(--wot-tabs-nav-map-fs, $-fs-content) !default; // map
$-tabs-nav-map-color: var(--wot-tabs-nav-map-color, rgba(0, 0, 0, 0.85)) !default; // map
$-tabs-nav-map-arrow-color: var(--wot-tabs-nav-map-arrow-color, rgba(0, 0, 0, 0.65)) !default; // map
$-tabs-nav-map-btn-before-bg: var(--wot-tabs-nav-map-btn-before-bg, linear-gradient(270deg, rgba(255, 255, 255, 1) 1%, rgba(255, 255, 255, 0) 100%)) !default; // map
$-tabs-nav-map-button-back-color: var(--wot-tabs-nav-map-button-back-color, rgba(0, 0, 0, 0.04)) !default; // map
$-tabs-nav-map-button-radius: var(--wot-tabs-nav-map-button-radius, 16px) !default; // map
$-tabs-nav-map-modal-bg: var(--wot-tabs-nav-map-modal-bg, $-overlay-bg) !default; // map
/* tag */
$-tag-fs: var(--wot-tag-fs, $-fs-secondary) !default; //
$-tag-color: var(--wot-tag-color, $-color-white) !default; //
$-tag-small-fs: var(--wot-tag-small-fs, $-fs-aid) !default; //
$-tag-info-color: var(--wot-tag-info-color, #585858) !default; // info
$-tag-primary-color: var(--wot-tag-primary-color, $-color-theme) !default; //
$-tag-danger-color: var(--wot-tag-danger-color, rgba(250, 67, 80, 1)) !default; // danger
$-tag-warning-color: var(--wot-tag-warning-color, rgba(255, 144, 0, 1)) !default; // warning
$-tag-success-color: var(--wot-tag-success-color, rgba(51, 187, 68, 1)) !default; // success
$-tag-info-bg: var(--wot-tag-info-bg, resultColor($open-linear, 49deg, $-color-black, "dark""light", #808080 #999999, 0% 100%)) !default; // info
$-tag-primary-bg: var(--wot-tag-primary-bg, resultColor($open-linear, 49deg, $-color-theme, "dark""light", rgba(81, 124, 240, 1) rgba(118, 158, 245, 1), 0% 100%)) !default; //
$-tag-danger-bg: var(--wot-tag-danger-bg, resultColor($open-linear, 44deg, $-color-danger, "dark""light", rgba(230, 62, 70, 1) rgba(255, 64, 73, 1), 0% 100%)) !default; // danger
$-tag-warning-bg: var(--wot-tag-warning-bg, resultColor($open-linear, 45deg, $-color-warning, "dark""light", rgba(230, 113, 55, 1) rgba(255, 151, 76, 1), 0% 100%)) !default; // warning
$-tag-success-bg: var(--wot-tag-success-bg, resultColor($open-linear, 45deg, $-color-success, "dark""light", rgba(32, 176, 128, 1) rgba(43, 214, 157, 1), 0% 100%)) !default; // success
$-tag-round-color: var(--wot-tag-round-color, rgba(102, 102, 102, 1)) !default; // round
$-tag-round-border-color: var(--wot-tag-round-border-color, rgba(225, 225, 225, 1)) !default; // round
$-tag-round-radius: var(--wot-tag-round-radius, 12px) !default; // round
$-tag-mark-radius: var(--wot-tag-mark-radius, 6px 2px 6px 2px) !default; // mark
$-tag-close-size: var(--wot-tag-close-size, 14px) !default; //
$-tag-close-color: var(--wot-tag-close-color, $-tag-info-color) !default; //
$-tag-close-active-color: var(--wot-tag-close-active-color, rgba(0, 0, 0, 0.45)) !default; // active
/* toast */
$-toast-padding: var(--wot-toast-padding, 16px 24px) !default; // padding
$-toast-max-width: var(--wot-toast-max-width, 300px) !default; //
$-toast-radius: var(--wot-toast-radius, 8px) !default; //
$-toast-bg: var(--wot-toast-bg, $-overlay-bg) !default; //
$-toast-fs: var(--wot-toast-fs, $-fs-content) !default; //
$-toast-with-icon-min-width: var(--wot-toast-with-icon-min-width, 150px) !default; //
$-toast-icon-size: var(--wot-toast-icon-size, 39px) !default; //
$-toast-loading-padding: var(--wot-toast-loading-padding, 10px) !default; // loading padding
$-toast-box-shadow: var(--wot-toast-box-shadow, 0px 6px 16px 0px rgba(0, 0, 0, 0.08)) !default; //
/* tooltip */
$-tooltip-bg: var(--wot-tooltip-bg, rgba(38, 39, 40, 0.8)) !default; //
$-tooltip-color: var(--wot-tooltip-color, $-color-white) !default; //
$-tooltip-radius: var(--wot-tooltip-radius, 8px) !default; //
$-tooltip-arrow-size: var(--wot-tooltip-arrow-size, 9px) !default; //
$-tooltip-fs: var(--wot-tooltip-fs, $-fs-content) !default; //
$-tooltip-blur: var(--wot-tooltip-blur, 10px) !default; //
$-tooltip-padding: var(--wot-tooltip-padding, 9px 20px) !default; //
$-tooltip-close-size: var(--wot-tooltip-close-size, 6px) !default; //
$-tooltip-z-index: var(--wot-tooltip-z-index, 500) !default;
$-tooltip-line-height: var(--wot-tooltip-line-height, 18px) !default; //
/* popover */
$-popover-bg: var(--wot-popover-bg, $-color-white) !default; //
$-popover-color: var(--wot-popover-color, rgba(0, 0, 0, 0.85)) !default; //
$-popover-box-shadow: var(--wot-popover-box-shadow, 0px 2px 10px 0px rgba(0, 0, 0, 0.1)) !default; //
$-popover-arrow-box-shadow: var(--wot-popover-arrow-box-shadow, 0px 2px 10px 0px rgba(0, 0, 0, 0.2)) !default; //
$-popover-border-color: var(--wot-popover-border-color, rgba(0, 0, 0, 0.09)) !default; //
$-popover-radius: var(--wot-popover-radius, 4px) !default; //
$-popover-arrow-size: var(--wot-popover-arrow-size, 6px) !default; //
$-popover-fs: var(--wot-popover-fs, $-fs-content) !default; //
$-popover-padding: var(--wot-popover-padding, 15px) !default; //
$-popover-line-height: var(--wot-popover-line-height, 18px) !default; //
$-popover-z-index: var(--wot-popover-z-index, $-tooltip-z-index) !default;
/* grid-item */
$-grid-item-fs: var(--wot-grid-item-fs, 12px) !default; //
$-grid-item-bg: var(--wot-grid-item-bg, $-color-white) !default; //
$-grid-item-padding: var(--wot-grid-item-padding, 14px 0px) !default; // padding
$-grid-item-border-color: var(--wot-grid-item-border-color, $-color-border-light) !default; //
/* statustip */
$-statustip-fs: var(--wot-statustip-fs, $-fs-content) !default; //
$-statustip-color: var(--wot-statustip-color, rgba(0, 0, 0, 0.45)) !default; //
$-statustip-line-height: var(--wot-statustip-line-height, 16px) !default; //
$-statustip-padding: var(--wot-statustip-padding, 5px 10px) !default; //
/* card */
$-card-bg: var(--wot-card-bg, $-color-white) !default; //
$-card-fs: var(--wot-card-fs, $-fs-content) !default; //
$-card-padding: var(--wot-card-padding, 0 $-size-side-padding) !default; //
$-card-footer-padding: var(--wot-card-footer-padding, 12px 0 16px) !default; //
$-card-shadow-color: var(--wot-card-shadow-color, 0px 4px 8px 0px rgba(0, 0, 0, 0.02)) !default; //
$-card-radius: var(--wot-card-radius, 8px) !default; //
$-card-line-height: var(--wot-card-line-height, 1.1) !default; //
$-card-margin: var(--wot-card-margin, 0 $-size-side-padding) !default; //
$-card-title-color: var(--wot-card-title-color, rgba(0, 0, 0, 0.85)) !default; //
$-card-title-fs: var(--wot-card-title-fs, $-fs-title) !default; //
$-card-content-border-color: var(--wot-card-content-border-color, rgba(0, 0, 0, 0.09)) !default; //
$-card-rectangle-title-padding: var(--wot-card-rectangle-title-padding, 15px 15px 12px) !default; //
$-card-rectangle-content-padding: var(--wot-card-rectangle-content-padding, 16px 0) !default; //
$-card-rectangle-footer-padding: var(--wot-card-rectangle-footer-padding, 12px 0) !default; //
$-card-content-color: var(--wot-card-content-color, rgba(0, 0, 0, 0.45)) !default; //
$-card-content-line-height: var(--wot-card-content-line-height, 1.428) !default; //
$-card-content-margin: var(--wot-card-content-margin, 13px 0 12px) !default; //
$-card-content-rectangle-margin: var(--wot-card-content-rectangle-margin, 14px 0 12px) !default; //
/* upload */
$-upload-size: var(--wot-upload-size, 80px) !default; // upload
$-upload-evoke-icon-size: var(--wot-upload-evoke-icon-size, 32px) !default; //
$-upload-evoke-bg: var(--wot-upload-evoke-bg, rgba(0, 0, 0, 0.04)) !default; //
$-upload-evoke-color: var(--wot-upload-evoke-color, rgba(0, 0, 0, 0.25)) !default; //
$-upload-evoke-disabled-color: var(--wot-upload-evoke-disabled-color, rgba(0, 0, 0, 0.09)) !default; //
$-upload-close-icon-size: var(--wot-upload-close-icon-size, 16px) !default; //
$-upload-close-icon-color: var(--wot-upload-close-icon-color, rgba(0, 0, 0, 0.65)) !default; //
$-upload-progress-fs: var(--wot-upload-progress-fs, 14px) !default; //
$-upload-preview-name-fs: var(--wot-upload-preview-name-fs, 12px) !default; //
$-upload-preview-icon-size: var(--wot-upload-preview-icon-size, 24px) !default; //
$-upload-preview-name-bg: var(--wot-upload-preview-name-bg, rgba(0, 0, 0, 0.6)) !default; //
$-upload-preview-name-height: var(--wot-upload-preview-name-height, 22px) !default; //
/* curtain */
$-curtain-content-radius: var(--wot-curtain-content-radius, 24px) !default; //
$-curtain-content-close-color: var(--wot-curtain-content-close-color, $-color-white) !default; //
$-curtain-content-close-fs: var(--wot-curtain-content-close-fs, $-fs-big) !default; //
// Notify
$-notify-text-color: var(--wot-notify-text-color, $-color-white) !default;
$-notify-padding: var(--wot-notify-padding, 8px 16px) !default;
$-notify-font-size: var(--wot-notify-font-size, $-fs-content) !default;
$-notify-line-height: var(--wot-notify-line-height, 20px) !default;
$-notify-primary-background: var(--wot-notify-primary-background, $-color-theme) !default;
$-notify-success-background: var(--wot-notify-success-background, $-color-success) !default;
$-notify-danger-background: var(--wot-notify-danger-background, $-color-danger) !default;
$-notify-warning-background: var(--wot-notify-warning-background, $-color-warning) !default;
// Skeleton
$-skeleton-background-color: var(--wot-skeleton-background-color, #eee) !default;
$-skeleton-animation-gradient: var(--wot-skeleton-animation-gradient, rgba(0, 0, 0, 0.04)) !default;
$-skeleton-animation-flashed: var(--wot-skeleton-animation-flashed, rgba(230, 230, 230, 0.3)) !default;
$-skeleton-text-height-default: var(--wot-skeleton-text-height-default, 16px) !default;
$-skeleton-rect-height-default: var(--wot-skeleton-rect-height-default, 16px) !default;
$-skeleton-circle-height-default: var(--wot-skeleton-circle-height-default, 48px) !default;
$-skeleton-row-margin-bottom: var(--wot-skeleton-row-margin-bottom, 16px) !default;
$-skeleton-border-radius-text: var(--wot-skeleton-border-radius-text, 2px) !default;
$-skeleton-border-radius-rect: var(--wot-skeleton-border-radius-rect, 4px) !default;
$-skeleton-border-radius-circle: var(--wot-skeleton-border-radius-circle, 50%) !default;
// circle
$-circle-text-color: var(--wot-circle-text-color, $-color-content) !default; // circle
// swiper
$-swiper-radius: var(--wot-swiper-radius, 8px);
$-swiper-item-padding: var(--wot-swiper-item-padding, 0);
// swiper-nav
// dot & dots-bar
$-swiper-nav-dot-color: var(--wot-swiper-nav-dot-color, $-font-white-2) !default;
$-swiper-nav-dot-active-color: var(--wot-swiper-nav-dot-active-color, $-font-white-1) !default;
$-swiper-nav-dot-size: var(--wot-swiper-nav-dot-size, 12rpx) !default;
$-swiper-nav-dots-bar-active-width: var(--wot-swiper-nav-dots-bar-active-width, 40rpx) !default;
// fraction
$-swiper-nav-fraction-color: var(--wot-swiper-nav-fraction-color, $-font-white-1) !default;
$-swiper-nav-fraction-bg-color: var(--wot-swiper-nav-fraction-bg-color, $-font-gray-3) !default;
$-swiper-nav-fraction-height: var(--wot-swiper-nav-fraction-height, 48rpx) !default;
$-swiper-nav-fraction-font-size: var(--wot-swiper-nav-fraction-font-size, 24rpx) !default;
// button
$-swiper-nav-btn-color: var(--wot-swiper-nav-btn-color, $-font-white-1) !default;
$-swiper-nav-btn-bg-color: var(--wot-swiper-nav-btn-bg-color, $-font-gray-3) !default;
$-swiper-nav-btn-size: var(--wot-swiper-nav-btn-size, 48rpx) !default;
// segmented
$-segmented-padding: var(--wot-segmented-padding, 4px) !default; // padding
$-segmented-item-bg-color: var(--wot-segmented-item-bg-color, #eeeeee) !default;
$-segmented-item-color: var(--wot-segmented-item-color, rgba(0, 0, 0, 0.85)) !default; //
$-segmented-item-acitve-bg: var(--wot-segmented-item-acitve-bg, #FFFFFF) !default; //
$-segmented-item-disabled-color: var(--wot-segmented-item-disabled-color, rgba(0, 0, 0, 0.25)) !default; //
// tabbar
$-tabbar-height: var(--wot-tabbar-height, 50px) !default;
$-tabbar-box-shadow: var(--wot-tabbar-box-shadow, 0 6px 30px 5px rgba(0, 0, 0, 0.05), 0 16px 24px 2px rgba(0, 0, 0, 0.04), 0 8px 10px -5px rgba(0, 0, 0, 0.08)) !default; // roundtabbar
// tabbar-item
$-tabbar-item-title-font-size: var(--wot-tabbar-item-title-font-size, 10px) !default; // tabbar
$-tabbar-item-title-line-height: var(--wot-tabbar-item-title-line-height, initial) !default; // tabbar
$-tabbar-inactive-color: var(--wot-tabbar-inactive-color, $-color-title) !default; //
$-tabbar-active-color: var(--wot-tabbar-active-color, $-color-theme) !default; //
// navbar
$-navbar-height: var(--wot-navbar-height, 44px) !default; // navbar
$-navbar-color: var(--wot-navbar-color, $-font-gray-1) !default; // navbar
$-navbar-background: var(--wot-navbar-background, $-color-white) !default; // navbar
$-navbar-arrow-size: var(--wot-navbar-arrow-size, 24px) !default; // navbar
$-navbar-desc-font-size: var(--wot-navbar-desc-font-size, 16px); // navbar
$-navbar-desc-font-color: var(--wot-navbar-desc-font-color, $-font-gray-1) !default; // navbar
$-navbar-title-font-size: var(--wot-navbar-title-font-size, 18px); // navbar title
$-navbar-title-font-weight: var(--wot-navbar-title-font-weight, 600); // navbar title
$-navbar-disabled-opacity: var(--wot-navbar-disabled-opacity, 0.6) !default; // navbar
$-navbar-hover-color :var(--wot-navbar-hover-color, #eee) !default; // navbar hover
// navbar capsule
$-navbar-capsule-border-color: var(--wot-navbar-capsule-border-color, #e7e7e7) !default;
$-navbar-capsule-border-radius: var(--wot-navbar-capsule-border-radius, 16px) !default;
$-navbar-capsule-width: var(--wot-navbar-capsule-width, 88px) !default;
$-navbar-capsule-height: var(--wot-navbar-capsule-height, 32px) !default;
// table
$-table-color: var(--wot-table-color, $-font-gray-1) !default; //
$-table-bg: var(--wot-table-bg, #FFFFFF) !default; //
$-table-stripe-bg: var(--wot-table-stripe-bg, #f3f3f3) !default; //
$-table-border-color: var(--wot-table-border-color, #ececec) !default; //
$-table-font-size: var(--wot-table-font-size, 13px) !default; //
// sidebar
$-sidebar-bg: var(--wot-sidebar-bg, $-color-gray-1) !default; //
$-sidebar-width: var(--wot-sidebar-width, 104px) !default; //
$-sidebar-height: var(--wot-sidebar-height, 100%) !default; //
// sidebar-item
$-sidebar-color: var(--wd-sidebar-color, $-font-gray-1) !default;
$-sidebar-item-height: var(--wd-sidebar-item-height, 56px) !default;
$-sidebar-item-line-height: var(--wd-sidebar-item-line-height, 24px) !default;
$-sidebar-disabled-color: var(--wd-side-bar-disabled-color, $-font-gray-4) !default;
$-sidebar-active-color: var(--wd-sidebar-active-color, $-color-theme) !default; //
$-sidebar-active-bg: var(--wot-sidebar-active-bg, $-color-white) !default; //
$-sidebar-hover-bg: var(--wot-sidebar-hover-bg, $-color-gray-2) !default; //
$-sidebar-border-radius: var(--wd-sidebar-border-radius, 8px) !default;
$-sidebar-font-size: var(--wd-sidebar-font-size, 16px) !default;
$-sidebar-icon-size: var(--wd-sidebar-icon-size, 20px) !default;
$-sidebar-active-border-width: var(--wd-sidebar-active-border-width, 4px) !default;
$-sidebar-active-border-height: var(--wd-sidebar-active-border-height, 16px) !default;
// fab
$-fab-trigger-height: var(--wd-fab-trigger-height, 56px) !default;
$-fab-trigger-width: var(--wd-fab-trigger-width, 56px) !default;
$-fab-actions-padding: var(--wd-actions-padding, 12px) !default;
$-fab-top: var(--wd-fab-top, 16px) !default;
$-fab-left: var(--wd-fab-left, 16px) !default;
$-fab-right: var(--wd-fab-right, 16px) !default;
$-fab-bottom: var(--wd-fab-bottom, 16px) !default;
// count-down
$-count-down-text-color: var(--wd-count-down-text-color, $-color-gray-8) !default;
$-count-down-font-size: var(--wd-count-down-font-size, $-fs-content) !default;
$-count-down-line-height: var(--wd-count-down-line-height, 20px) !default;
// keyboard
$-number-keyboard-key-height: var(--wd-number-keyboard-key-height, 48px) !default;
$-number-keyboard-key-font-size: var(--wd-number-keyboard-key-font-size, 28px) !default;
$-number-keyboard-key-background: var(--wd-number-keyboard-key-background, $-color-white) !default;
$-number-keyboard-key-border-radius: var(--wot-number-keyboard-key-border-radius, 8px) !default;
$-number-keyboard-delete-font-size: var(--wot-number-keyboard-delete-font-size, 16px) !default;
$-number-keyboard-key-active-color: var(--wot-number-keyboard-key-active-color, $-color-gray-3) !default;
$-number-keyboard-button-text-color: var(--wot-number-keyboard-button-text-color, $-color-white) !default;
$-number-keyboard-button-background: var(--wot-number-keyboard--button-background, $-color-theme) !default;
$-number-keyboard-button-active-opacity: var(--wot-number-keyboard-button-active-opacity, 0.6) !default;
$-number-keyboard-background: var(--wot-number-keyboard-background, $-color-gray-2) !default;
$-number-keyboard-title-height: var(--wot-number-keyboard-title-height, 34px) !default;
$-number-keyboard-title-color: var(--wot-number-keyboard-title-color, $-color-gray-7) !default;
$-number-keyboard-title-font-size: var(--wot-number-keyboard-title-font-size, 16px) !default;
$-number-keyboard-close-padding: var(--wot-number-keyboard-title-font-size, 0 16px) !default;
$-number-keyboard-close-color: var(--wot-number-keyboard-close-color, $-color-theme) !default;
$-number-keyboard-close-font-size: var(--wot-number-keyboard-close-font-size, 14px) !default;
// passwod-input
$-password-input-height: var(--wd-password-input-height, 50px);
$-password-input-margin: var(--wd-password-input-margin, 16px);
$-password-input-font-size: var(--wd-password-input-margin, 20px);
$-password-input-radius: var(--wd-password-input-radius, 6px);
$-password-input-background: var(--wd-password-input-background, #fff);
$-password-input-info-color: var(--wd-password-input-info-color, $-color-info);
$-password-input-info-font-size: var(--wd-password-input-info-font-size, $-fs-content);
$-password-input-border-color: var(--wd-password-border-color, #ebedf0);
$-password-input-error-info-color: var(--wd-password-input-error-info-color, $-color-danger);
$-password-input-dot-size: var(--wd-password-input-dot-size, 10px);
$-password-input-dot-color: var(--wd-password-input-dot-color, $-color-gray-8);
$-password-input-text-color: var(--wd-password-input-text-color, $-color-gray-8);
$-password-input-cursor-color: var(--wd-password-input-cursor-color, $-color-gray-8);
$-password-input-cursor-width: var(--wd-password-input-cursor-width, 1px);
$-password-input-cursor-height: var(--wd-password-input-cursor-height, 40%);
$-password-input-cursor-duration: var(--wd-password-input-cursor-duration, 1s);
// form-item
$-form-item-error-message-color: var(--wot-form-item-error-message-color, $-color-danger) !default;
$-form-item-error-message-font-size: var(--wot-form-item-error-message-font-size, $-fs-secondary) !default;
$-form-item-error-message-line-height: var(--wot-form-item-error-message-line-height, 24px) !default;

@ -0,0 +1,29 @@
const _b64chars = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/']
const _mkUriSafe = (src) => src.replace(/[+/]/g, (m0) => (m0 === '+' ? '-' : '_')).replace(/=+$/m, '')
const fromUint8Array = (src, rfc4648 = false) => {
let b64 = ''
for (let i = 0, l = src.length; i < l; i += 3) {
const [a0, a1, a2] = [src[i], src[i + 1], src[i + 2]]
const ord = (a0 << 16) | (a1 << 8) | a2
b64 += _b64chars[ord >>> 18]
b64 += _b64chars[(ord >>> 12) & 63]
b64 += typeof a1 !== 'undefined' ? _b64chars[(ord >>> 6) & 63] : '='
b64 += typeof a2 !== 'undefined' ? _b64chars[ord & 63] : '='
}
return rfc4648 ? _mkUriSafe(b64) : b64
}
const _btoa =
typeof btoa === 'function'
? (s) => btoa(s)
: (s) => {
if (s.charCodeAt() > 255) {
throw new RangeError('The string contains invalid characters.')
}
return fromUint8Array(Uint8Array.from(s, (c: any) => c.charCodeAt(0)))
}
const utob = (src) => unescape(encodeURIComponent(src))
export default function encode(src, rfc4648 = false) {
const b64 = _btoa(utob(src))
return rfc4648 ? _mkUriSafe(b64) : b64
}

@ -0,0 +1,34 @@
/*
* @Author: weisheng
* @Date: 2023-07-02 22:51:06
* @LastEditTime: 2023-07-13 10:07:17
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\common\clickoutside.ts
*
*/
let queue: any[] = []
export function pushToQueue(comp) {
queue.push(comp)
}
export function removeFromQueue(comp) {
queue = queue.filter((item) => {
return item.$.uid !== comp.$.uid
})
}
export function closeOther(comp) {
queue.forEach((item) => {
if (item.$.uid !== comp.$.uid) {
item.$.exposed.close()
}
})
}
export function closeOutside() {
queue.forEach((item) => {
item.$.exposed.close()
})
}

@ -0,0 +1,146 @@
/* eslint-disable */
class Dayjs {
utc: boolean
date: Date
timeZone: number
timeZoneString: any
mYear: any
mMonth: any
mDay: any
mWeek: any
mHour: any
mMinute: any
mSecond: any
constructor(dateStr) {
this.utc = false
const parsedDate = this.parseConfig(dateStr)
this.date = new Date(parsedDate)
this.timeZone = this.date.getTimezoneOffset() / 60
this.timeZoneString = this.padNumber(String(-1 * this.timeZone).replace(/^(.)?(\d)/, '$10$200'), 5, '+')
this.mYear = this.date.getFullYear()
this.mMonth = this.date.getMonth()
this.mDay = this.date.getDate()
this.mWeek = this.date.getDay()
this.mHour = this.date.getHours()
this.mMinute = this.date.getMinutes()
this.mSecond = this.date.getSeconds()
}
parseConfig(dateStr) {
if (!dateStr) return new Date()
if (dateStr instanceof Date) return dateStr
if (/^(\d){8}$/.test(dateStr)) {
this.utc = true
return `${dateStr.substr(0, 4)}-${dateStr.substr(4, 2)}-${dateStr.substr(6, 2)}`
}
return dateStr
}
padNumber(num, length, padChar) {
return !num || num.length >= length ? num : `${Array(length + 1 - num.length).join(padChar)}${num}`
}
year() {
return this.mYear
}
month() {
return this.mMonth
}
unix() {
const timeZoneOffset = this.utc ? 60 * this.timeZone * 60 * 1000 : 0
return Math.floor((this.date.getTime() + timeZoneOffset) / 1000)
}
toString() {
return this.date.toUTCString()
}
startOf(unit) {
switch (unit) {
case 'year':
return new Dayjs(new Date(this.year(), 0, 1))
case 'month':
return new Dayjs(new Date(this.year(), this.month(), 1))
default:
return this
}
}
add(amount, unit) {
let interval
switch (unit) {
case 'm':
case 'minutes':
interval = 60
break
case 'h':
case 'hours':
interval = 60 * 60
break
case 'd':
case 'days':
interval = 24 * 60 * 60
break
case 'w':
case 'weeks':
interval = 7 * 24 * 60 * 60
break
default:
interval = 1
}
const newUnixTime = this.unix() + amount * interval
return new Dayjs(1000 * newUnixTime)
}
subtract(amount, unit) {
return this.add(-1 * amount, unit)
}
format(formatStr = 'YYYY-MM-DDTHH:mm:ssZ') {
const weekdays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
return formatStr.replace(/Y{2,4}|M{1,2}|D{1,2}|d{1,4}|H{1,2}|m{1,2}|s{1,2}|Z{1,2}/g, (match) => {
switch (match) {
case 'YY':
return String(this.mYear).slice(-2)
case 'YYYY':
return String(this.mYear)
case 'M':
return String(this.mMonth + 1)
case 'MM':
return this.padNumber(String(this.mMonth + 1), 2, '0')
case 'D':
return String(this.mDay)
case 'DD':
return this.padNumber(String(this.mDay), 2, '0')
case 'd':
return String(this.mWeek)
case 'dddd':
return weekdays[this.mWeek]
case 'H':
return String(this.mHour)
case 'HH':
return this.padNumber(String(this.mHour), 2, '0')
case 'm':
return String(this.mMinute)
case 'mm':
return this.padNumber(String(this.mMinute), 2, '0')
case 's':
return String(this.mSecond)
case 'ss':
return this.padNumber(String(this.mSecond), 2, '0')
case 'Z':
return `${this.timeZoneString.slice(0, -2)}:00`
case 'ZZ':
return this.timeZoneString
default:
return match
}
})
}
}
export function dayjs(dateStr?: string | number | Date) {
return new Dayjs(dateStr)
}

@ -0,0 +1,206 @@
import isObject from './isObject'
import root from './internal/root'
/**
* Creates a debounced function that delays invoking `func` until after `wait`
* milliseconds have elapsed since the last time the debounced function was
* invoked, or until the next browser frame is drawn. The debounced function
* comes with a `cancel` method to cancel delayed `func` invocations and a
* `flush` method to immediately invoke them. Provide `options` to indicate
* whether `func` should be invoked on the leading and/or trailing edge of the
* `wait` timeout. The `func` is invoked with the last arguments provided to the
* debounced function. Subsequent calls to the debounced function return the
* result of the last `func` invocation.
*
* **Note:** If `leading` and `trailing` options are `true`, `func` is
* invoked on the trailing edge of the timeout only if the debounced function
* is invoked more than once during the `wait` timeout.
*
* If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
* until the next tick, similar to `setTimeout` with a timeout of `0`.
*
* If `wait` is omitted in an environment with `requestAnimationFrame`, `func`
* invocation will be deferred until the next frame is drawn (typically about
* 16ms).
*
* See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
* for details over the differences between `debounce` and `throttle`.
*
* @since 0.1.0
* @category Function
* @param {Function} func The function to debounce.
* @param {number} [wait=0]
* The number of milliseconds to delay; if omitted, `requestAnimationFrame` is
* used (if available).
* @param {Object} [options={}] The options object.
* @param {boolean} [options.leading=false]
* Specify invoking on the leading edge of the timeout.
* @param {number} [options.maxWait]
* The maximum time `func` is allowed to be delayed before it's invoked.
* @param {boolean} [options.trailing=true]
* Specify invoking on the trailing edge of the timeout.
* @returns {Function} Returns the new debounced function.
* @example
*
* // Avoid costly calculations while the window size is in flux.
* jQuery(window).on('resize', debounce(calculateLayout, 150))
*
* // Invoke `sendMail` when clicked, debouncing subsequent calls.
* jQuery(element).on('click', debounce(sendMail, 300, {
* 'leading': true,
* 'trailing': false
* }))
*
* // Ensure `batchLog` is invoked once after 1 second of debounced calls.
* const debounced = debounce(batchLog, 250, { 'maxWait': 1000 })
* const source = new EventSource('/stream')
* jQuery(source).on('message', debounced)
*
* // Cancel the trailing debounced invocation.
* jQuery(window).on('popstate', debounced.cancel)
*
* // Check for pending invocations.
* const status = debounced.pending() ? "Pending..." : "Ready"
*/
function debounce(func, wait, options?) {
let lastArgs, lastThis, maxWait, result, timerId, lastCallTime
let lastInvokeTime = 0
let leading = false
let maxing = false
let trailing = true
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
const useRAF = !wait && wait !== 0 && typeof root.requestAnimationFrame === 'function'
if (typeof func !== 'function') {
throw new TypeError('Expected a function')
}
wait = +wait || 0
if (isObject(options)) {
leading = !!options.leading
maxing = 'maxWait' in options
maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait
trailing = 'trailing' in options ? !!options.trailing : trailing
}
function invokeFunc(time) {
const args = lastArgs
const thisArg = lastThis
lastArgs = lastThis = undefined
lastInvokeTime = time
result = func.apply(thisArg, args)
return result
}
function startTimer(pendingFunc, wait) {
if (useRAF) {
root.cancelAnimationFrame(timerId)
return root.requestAnimationFrame(pendingFunc)
}
return setTimeout(pendingFunc, wait)
}
function cancelTimer(id) {
if (useRAF) {
return root.cancelAnimationFrame(id)
}
clearTimeout(id)
}
function leadingEdge(time) {
// Reset any `maxWait` timer.
lastInvokeTime = time
// Start the timer for the trailing edge.
timerId = startTimer(timerExpired, wait)
// Invoke the leading edge.
return leading ? invokeFunc(time) : result
}
function remainingWait(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
const timeWaiting = wait - timeSinceLastCall
return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting
}
function shouldInvoke(time) {
const timeSinceLastCall = time - lastCallTime
const timeSinceLastInvoke = time - lastInvokeTime
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
// it as the trailing edge, or we've hit the `maxWait` limit.
return lastCallTime === undefined || timeSinceLastCall >= wait || timeSinceLastCall < 0 || (maxing && timeSinceLastInvoke >= maxWait)
}
function timerExpired() {
const time = Date.now()
if (shouldInvoke(time)) {
return trailingEdge(time)
}
// Restart the timer.
timerId = startTimer(timerExpired, remainingWait(time))
}
function trailingEdge(time) {
timerId = undefined
// Only invoke if we have `lastArgs` which means `func` has been
// debounced at least once.
if (trailing && lastArgs) {
return invokeFunc(time)
}
lastArgs = lastThis = undefined
return result
}
function cancel() {
if (timerId !== undefined) {
cancelTimer(timerId)
}
lastInvokeTime = 0
lastArgs = lastCallTime = lastThis = timerId = undefined
}
function flush() {
return timerId === undefined ? result : trailingEdge(Date.now())
}
function pending() {
return timerId !== undefined
}
function debounced(this: any, ...args) {
const time = Date.now()
const isInvoking = shouldInvoke(time)
lastArgs = args
lastThis = this
lastCallTime = time
if (isInvoking) {
if (timerId === undefined) {
return leadingEdge(lastCallTime)
}
if (maxing) {
// Handle invocations in a tight loop.
timerId = startTimer(timerExpired, wait)
return invokeFunc(lastCallTime)
}
}
if (timerId === undefined) {
timerId = startTimer(timerExpired, wait)
}
return result
}
debounced.cancel = cancel
debounced.flush = flush
debounced.pending = pending
return debounced
}
export default debounce

@ -0,0 +1,4 @@
/** Detect free variable `global` from Node.js. */
const freeGlobal = typeof global === 'object' && global !== null && global.Object === Object && global
export default freeGlobal

@ -0,0 +1,23 @@
/*
* @Author: weisheng
* @Date: 2023-09-01 21:42:32
* @LastEditTime: 2023-09-03 11:43:06
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\common\lodash\internal\root.ts
*
*/
import freeGlobal from './freeGlobal'
/** Detect free variable `globalThis` */
// eslint-disable-next-line eqeqeq
const freeGlobalThis = typeof globalThis === 'object' && globalThis !== null && globalThis.Object == Object && globalThis
/** Detect free variable `self`. */
const freeSelf = typeof self === 'object' && self !== null && self.Object === Object && self
/** Used as a reference to the global object. */
// eslint-disable-next-line no-new-func
const root = freeGlobalThis || freeGlobal || freeSelf || Function('return this')()
export default root

@ -0,0 +1,29 @@
/**
* Checks if `value` is the
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @since 0.1.0
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* isObject({})
* // => true
*
* isObject([1, 2, 3])
* // => true
*
* isObject(Function)
* // => true
*
* isObject(null)
* // => false
*/
function isObject(value) {
const type = typeof value
return value != null && (type === 'object' || type === 'function')
}
export default isObject

@ -0,0 +1,555 @@
/* eslint-disable no-prototype-builtins */
import debounce from './lodash/debounce'
/**
* uuid
* @returns string
*/
export function uuid() {
return s4() + s4() + s4() + s4() + s4() + s4() + s4() + s4()
}
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1)
}
/**
* @description numpx
* @param {Number} num
* @return {string} num+px
*/
export function addUnit(num: number | string) {
return Number.isNaN(Number(num)) ? num : `${num}px`
}
/**
* @description target
* @param value
* @return {boolean}
*/
export function isObj(value: any): value is object {
return Object.prototype.toString.call(value) === '[object Object]' || typeof value === 'object'
}
/**
*
* @param target
* @returns {string} type
*/
export function getType(target: unknown): string {
// 得到原生类型
const typeStr = Object.prototype.toString.call(target)
// 拿到类型值
const match = typeStr.match(/\[object (\w+)\]/)
const type = match && match.length ? match[1].toLowerCase() : ''
// 类型值转小写并返回
return type
}
/**
* @description - picker
* @param items -
* @param kv - labelKey
* @returns
*/
export const defaultDisplayFormat = function (items: any[] | Record<string, any>, kv?: { labelKey?: string }): string {
const labelKey: string = kv?.labelKey || 'value'
if (Array.isArray(items)) {
return items.map((item) => item[labelKey]).join(', ')
} else {
return items[labelKey]
}
}
/**
* @description - pickerView
* @param value
* @return value
*/
export const defaultFunction = <T>(value: T): T => value
/**
* @description
* @param value
* @return {Boolean}
*/
export const isDef = <T>(value: T): value is NonNullable<T> => value !== undefined && value !== null
export { debounce }
/**
* @description
* @param {number} num
* @param {string} label
*/
export const checkNumRange = (num: number, label: string = 'value'): void => {
if (num < 0) {
throw new Error(`${label} shouldn't be less than zero`)
}
}
/**
* @description pixel
* @param {number} num
* @param {string} label
*/
export const checkPixelRange = (num: number, label: string = 'value'): void => {
if (num <= 0) {
throw new Error(`${label} should be greater than zero`)
}
}
/**
* RGB
* @param {number} r - (0-255)
* @param {number} g - 绿 (0-255)
* @param {number} b - (0-255)
* @returns {string} (#RRGGBB)
*/
export function rgbToHex(r: number, g: number, b: number): string {
// 将 RGB 分量组合成一个十六进制数。
const hex = ((r << 16) | (g << 8) | b).toString(16)
// 使用零填充十六进制数,确保它有 6 位数字RGB 范围)。
const paddedHex = '#' + '0'.repeat(Math.max(0, 6 - hex.length)) + hex
return paddedHex
}
/**
* RGB
* @param hex '#RRGGBB'
* @returns 绿
*/
function hexToRgb(hex: string): number[] {
const rgb: number[] = []
// 从第一个字符开始,每两个字符代表一个颜色分量
for (let i = 1; i < 7; i += 2) {
// 将两个字符的十六进制转换为十进制,并添加到 rgb 数组中
rgb.push(parseInt('0x' + hex.slice(i, i + 2), 16))
}
return rgb
}
/**
*
* @param {string} startColor
* @param {string} endColor
* @param {number} step
* @returns {string[]}
*/
export const gradient = (startColor: string, endColor: string, step: number = 2): string[] => {
// 将hex转换为rgb
const sColor: number[] = hexToRgb(startColor)
const eColor: number[] = hexToRgb(endColor)
// 计算R\G\B每一步的差值
const rStep: number = (eColor[0] - sColor[0]) / step
const gStep: number = (eColor[1] - sColor[1]) / step
const bStep: number = (eColor[2] - sColor[2]) / step
const gradientColorArr: string[] = []
for (let i = 0; i < step; i++) {
// 计算每一步的hex值
gradientColorArr.push(
rgbToHex(parseInt(String(rStep * i + sColor[0])), parseInt(String(gStep * i + sColor[1])), parseInt(String(bStep * i + sColor[2])))
)
}
return gradientColorArr
}
/**
*
* @param {number} num
* @param {number} min
* @param {number} max
* @returns {number}
*/
export const range = (num: number, min: number, max: number): number => {
// 使用 Math.min 和 Math.max 保证 num 不会超出指定范围
return Math.min(Math.max(num, min), max)
}
/**
*
* @param {any} value1
* @param {any} value2
* @returns {boolean} true false
*/
export const isEqual = (value1: any, value2: any): boolean => {
// 使用严格相等运算符比较值是否相等
if (value1 === value2) {
return true
}
// 如果其中一个值不是数组,则认为值不相等
if (!Array.isArray(value1) || !Array.isArray(value2)) {
return false
}
// 如果数组长度不相等,则认为值不相等
if (value1.length !== value2.length) {
return false
}
// 逐个比较数组元素是否相等
for (let i = 0; i < value1.length; ++i) {
if (value1[i] !== value2[i]) {
return false
}
}
// 所有比较均通过,则认为值相等
return true
}
/**
* 使
* @param {number | string} number
* @param {number} length 2
* @returns {string}
*/
export const padZero = (number: number | string, length: number = 2): string => {
// 将输入转换为字符串
let numStr: string = number.toString()
// 在数字前补零,直到达到指定长度
while (numStr.length < length) {
numStr = '0' + numStr
}
return numStr
}
/** @description 全局变量id */
export const context = {
id: 1000
}
/**
*
* @param selector #id,.class
* @param all selector
* @param scope
* @returns
*/
export function getRect(selector: string, all: boolean = false, scope?: any) {
return new Promise<UniApp.NodeInfo | UniApp.NodeInfo[]>((resolve) => {
let query: UniNamespace.SelectorQuery | null = null
if (scope) {
query = uni.createSelectorQuery().in(scope)
} else {
query = uni.createSelectorQuery()
}
query[all ? 'selectAll' : 'select'](selector)
.boundingClientRect((rect) => {
if (all && Array.isArray(rect) && rect.length) {
resolve(rect)
}
if (!all && rect) {
resolve(rect)
}
})
.exec()
})
}
/**
* 线
* @param {string} word
* @returns {string}
*/
export function kebabCase(word: string): string {
// 使用正则表达式匹配所有大写字母,并在前面加上短横线,然后转换为小写
const newWord: string = word
.replace(/[A-Z]/g, function (match) {
return '-' + match
})
.toLowerCase()
return newWord
}
/**
*
* @param {any} value
* @returns {boolean} true false
*/
export function isArray(value: any): value is Array<any> {
// 如果 Array.isArray 函数可用,直接使用该函数检查
if (typeof Array.isArray === 'function') {
return Array.isArray(value)
}
// 否则,使用对象原型的 toString 方法进行检查
return Object.prototype.toString.call(value) === '[object Array]'
}
/**
*
* @param {any} value
* @returns {boolean} true false
*/
// eslint-disable-next-line @typescript-eslint/ban-types
export function isFunction<T extends Function>(value: any): value is T {
return getType(value) === 'function'
}
/**
*
* @param {unknown} value
* @returns {value is string} true false
*/
export function isString(value: unknown): value is string {
return getType(value) === 'string'
}
/**
*
* @param {*} value
*/
export function isNumber(value: any): value is number {
return getType(value) === 'number'
}
/**
* Promise
* @param {unknown} value
* @returns {value is Promise<any>} Promise true false
*/
export function isPromise(value: unknown): value is Promise<any> {
// 先将 value 断言为 object 类型
if (isObj(value)) {
// 然后进一步检查 value 是否具有 then 和 catch 方法,并且它们是函数类型
return isFunction((value as Promise<any>).then) && isFunction((value as Promise<any>).catch)
}
return false // 如果 value 不是对象类型,则肯定不是 Promise
}
/**
*
* @param value
* @returns truefalse
*/
export function isBoolean(value: any): value is boolean {
return typeof value === 'boolean'
}
/**
*
* @param value
* @returns
*/
export function isOdd(value: number): boolean {
if (typeof value !== 'number') {
throw new Error('输入必须为数字')
}
// 使用取模运算符来判断是否为奇数
// 如果 number 除以 2 的余数为 1就是奇数
// 否则是偶数
return value % 2 === 1
}
/**
* base64
* @param {string} url
* @return
*/
export function isBase64Image(url: string) {
// 使用正则表达式检查URL是否以"data:image"开头这是Base64图片的常见前缀
return /^data:image\/(png|jpg|jpeg|gif|bmp);base64,/.test(url)
}
/**
* CSS
* @param {object | object[]} styles
* @returns {string} CSS
*/
export function objToStyle(styles: object | object[]): string {
// 如果 styles 是数组类型
if (isArray(styles)) {
// 使用过滤函数去除空值和 null 值的元素
// 对每个非空元素递归调用 objToStyle然后通过分号连接
return styles
.filter(function (item) {
return item != null && item !== ''
})
.map(function (item) {
return objToStyle(item)
})
.join(';')
}
if (isString(styles)) {
return styles
}
// 如果 styles 是对象类型
if (isObj(styles)) {
// 使用 Object.keys 获取所有属性名
// 使用过滤函数去除值为 null 或空字符串的属性
// 对每个属性名和属性值进行格式化,通过分号连接
return Object.keys(styles)
.filter(function (key) {
return styles[key] != null && styles[key] !== ''
})
.map(function (key) {
// 使用 kebabCase 函数将属性名转换为 kebab-case 格式
// 将属性名和属性值格式化为 CSS 样式的键值对
return [kebabCase(key), styles[key]].join(':')
})
.join(';')
}
// 如果 styles 不是对象也不是数组,则直接返回
return ''
}
export const requestAnimationFrame = (cb = () => void 0) => {
return new Promise((resolve, reject) => {
const timer = setInterval(() => {
clearInterval(timer)
resolve(true)
cb()
}, 1000 / 30)
})
}
/**
*
* @param obj
* @param cache
* @returns
*/
export function deepClone<T>(obj: T, cache: Map<any, any> = new Map()): T {
// 如果对象为 null 或者不是对象类型,则直接返回该对象
if (obj === null || typeof obj !== 'object') {
return obj
}
// 处理特殊对象类型:日期、正则表达式、错误对象
if (obj instanceof Date) {
return new Date(obj.getTime()) as any
}
if (obj instanceof RegExp) {
return new RegExp(obj.source, obj.flags) as any
}
if (obj instanceof Error) {
const errorCopy = new Error(obj.message) as any
errorCopy.stack = obj.stack
return errorCopy
}
// 检查缓存中是否已存在该对象的复制
if (cache.has(obj)) {
return cache.get(obj)
}
// 根据原始对象的类型创建对应的空对象或数组
const copy: any = Array.isArray(obj) ? [] : {}
// 将当前对象添加到缓存中
cache.set(obj, copy)
// 递归地深拷贝对象的每个属性
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
copy[key] = deepClone(obj[key], cache)
}
}
return copy as T
}
/**
*
* @param target
* @param source
* @returns
*/
export function deepMerge<T extends Record<string, any>>(target: T, source: Record<string, any>): T {
// 深拷贝目标对象,避免修改原始对象
target = deepClone(target)
// 检查目标和源是否都是对象类型
if (typeof target !== 'object' || typeof source !== 'object') {
throw new Error('Both target and source must be objects.')
}
// 遍历源对象的属性
for (const prop in source) {
if (!source.hasOwnProperty(prop))
continue
// 使用类型断言,告诉 TypeScript 这是有效的属性
;(target as Record<string, any>)[prop] = source[prop]
}
return target
}
/**
* URL
* @param baseUrl URL
* @param params URL
* @returns URL
*/
export function buildUrlWithParams(baseUrl: string, params: Record<string, string>) {
// 将参数对象转换为查询字符串
const queryString = Object.entries(params)
.map(([key, value]) => `${key}=${encodeURIComponent(value)}`)
.join('&')
// 检查基础URL是否已包含查询字符串并选择适当的分隔符
const separator = baseUrl.includes('?') ? '&' : '?'
// 返回带有参数的URL
return `${baseUrl}${separator}${queryString}`
}
// eslint-disable-next-line @typescript-eslint/ban-types
export function throttle(func: Function, wait: number): Function {
let timeout: NodeJS.Timeout | null
let previous = 0
const throttled = function (this: any, ...args: any[]) {
const now = Date.now()
const remaining = wait - (now - previous)
if (remaining <= 0) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
previous = now
func.apply(this, args)
} else if (!timeout) {
timeout = setTimeout(() => {
previous = Date.now()
timeout = null
func.apply(this, args)
}, remaining)
}
}
return throttled
}
/**
*
* @param obj
* @param path
* @returns null undefined undefined
*/
export const getPropByPath = (obj: any, path: string): any => {
const keys: string[] = path.split('.')
try {
return keys.reduce((acc: any, key: string) => (acc !== undefined && acc !== null ? acc[key] : undefined), obj)
} catch (error) {
return undefined
}
}

@ -0,0 +1,45 @@
import { getCurrentInstance, inject, onMounted, onBeforeMount, ref, watch } from 'vue'
export function useCell() {
const border = ref<boolean>(false) // 是否展示边框
const cellGroup: any = inject('cell-group', null) || {}
const cellList: any = inject('cell-list', null) || ref<any[]>([])
const { proxy } = getCurrentInstance() as any
watch(
() => cellGroup.border,
(newVal) => {
setIndexAndStatus(Boolean(newVal), proxy.$.uid)
},
{
deep: true,
immediate: true
}
)
onBeforeMount(() => {
cellList.value = [...cellList.value.concat([{ uid: proxy.$.uid }])]
setIndexAndStatus(cellGroup.border, proxy.$.uid)
})
/**
* @description cellGroup
* @return {Number}
*/
function getIndex(uuid: string) {
if (!cellList || !cellList.value) return
return cellList.value.findIndex((cell) => {
return cell.uid === uuid
})
}
/**
* @description 0线cellGroup
*/
function setIndexAndStatus(isBorder: boolean, uuid: string) {
const index = getIndex(uuid)
border.value = isBorder && index
}
return { border, cellGroup, cellList, setIndexAndStatus, getIndex }
}

@ -0,0 +1,113 @@
import {
VNode,
provide,
reactive,
InjectionKey,
getCurrentInstance,
VNodeNormalizedChildren,
ComponentPublicInstance,
ComponentInternalInstance
} from 'vue'
// 小程序端不支持从vue导出的isVNode方法参考uni-mp-vue的实现
function isVNode(value: any): value is VNode {
return value ? value.__v_isVNode === true : false
}
export function flattenVNodes(children: VNodeNormalizedChildren) {
const result: VNode[] = []
const traverse = (children: VNodeNormalizedChildren) => {
if (Array.isArray(children)) {
children.forEach((child) => {
if (isVNode(child)) {
result.push(child)
if (child.component?.subTree) {
result.push(child.component.subTree)
traverse(child.component.subTree.children)
}
if (child.children) {
traverse(child.children)
}
}
})
}
}
traverse(children)
return result
}
const findVNodeIndex = (vnodes: VNode[], vnode: VNode) => {
const index = vnodes.indexOf(vnode)
if (index === -1) {
return vnodes.findIndex((item) => vnode.key !== undefined && vnode.key !== null && item.type === vnode.type && item.key === vnode.key)
}
return index
}
// sort children instances by vnodes order
export function sortChildren(
parent: ComponentInternalInstance,
publicChildren: ComponentPublicInstance[],
internalChildren: ComponentInternalInstance[]
) {
const vnodes = parent && parent.subTree && parent.subTree.children ? flattenVNodes(parent.subTree.children) : []
internalChildren.sort((a, b) => findVNodeIndex(vnodes, a.vnode) - findVNodeIndex(vnodes, b.vnode))
const orderedPublicChildren = internalChildren.map((item) => item.proxy!)
publicChildren.sort((a, b) => {
const indexA = orderedPublicChildren.indexOf(a)
const indexB = orderedPublicChildren.indexOf(b)
return indexA - indexB
})
}
export function useChildren<
// eslint-disable-next-line
Child extends ComponentPublicInstance = ComponentPublicInstance<{}, any>,
ProvideValue = never
>(key: InjectionKey<ProvideValue>) {
const publicChildren: Child[] = reactive([])
const internalChildren: ComponentInternalInstance[] = reactive([])
const parent = getCurrentInstance()!
const linkChildren = (value?: ProvideValue) => {
const link = (child: ComponentInternalInstance) => {
if (child.proxy) {
internalChildren.push(child)
publicChildren.push(child.proxy as Child)
sortChildren(parent, publicChildren, internalChildren)
}
}
const unlink = (child: ComponentInternalInstance) => {
const index = internalChildren.indexOf(child)
publicChildren.splice(index, 1)
internalChildren.splice(index, 1)
}
provide(
key,
Object.assign(
{
link,
unlink,
children: publicChildren,
internalChildren
},
value
)
)
}
return {
children: publicChildren,
linkChildren
}
}

@ -0,0 +1,159 @@
import { ref, computed, onBeforeUnmount } from 'vue'
import { isDef } from '../common/util'
// 定义倒计时时间的数据结构
export type CurrentTime = {
days: number
hours: number
total: number
minutes: number
seconds: number
milliseconds: number
}
// 定义倒计时的配置项
export type UseCountDownOptions = {
time: number // 倒计时总时间,单位为毫秒
millisecond?: boolean // 是否开启毫秒级倒计时,默认为 false
onChange?: (current: CurrentTime) => void // 倒计时每次变化时的回调函数
onFinish?: () => void // 倒计时结束时的回调函数
}
// 定义常量
const SECOND = 1000
const MINUTE = 60 * SECOND
const HOUR = 60 * MINUTE
const DAY = 24 * HOUR
// 将时间转换为倒计时数据结构
function parseTime(time: number): CurrentTime {
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 {
total: time,
days,
hours,
minutes,
seconds,
milliseconds
}
}
// 判断两个时间是否在同一秒内
function isSameSecond(time1: number, time2: number): boolean {
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
}
// 判断当前环境是否为 H5
const isH5 = process.env.UNI_PLATFORM === 'h5'
// 封装 requestAnimationFrame 和 setTimeout
function raf(fn: FrameRequestCallback): number {
return isH5 ? requestAnimationFrame(fn) : setTimeout(fn, 33)
}
function cancelRaf(id: number) {
if (isH5) {
cancelAnimationFrame(id)
} else {
clearTimeout(id)
}
}
// 定义 useCountDown 函数
export function useCountDown(options: UseCountDownOptions) {
let timer: number | null = null // 定时器
let endTime: number // 结束时间
let counting: boolean // 是否计时中
const remain = ref(options.time) // 剩余时间
const current = computed(() => parseTime(remain.value)) // 当前倒计时数据
// 暂停倒计时
const pause = () => {
counting = false
cancelRaf(timer!)
}
// 获取当前剩余时间
const getCurrentRemain = () => Math.max(endTime - Date.now(), 0)
// 设置剩余时间
const setRemain = (value: number) => {
remain.value = value
isDef(options.onChange) && options.onChange(current.value)
if (value === 0) {
pause()
isDef(options.onFinish) && options.onFinish()
}
}
// 每毫秒更新一次倒计时
const microTick = () => {
timer = raf(() => {
if (counting) {
setRemain(getCurrentRemain())
if (remain.value > 0) {
microTick()
}
}
})
}
// 每秒更新一次倒计时
const macroTick = () => {
timer = raf(() => {
if (counting) {
const remainRemain = getCurrentRemain()
if (!isSameSecond(remainRemain, remain.value) || remainRemain === 0) {
setRemain(remainRemain)
}
if (remain.value > 0) {
macroTick()
}
}
})
}
// 根据配置项选择更新方式
const tick = () => {
if (options.millisecond) {
microTick()
} else {
macroTick()
}
}
// 开始倒计时
const start = () => {
if (!counting) {
endTime = Date.now() + remain.value
counting = true
tick()
}
}
// 重置倒计时
const reset = (totalTime: number = options.time) => {
pause()
remain.value = totalTime
}
// 在组件卸载前暂停倒计时
onBeforeUnmount(pause)
return {
start,
pause,
reset,
current
}
}

@ -0,0 +1,32 @@
import { ref, watch } from 'vue'
function useLockScroll(shouldLock: () => boolean) {
const scrollLockCount = ref(0)
const lock = () => {
if (scrollLockCount.value === 0) {
document.getElementsByTagName('body')[0].style.overflow = 'hidden'
}
scrollLockCount.value++
}
const unlock = () => {
if (scrollLockCount.value > 0) {
scrollLockCount.value--
if (scrollLockCount.value === 0) {
document.getElementsByTagName('body')[0].style.overflow = ''
}
}
}
watch(shouldLock, (value) => {
value ? lock() : unlock()
})
return {
lock,
unlock
}
}
export default useLockScroll

@ -0,0 +1,32 @@
import { ref, inject, computed, onUnmounted, InjectionKey, getCurrentInstance, ComponentPublicInstance, ComponentInternalInstance } from 'vue'
type ParentProvide<T> = T & {
link(child: ComponentInternalInstance): void
unlink(child: ComponentInternalInstance): void
children: ComponentPublicInstance[]
internalChildren: ComponentInternalInstance[]
}
export function useParent<T>(key: InjectionKey<ParentProvide<T>>) {
const parent = inject(key, null)
if (parent) {
const instance = getCurrentInstance()!
const { link, unlink, internalChildren } = parent
link(instance)
onUnmounted(() => unlink(instance))
const index = computed(() => internalChildren.indexOf(instance))
return {
parent,
index
}
}
return {
parent: null,
index: ref(-1)
}
}

@ -0,0 +1,168 @@
import { getCurrentInstance, ref } from 'vue'
import { getRect } from '../common/util'
export function usePopover() {
const { proxy } = getCurrentInstance() as any
const popStyle = ref<string>('')
const arrowStyle = ref<string>('')
const showStyle = ref<string>('')
const arrowClass = ref<string>('')
const popWidth = ref<number>(0)
const popHeight = ref<number>(0)
const left = ref<number>(0)
const bottom = ref<number>(0)
const width = ref<number>(0)
const height = ref<number>(0)
const top = ref<number>(0)
function noop() {}
function init(
placement:
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'
| 'right'
| 'right-start'
| 'right-end',
visibleArrow: boolean,
selector: string
) {
// 初始化 class
if (visibleArrow) {
const arrowClassArr = [
`wd-${selector}__arrow`,
placement === 'bottom' || placement === 'bottom-start' || placement === 'bottom-end' ? `wd-${selector}__arrow-up` : '',
placement === 'left' || placement === 'left-start' || placement === 'left-end' ? `wd-${selector}__arrow-right` : '',
placement === 'right' || placement === 'right-start' || placement === 'right-end' ? `wd-${selector}__arrow-left` : '',
placement === 'top' || placement === 'top-start' || placement === 'top-end' ? `wd-${selector}__arrow-down` : ''
]
arrowClass.value = arrowClassArr.join(' ')
}
// 初始化数据获取
getRect('#target', false, proxy).then((rect: any) => {
if (!rect) return
left.value = rect.left
bottom.value = rect.bottom
width.value = rect.width
height.value = rect.height
top.value = rect.top
})
// 用透明度可在初始化时获取到pop尺寸
getRect('#pos', false, proxy).then((rect: any) => {
if (!rect) return
popWidth.value = rect.width
popHeight.value = rect.height
})
}
function checkType(value) {
return Object.prototype.toString.call(value).slice(8, -1)
}
function control(
placement:
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'
| 'right'
| 'right-start'
| 'right-end',
offset: number
) {
// arrow size
const arrowSize = 9
// 上下位(纵轴)对应的距离左边的距离
const verticalX = width.value / 2
// 上下位(纵轴)对应的距离底部的距离
const verticalY = arrowSize + height.value + 5
// 左右位(横轴)对应的距离左边的距离
const horizontalX = width.value + arrowSize + 5
// 左右位(横轴)对应的距离底部的距离
const horizontalY = height.value / 2
const offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset
const offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset
const placements = new Map([
// 上
['top', [`left: ${verticalX}px; bottom: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
[
'top-start',
[
`left: ${offsetX}px; bottom: ${verticalY}px;`,
`left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`
]
],
[
'top-end',
[
`right: ${offsetX}px; bottom: ${verticalY}px;`,
`right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`
]
],
// 下
['bottom', [`left: ${verticalX}px; top: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
[
'bottom-start',
[`left: ${offsetX}px; top: ${verticalY}px;`, `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`]
],
[
'bottom-end',
[
`right: ${offsetX}px; top: ${verticalY}px;`,
`right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`
]
],
// 左
['left', [`right: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
[
'left-start',
[
`right: ${horizontalX}px; top: ${offsetY}px;`,
`top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`
]
],
[
'left-end',
[
`right: ${horizontalX}px; bottom: ${offsetY}px;`,
`bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`
]
],
// 右
['right', [`left: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
[
'right-start',
[
`left: ${horizontalX}px; top: ${offsetY}px;`,
`top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`
]
],
[
'right-end',
[
`left: ${horizontalX}px; bottom: ${offsetY}px;`,
`bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`
]
]
])
popStyle.value = placements.get(placement)![0]
arrowStyle.value = placements.get(placement)![1]
}
return { popStyle, arrowStyle, showStyle, arrowClass, init, control, noop, checkType }
}

@ -0,0 +1,52 @@
import { type Ref, provide, ref } from 'vue'
export const queueKey = '__QUEUE_KEY__'
export interface Queue {
queue: Ref<any[]>
pushToQueue: (comp: any) => void
removeFromQueue: (comp: any) => void
closeOther: (comp: any) => void
closeOutside: () => void
}
export function useQueue() {
const queue = ref<any[]>([])
function pushToQueue(comp: any) {
queue.value.push(comp)
}
function removeFromQueue(comp: any) {
queue.value = queue.value.filter((item) => {
return item.$.uid !== comp.$.uid
})
}
function closeOther(comp: any) {
queue.value.forEach((item) => {
if (item.$.uid !== comp.$.uid) {
item.$.exposed.close()
}
})
}
function closeOutside() {
queue.value.forEach((item) => {
item.$.exposed.close()
})
}
provide(queueKey, {
queue,
pushToQueue,
removeFromQueue,
closeOther,
closeOutside
})
return {
closeOther,
closeOutside
}
}

@ -0,0 +1,43 @@
import { ref } from 'vue'
export function useTouch() {
const direction = ref<string>('')
const deltaX = ref<number>(0)
const deltaY = ref<number>(0)
const offsetX = ref<number>(0)
const offsetY = ref<number>(0)
const startX = ref<number>(0)
const startY = ref<number>(0)
function touchStart(event) {
const touch = event.touches[0]
direction.value = ''
deltaX.value = 0
deltaY.value = 0
offsetX.value = 0
offsetY.value = 0
startX.value = touch.clientX
startY.value = touch.clientY
}
function touchMove(event) {
const touch = event.touches[0]
deltaX.value = touch.clientX - startX.value
deltaY.value = touch.clientY - startY.value
offsetX.value = Math.abs(deltaX.value)
offsetY.value = Math.abs(deltaY.value)
direction.value = offsetX.value > offsetY.value ? 'horizontal' : offsetX.value < offsetY.value ? 'vertical' : ''
}
return {
touchStart,
touchMove,
direction,
deltaX,
deltaY,
offsetX,
offsetY,
startX,
startY
}
}

@ -0,0 +1,194 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(action-sheet) {
background-color: $-dark-background2;
color: $-dark-color;
@include e(action) {
color: $-dark-color;
background: $-dark-background2;
&:active {
background: $-dark-background4;
}
}
@include e(subname) {
color: $-dark-color3;
}
@include e(cancel) {
color: $-dark-color;
background: $-dark-background4;
&:active {
background: $-dark-background5;
}
}
:deep(.wd-action-sheet__close) {
color: $-dark-color3;
}
@include e(panel-title) {
color: $-dark-color;
}
@include e(header) {
color: $-dark-color;
}
}
}
:deep(.wd-action-sheet__popup){
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
}
@include b(action-sheet) {
background-color: $-color-white;
padding-bottom: 1px;
@include edeep(popup) {
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
}
@include e(actions) {
padding: 8px 0;
max-height: 50vh;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
@include e(action) {
position: relative;
display: block;
width: 100%;
height: $-action-sheet-action-height;
line-height: $-action-sheet-action-height;
color: $-action-sheet-color;
font-size: $-action-sheet-fs;
text-align: center;
border: none;
background: $-action-sheet-bg;
outline: none;
&:after {
display: none;
}
&:active {
background: $-action-sheet-active-color;
}
@include m(disabled) {
color: $-action-sheet-disabled-color;
}
@include m(loading) {
display: flex;
align-items: center;
justify-content: center;
line-height: initial;
}
}
@include e(name) {
display: inline-block;
}
@include e(subname) {
display: inline-block;
margin-left: 4px;
font-size: $-action-sheet-subname-fs;
color: $-action-sheet-subname-color;
}
@include e(cancel) {
display: block;
width: calc(100% - 48px);
line-height: $-action-sheet-cancel-height;
padding: 0;
color: $-action-sheet-cancel-color;
font-size: $-action-sheet-fs;
text-align: center;
border-radius: $-action-sheet-cancel-radius;
border: none;
background: $-action-sheet-cancel-bg;
outline: none;
margin: 0 auto 24px;
font-weight: $-action-sheet-weight;
&:active {
background: $-action-sheet-active-color;
}
&:after {
display: none;
}
}
@include e(header) {
color: $-action-sheet-color;
position: relative;
height: $-action-sheet-title-height;
line-height: $-action-sheet-title-height;
text-align: center;
font-size: $-action-sheet-title-fs;
font-weight: $-action-sheet-weight;
}
@include edeep(close) {
position: absolute;
top: $-action-sheet-close-top;
right: $-action-sheet-close-right;
color: $-action-sheet-close-color;
font-size: $-action-sheet-close-fs;
transform: rotate(-45deg);
line-height: 1.1;
}
@include e(panels) {
height: 84px;
overflow-y: hidden;
&:first-of-type {
margin-top: 20px;
}
&:last-of-type {
margin-bottom: 12px;
}
}
@include e(panels-content) {
display: flex;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
@include e(panel) {
width: 88px;
flex: 0 0 auto;
display: inline-block;
padding: $-action-sheet-panel-padding;
}
@include e(panel-img) {
display: block;
width: $-action-sheet-panel-img-fs;
height: $-action-sheet-panel-img-fs;
margin: 0 auto;
margin-bottom: 7px;
border-radius: $-action-sheet-panel-img-radius;
}
@include e(panel-title) {
font-size: $-action-sheet-subname-fs;
line-height: 1.2;
text-align: center;
color: $-action-sheet-color;
@include lineEllipsis;
}
}

@ -0,0 +1,197 @@
<template>
<view>
<wd-popup
custom-class="wd-action-sheet__popup"
:custom-style="`${(actions && actions.length) || (panels && panels.length) ? 'background: transparent;' : ''}`"
v-model="showPopup"
:duration="duration"
position="bottom"
:close-on-click-modal="closeOnClickModal"
:safe-area-inset-bottom="safeAreaInsetBottom"
:lazy-render="lazyRender"
@enter="handleOpen"
@close="close"
@after-enter="handleOpened"
@after-leave="handleClosed"
@click-modal="handleClickModal"
:z-index="zIndex"
>
<view
:class="`wd-action-sheet ${customClass}`"
:style="`${
(actions && actions.length) || (panels && panels.length)
? 'margin: 0 10px calc(var(--window-bottom) + 10px) 10px; border-radius: 16px;'
: 'margin-bottom: var(--window-bottom);'
} ${customStyle}`"
>
<view v-if="title" :class="`wd-action-sheet__header ${customHeaderClass}`">
{{ title }}
<wd-icon custom-class="wd-action-sheet__close" name="add" @click="close" />
</view>
<view class="wd-action-sheet__actions" v-if="actions && actions.length">
<button
v-for="(action, rowIndex) in actions"
:key="rowIndex"
:class="`wd-action-sheet__action ${action.disabled ? 'wd-action-sheet__action--disabled' : ''} ${
action.loading ? 'wd-action-sheet__action--loading' : ''
}`"
:style="`color: ${action.color}`"
@click="select(rowIndex, 'action')"
>
<wd-loading v-if="action.loading" size="20px" />
<view v-else class="wd-action-sheet__name">{{ action.name }}</view>
<view v-if="!action.loading && action.subname" class="wd-action-sheet__subname">{{ action.subname }}</view>
</button>
</view>
<view v-if="formatPanels && formatPanels.length">
<view v-for="(panel, rowIndex) in formatPanels" :key="rowIndex" class="wd-action-sheet__panels">
<view class="wd-action-sheet__panels-content">
<view v-for="(col, colIndex) in panel" :key="colIndex" class="wd-action-sheet__panel" @click="select(rowIndex, 'panels', colIndex)">
<image class="wd-action-sheet__panel-img" :src="(col as any).iconUrl" />
<view class="wd-action-sheet__panel-title">{{ (col as any).title }}</view>
</view>
</view>
</view>
</view>
<slot />
<button v-if="cancelText" class="wd-action-sheet__cancel" @click="handleCancel">{{ cancelText }}</button>
</view>
</wd-popup>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-action-sheet',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { watch, ref } from 'vue'
interface Action {
//
name: string
//
subname: string
//
color: string
//
disabled: boolean
//
loading: boolean
}
interface Panel {
//
iconUrl: string
//
title: string
}
interface Props {
customClass?: string
customHeaderClass?: string
customStyle?: string
modelValue: boolean
actions?: Array<Action>
panels?: Array<Panel>
title?: string
cancelText?: string
closeOnClickAction?: boolean
closeOnClickModal?: boolean
duration?: number
zIndex?: number
lazyRender?: boolean
safeAreaInsetBottom?: boolean
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customHeaderClass: '',
customStyle: '',
modelValue: false,
actions: () => [] as Array<Action>,
panels: () => [] as Array<Panel>,
closeOnClickAction: true,
closeOnClickModal: true,
duration: 200,
zIndex: 10,
lazyRender: true,
safeAreaInsetBottom: true
})
const formatPanels = ref<Array<Panel> | Array<Array<Panel>>>([])
const showPopup = ref<boolean>(false)
watch(() => props.panels, computedValue, { deep: true, immediate: true })
watch(
() => props.modelValue,
(newValue) => {
showPopup.value = newValue
},
{ deep: true, immediate: true }
)
const emit = defineEmits(['select', 'click-modal', 'cancel', 'closed', 'close', 'open', 'opened', 'update:modelValue'])
function isArray() {
return props.panels.length && !(props.panels[0] instanceof Array)
}
function computedValue() {
formatPanels.value = isArray() ? [props.panels] : props.panels
}
function select(rowIndex: number, type: 'action' | 'panels', colIndex?: number) {
if (type === 'action') {
emit('select', {
item: props.actions[rowIndex],
index: rowIndex
})
} else if (isArray()) {
emit('select', {
item: props.panels[Number(colIndex)],
index: colIndex
})
} else {
emit('select', {
item: props.panels[rowIndex][Number(colIndex)],
rowIndex,
colIndex
})
}
close()
}
function handleClickModal() {
emit('click-modal')
if (props.closeOnClickModal) {
close()
}
}
function handleCancel() {
emit('cancel')
close()
}
function close() {
emit('update:modelValue', false)
emit('close')
}
function handleOpen() {
emit('open')
}
function handleOpened() {
emit('opened')
}
function handleClosed() {
emit('closed')
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,60 @@
@import './../common/abstracts/_mixin.scss';
@import './../common/abstracts/variable.scss';
.wot-theme-dark {
@include b(badge) {
@include e(content) {
border-color: $-dark-background2;
}
}
}
@include b(badge) {
position: relative;
vertical-align: middle;
display: inline-block;
@include e(content) {
display: inline-block;
height: $-badge-height;
line-height: $-badge-height;
padding: $-badge-padding;
background-color: $-badge-bg;
border-radius: calc($-badge-height / 2 + 2px);
color: $-badge-color;
font-size: $-badge-fs;
text-align: center;
white-space: nowrap;
border: $-badge-border;
font-weight: 500;
@include when(fixed) {
position: absolute;
transform: translateY(-50%) translateX(50%);
}
@include when(dot) {
height: $-badge-dot-size;
width: $-badge-dot-size;
padding: 0;
border-radius: 50%;
}
@each $type in (primary, success, warning, info, danger) {
@include m($type) {
@if $type == primary {
background-color: $-badge-primary;
} @else if $type == success {
background-color: $-badge-success;
} @else if $type == warning {
background-color: $-badge-warning;
} @else if $type == info {
background-color: $-badge-info;
} @else {
background-color: $-badge-danger;
}
}
}
}
}

@ -0,0 +1,93 @@
<!--
* @Author: weisheng
* @Date: 2023-06-12 18:40:59
* @LastEditTime: 2023-11-22 13:11:29
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-badge\wd-badge.vue
* 记得注释
-->
<template>
<view :class="['wd-badge', customClass]" :style="customStyle">
<slot></slot>
<view
v-if="isBadgeShow"
:class="['wd-badge__content', 'is-fixed', type ? 'wd-badge__content--' + type : '', isDot ? 'is-dot' : '']"
:style="contentStyle"
>
{{ content }}
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-badge',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
type BadgeType = 'primary' | 'success' | 'warning' | 'danger' | 'info'
interface Props {
modelValue?: number | string | null
/** 当数值为 0 时,是否展示徽标 */
showZero?: boolean
bgColor?: string
max?: number
isDot?: boolean
hidden?: boolean
type?: BadgeType
top?: number
right?: number
customClass?: string
customStyle?: string
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customStyle: '',
modelValue: null,
showZero: false
})
const content = ref<number | string | null>(null)
watch(
[() => props.modelValue, () => props.max, () => props.isDot],
() => {
notice()
},
{ immediate: true, deep: true }
)
const contentStyle = computed(() => {
return `background-color: ${props.bgColor};top:${props.top || 0}px;right:${props.right || 0}px`
})
//
const isBadgeShow = computed(() => {
let isBadgeShow: boolean = false
if (!props.hidden && (content.value || (content.value === 0 && props.showZero) || props.isDot)) {
isBadgeShow = true
}
return isBadgeShow
})
function notice() {
if (props.isDot) return
let value = props.modelValue
const max = props.max
if (value && max && typeof value === 'number' && !Number.isNaN(value) && !Number.isNaN(max)) {
value = max < value ? `${max}+` : value
}
content.value = value
}
</script>
<script></script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,521 @@
@import './../common/abstracts/_mixin.scss';
@import './../common/abstracts/variable.scss';
@mixin button-type-style($color, $normal, $active, $disabled, $disabledcolor) {
background: $normal;
color: $color;
font-weight: $-fw-medium;
&::after {
border-color: $normal;
}
&.wd-button--active {
background: $active;
}
@include when(disabled) {
&.wd-button--active {
background: $disabled;
color: $disabledcolor;
}
background: $disabled;
color: $disabledcolor;
&::after {
border-color: $disabled;
}
}
@include when(loading) {
&,
&.wd-button--active {
color: $color;
background: $normal;
}
&::after {
border-color: $normal;
}
}
@include when(suck) {
border-radius: 0;
}
}
@mixin button-plain-style($color, $normal, $active, $disabled) {
color: $color;
background: transparent;
&::after {
border-color: $normal;
}
&.wd-button--active {
color: $active;
background: transparent;
&::after {
border-color: $active;
}
}
@include when(disabled) {
color: $disabled;
background: transparent;
&::after {
border-color: $disabled;
}
&.wd-button--active {
background: transparent;
&::after {
border-color: $disabled;
}
}
}
@include when(loading) {
&,
&.wd-button--active {
color: $color;
background: transparent;
&::after {
border-color: $normal;
}
}
}
}
.wot-theme-dark {
@include b(button) {
@include when(info) {
@include button-type-style($-dark-color,
$-dark-background4,
$-dark-background5,
$-dark-background7,
$-dark-color3);
}
@include when(plain) {
@include when(info) {
@include button-plain-style($-dark-color, $-dark-background5, $-dark-color, $-dark-color-gray);
}
@include when(primary) {
&.wd-button--active {
background: transparent;
color: themeColor($-color-theme, "light", #9DB9F6);
}
@include when(disabled) {
background-color: transparent;
&.wd-button--active {
background-color: transparent;
}
}
}
}
@include when(text) {
@include when(disabled) {
color: $-dark-color-gray;
background: transparent;
}
}
@include when(icon) {
color: $-dark-color;
&.wd-button--active {
background: $-dark-background4;
}
@include when(disabled) {
color: $-dark-color-gray;
background: transparent;
&.wd-button--active {
background: transparent;
}
}
}
}
}
@include b(button) {
position: relative;
display: inline-flex;
justify-content: center;
align-items: center;
outline: none;
-webkit-appearance: none;
outline: none;
background: transparent;
box-sizing: border-box;
border: none;
color: $-button-normal-color;
transition: all 0.2s;
user-select: none;
font-weight: normal;
&::after {
display: none;
}
&.wd-button--active {
color: $-button-normal-active-color;
background: $-button-normal-active-bg;
&::after {
border-color: $-button-normal-border-active-color;
}
}
@include when(disabled) {
color: $-button-normal-disabled-color;
background: $-button-normal-disabled-bg;
&::after {
border-color: $-button-normal-border-disabled-color;
}
&.wd-button--active {
color: $-button-normal-disabled-color;
background: $-button-normal-disabled-bg;
&::after {
border-color: $-button-normal-border-disabled-color;
}
}
}
@include e(loading) {
margin-right: 5px;
animation: wd-rotate 0.8s linear infinite;
animation-duration: 2s;
}
@include e(loading-svg) {
width: 100%;
height: 100%;
background-size: cover;
background-repeat: no-repeat;
}
@include when(loading) {
&.wd-button--active {
color: $-button-normal-color;
background: transparent;
&::after {
border-color: $-button-border-color;
}
}
}
@include when(primary) {
@include button-type-style(
$-color-white,
$-button-primary-bg-color,
$-button-primary-active-color,
$-button-primary-disabled-color,
$-color-white
);
}
@include when(success) {
@include button-type-style($-color-white, $-button-success-color, $-button-success-active-color, $-button-success-disabled-color, $-color-white);
}
@include when(info) {
@include button-type-style(
$-button-info-color,
$-button-info-bg-color,
$-button-info-active-bg-color,
$-button-info-disabled-bg-color,
$-button-info-disabled-color
);
}
@include when(warning) {
@include button-type-style($-color-white, $-button-warning-color, $-button-warning-active-color, $-button-warning-disabled-color, $-color-white);
}
@include when(error) {
@include button-type-style($-color-white, $-button-error-color, $-button-error-active-color, $-button-error-disabled-color, $-color-white);
}
@include when(small) {
height: $-button-small-height;
padding: $-button-small-padding;
border-radius: $-button-small-radius;
font-size: $-button-small-fs;
font-weight: normal;
@include when(round) {
border-radius: calc($-button-small-height / 2);
&::after {
border-radius: $-button-small-height;
}
}
.wd-button__loading {
width: $-button-small-loading;
height: $-button-small-loading;
}
}
@include when(medium) {
height: $-button-medium-height;
padding: $-button-medium-padding;
border-radius: $-button-medium-radius;
font-size: $-button-medium-fs;
&::after {
border-radius: calc($-button-medium-radius * 2);
}
@include when(primary) {
box-shadow: $-button-medium-box-shadow-size $-button-primary-box-shadow-color;
}
@include when(success) {
box-shadow: $-button-medium-box-shadow-size $-button-success-box-shadow-color;
}
@include when(warning) {
box-shadow: $-button-medium-box-shadow-size $-button-warning-box-shadow-color;
}
@include when(error) {
box-shadow: $-button-medium-box-shadow-size $-button-error-box-shadow-color;
}
@include when(plain) {
box-shadow: none;
}
@include when(round) {
min-width: 118px;
border-radius: calc($-button-medium-height / 2);
&::after {
border-radius: $-button-medium-height;
}
@include when(icon) {
min-width: 0;
border-radius: 50%;
}
@include when(text) {
min-width: 0;
border-radius: 0;
}
}
.wd-button__loading {
width: $-button-medium-loading;
height: $-button-medium-loading;
}
}
@include when(large) {
height: $-button-large-height;
padding: $-button-large-padding;
border-radius: $-button-large-radius;
font-size: $-button-large-fs;
&::after {
border-radius: calc($-button-large-radius * 2);
}
&:not(.is-plain) {
@include when(primary) {
box-shadow: $-button-large-box-shadow-size $-button-primary-box-shadow-color;
}
@include when(success) {
box-shadow: $-button-large-box-shadow-size $-button-success-box-shadow-color;
}
@include when(warning) {
box-shadow: $-button-large-box-shadow-size $-button-warning-box-shadow-color;
}
@include when(error) {
box-shadow: $-button-large-box-shadow-size $-button-error-box-shadow-color;
}
}
@include when(round) {
border-radius: calc($-button-large-height / 2);
&::after {
border-radius: $-button-large-height;
}
@include when(icon) {
border-radius: 50%;
}
@include when(text) {
border-radius: 0;
}
}
.wd-button__loading {
width: $-button-large-loading;
height: $-button-large-loading;
}
}
@include when(text) {
color: $-button-primary-color;
padding: 4px 0;
&::after {
display: none;
}
&.wd-button--active {
color: $-button-primary-active-color;
background: transparent;
}
@include when(disabled) {
color: $-button-normal-disabled-color;
background: transparent;
}
}
@include when(plain) {
background: $-color-white;
&::after {
position: absolute;
display: block;
content: '';
width: 200%;
height: 200%;
left: 0;
top: 0;
border: 1px solid $-button-border-color;
box-sizing: border-box;
transform: scale(0.5);
transform-origin: left top;
}
@include when(primary) {
@include button-plain-style($-button-primary-color, $-button-primary-color, $-button-primary-active-color, $-button-primary-disabled-color);
&.wd-button--active {
background-color: $-button-primary-plain-active-bg-color;
}
@include when(disabled) {
&.wd-button--active {
background-color: $-button-primary-plain-active-bg-color;
}
opacity: 1;
background-color: $-button-primary-plain-active-bg-color;
color: $-button-primary-plain-disabled-color;
}
}
@include when(success) {
@include button-plain-style($-button-success-color, $-button-success-color, $-button-success-active-color, $-button-success-disabled-color);
}
@include when(info) {
@include button-plain-style($-button-info-plain-normal-color, $-button-info-bg-color, $-button-info-active-color, $-button-info-disabled-color);
&::after {
border-color: $-button-info-plain-border-color;
}
&.wd-button--active {
background-color: $-button-info-plain-active-bg-color;
&::after {
border-color: $-button-info-plain-active-color;
}
}
@include when(disabled) {
&,
&.wd-button--active {
background-color: $-button-info-plain-disabled-bg-color;
&::after {
border-color: $-button-info-plain-disabled-bg-color;
}
}
}
@include when(loading) {
&::after,
&.wd-button--active::after {
border-color: $-button-info-plain-border-color;
}
}
}
@include when(warning) {
@include button-plain-style($-button-warning-color, $-button-warning-color, $-button-warning-active-color, $-button-warning-disabled-color);
}
@include when(error) {
@include button-plain-style($-button-error-color, $-button-error-color, $-button-error-active-color, $-button-error-disabled-color);
}
@include when(suck) {
&.wd-button--active {
background: $-button-suck-active-color;
}
@include when(disabled) {
background: $-color-white;
}
}
}
@include when(suck) {
display: flex;
font-size: $-button-large-fs;
height: $-button-suck-height;
border-radius: 0;
&::after {
display: none;
}
}
@include when(block) {
display: flex;
}
@include when(icon) {
width: $-button-icon-size;
height: $-button-icon-size;
padding: 0;
border-radius: 50%;
font-size: 0;
color: $-button-icon-color;
&::after {
display: none;
}
&.wd-button--active {
background: $-button-icon-active-color;
}
:deep(.wd-button__icon){
margin-right: 0;
}
@include when(disabled) {
color: $-button-icon-disabled-color;
background: transparent;
&.wd-button--active {
background: transparent;
}
}
}
@include edeep(icon) {
display: block;
margin-right: 6px;
font-size: $-button-icon-fs;
vertical-align: middle;
}
@include e(text) {
user-select: none;
white-space: nowrap;
}
}
// 2.8.0
.wd-button {
min-height: auto;
width: auto;
}
@keyframes wd-rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

@ -0,0 +1,190 @@
<template>
<button
hover-class="wd-button--active"
:style="customStyle"
:class="[
'wd-button',
'is-' + type,
'is-' + size,
plain ? 'is-plain' : '',
disabled ? 'is-disabled' : '',
round ? 'is-round' : '',
suck ? 'is-suck' : '',
block ? 'is-block' : '',
loading ? 'is-loading' : '',
customClass
]"
:hover-start-time="hoverStartTime"
:hover-stay-time="hoverStayTime"
:open-type="openType"
:send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath"
:send-message-img="sendMessageImg"
:app-parameter="appParameter"
:show-message-card="showMessageCard"
:session-from="sessionFrom"
:lang="lang"
:hover-stop-propagation="hoverStopPropagation"
:form-type="formType"
@click="handleClick"
@getuserinfo="handleGetuserinfo"
@contact="handleConcat"
@getphonenumber="handleGetphonenumber"
@error="handleError"
@launchapp="handleLaunchapp"
@opensetting="handleOpensetting"
>
<view v-if="loading" class="wd-button__loading">
<view class="wd-button__loading-svg" :style="loadingStyle"></view>
</view>
<wd-icon v-if="icon" custom-class="wd-button__icon" :name="icon"></wd-icon>
<view class="wd-button__text"><slot /></view>
</button>
</template>
<script lang="ts">
export default {
name: 'wd-button',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, watch } from 'vue'
import { ref } from 'vue'
import base64 from '../common/base64'
const loadingIcon = (color = '#4D80F0', reverse = true) => {
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 42"><defs><linearGradient x1="100%" y1="0%" x2="0%" y2="0%" id="a"><stop stop-color="${
reverse ? color : '#fff'
}" offset="0%" stop-opacity="0"/><stop stop-color="${
reverse ? color : '#fff'
}" offset="100%"/></linearGradient></defs><g fill="none" fill-rule="evenodd"><path d="M21 1c11.046 0 20 8.954 20 20s-8.954 20-20 20S1 32.046 1 21 9.954 1 21 1zm0 7C13.82 8 8 13.82 8 21s5.82 13 13 13 13-5.82 13-13S28.18 8 21 8z" fill="${
reverse ? '#fff' : color
}"/><path d="M4.599 21c0 9.044 7.332 16.376 16.376 16.376 9.045 0 16.376-7.332 16.376-16.376" stroke="url(#a)" stroke-width="3.5" stroke-linecap="round"/></g></svg>`
}
type ButtonType = 'primary' | 'success' | 'info' | 'warning' | 'error' | 'default' | 'text' | 'icon'
type ButtonSize = 'small' | 'medium' | 'large'
interface Props {
plain?: boolean
disabled?: boolean
round?: boolean
suck?: boolean
block?: boolean
type?: ButtonType
size?: ButtonSize
icon?: string
loading?: boolean
loadingColor?: string
openType?: string
formType?: string
hoverStopPropagation?: boolean
lang?: string
sessionFrom?: string
sendMessageTitle?: string
sendMessagePath?: string
sendMessageImg?: string
appParameter?: string
showMessageCard?: boolean
customClass?: string
customStyle?: string
}
const props = withDefaults(defineProps<Props>(), {
type: 'primary',
size: 'medium',
round: true,
plain: false,
loading: false,
suck: false,
block: false,
disabled: false,
customClass: '',
customStyle: ''
})
const hoverStartTime = ref<number>(20)
const hoverStayTime = ref<number>(70)
const loadingIconSvg = ref<string>('')
watch(
() => props.loading,
() => {
buildLoadingSvg()
},
{ deep: true, immediate: true }
)
const loadingStyle = computed(() => {
return `background-image: url(${loadingIconSvg.value});`
})
const emit = defineEmits(['click', 'getuserinfo', 'contact', 'getphonenumber', 'error', 'launchapp', 'opensetting'])
function handleClick(event) {
if (!props.disabled && !props.loading) {
emit('click', event.detail)
}
}
function handleGetuserinfo(event) {
emit('getuserinfo', event.detail)
}
function handleConcat(event) {
emit('contact', event.detail)
}
function handleGetphonenumber(event) {
emit('getphonenumber', event.detail)
}
function handleError(event) {
emit('error', event.detail)
}
function handleLaunchapp(event) {
emit('launchapp', event.detail)
}
function handleOpensetting(event) {
emit('opensetting', event.detail)
}
function buildLoadingSvg() {
const { loadingColor, type, plain } = props
let color = loadingColor
if (!color) {
switch (type) {
case 'primary':
color = '#4D80F0'
break
case 'success':
color = '#34d19d'
break
case 'info':
color = '#333'
break
case 'warning':
color = '#f0883a'
break
case 'error':
color = '#fa4350'
break
case 'default':
color = '#333'
break
}
}
const svg = loadingIcon(color, !plain)
loadingIconSvg.value = `"data:image/svg+xml;base64,${base64(svg)}"`
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,9 @@
/*
* @Author: weisheng
* @Date: 2023-06-12 10:04:19
* @LastEditTime: 2023-07-15 16:16:34
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-calendar-view\index.scss
*
*/

@ -0,0 +1,151 @@
@import '../../common/abstracts/variable';
@import '../../common/abstracts/mixin';
.wot-theme-dark {
@include b(month) {
@include e(title) {
color: $-dark-color;
}
@include e(days) {
color: $-dark-color;
}
@include e(day) {
@include when(disabled) {
.wd-month__day-text {
color: $-dark-color-gray;
}
}
}
}
}
@include b(month) {
@include e(title) {
display: flex;
align-items: center;
justify-content: center;
height: 45px;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
}
@include e(days) {
display: flex;
flex-wrap: wrap;
font-size: $-calendar-day-fs;
color: $-calendar-day-color;
}
@include e(day) {
position: relative;
width: 14.285%;
height: $-calendar-day-height;
line-height: $-calendar-day-height;
text-align: center;
@include when(disabled) {
.wd-month__day-text {
color: $-calendar-disabled-color;
}
}
@include when(current) {
color: $-calendar-active-color;
}
@include when(selected) {
.wd-month__day-container {
border-radius: $-calendar-active-border;
background: $-calendar-active-color;
color: #fff;
}
}
@include when(middle) {
.wd-month__day-container {
background: $-calendar-range-color;
}
}
@include when(start) {
&::after {
position: absolute;
content: '';
height: $-calendar-day-height;
top: 0;
right: 0;
left: 50%;
background: $-calendar-range-color;
z-index: 1;
}
&.is-without-end::after {
display: none;
}
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
}
}
@include when(end) {
&::after {
position: absolute;
content: '';
height: $-calendar-day-height;
top: 0;
left: 0;
right: 50%;
background: $-calendar-range-color;
z-index: 1;
}
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
}
}
@include when(same) {
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
}
}
}
@include e(day-container) {
position: relative;
z-index: 2;
}
@include e(day-text) {
font-weight: $-calendar-day-fw;
}
@include e(day-top) {
position: absolute;
top: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
@include e(day-bottom) {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
}

@ -0,0 +1,378 @@
<template>
<view>
<wd-toast selector="wd-month" />
<view class="month">
<view class="wd-month">
<view class="wd-month__title">{{ monthTitle(date) }}</view>
<view class="wd-month__days">
<view
v-for="(item, index) in days"
:key="index"
:class="`wd-month__day ${item.disabled ? 'is-disabled' : ''} ${item.type ? itemClass(item.type, value, type) : ''}`"
:style="firstDayStyle(index, item.date, firstDayOfWeek)"
@click="handleDateClick(index)"
>
<view class="wd-month__day-container">
<view class="wd-month__day-top">{{ item.topInfo }}</view>
<view class="wd-month__day-text">
{{ item.text }}
</view>
<view class="wd-month__day-bottom">{{ item.bottomInfo }}</view>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script lang="ts">
export default {
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import {
compareDate,
formatMonthTitle,
getDateByDefaultTime,
getDayByOffset,
getDayOffset,
getFirstDayStyle,
getItemClass,
getMonthEndDay,
getWeekRange
} from '../utils'
import { useToast } from '../../wd-toast'
import { deepClone, getType, isArray } from '../../common/util'
interface Props {
type: string
date: number
value: null | number | Array<number>
minDate: number
maxDate: number
firstDayOfWeek: number
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
maxRange?: number
rangePrompt?: string
allowSameDay?: boolean
defaultTime: Array<number>
}
const props = withDefaults(defineProps<Props>(), {
allowSameDay: false
})
const days = ref<Array<Record<string, any>>>([])
const itemClass = computed(() => {
return (monthType, value, type) => {
return getItemClass(monthType, value, type)
}
})
const monthTitle = computed(() => {
return (date) => {
return formatMonthTitle(date)
}
})
const firstDayStyle = computed(() => {
return (index: number, date: number, firstDayOfWeek: number) => {
return getFirstDayStyle(index, date, firstDayOfWeek)
}
})
watch(
[() => props.type, () => props.date, () => props.value, () => props.minDate, () => props.maxDate, () => props.formatter],
() => {
setDays()
},
{
deep: true,
immediate: true
}
)
const toast = useToast('wd-month')
const emit = defineEmits(['change'])
function setDays() {
const dayList: Array<Record<string, any>> = []
const date = new Date(props.date)
const year = date.getFullYear()
const month = date.getMonth()
const totalDay = getMonthEndDay(year, month + 1)
let value = props.value
if ((props.type === 'week' || props.type === 'weekrange') && value) {
value = getWeekValue()
}
for (let day = 1; day <= totalDay; day++) {
const date = new Date(year, month, day).getTime()
let type = getDayType(date, value)
if (!type && compareDate(date, Date.now()) === 0) {
type = 'current'
}
const dayObj = getFormatterDate(date, day, type)
dayList.push(dayObj)
}
days.value = dayList
}
function getDayType(date, value) {
switch (props.type) {
case 'date':
case 'datetime':
return getDateType(date)
case 'dates':
return getDatesType(date)
case 'daterange':
case 'datetimerange':
return getDatetimeType(date, value)
case 'week':
return getWeektimeType(date, value)
case 'weekrange':
return getWeektimeType(date, value)
default:
return getDateType(date)
}
}
function getDateType(date) {
if (props.value && compareDate(date, props.value) === 0) {
return 'selected'
}
return ''
}
function getDatesType(date) {
if (!props.value) return ''
let type = ''
;(props.value as any).some((item) => {
if (compareDate(date, item) === 0) {
type = 'selected'
return true
}
return false
})
return type
}
function getDatetimeType(date, value) {
const [startDate, endDate] = value || []
if (startDate && compareDate(date, startDate) === 0) {
if (props.allowSameDay && endDate && compareDate(startDate, endDate) === 0) {
return 'same'
}
return 'start'
} else if (endDate && compareDate(date, endDate) === 0) {
return 'end'
} else if (startDate && endDate && compareDate(date, startDate) === 1 && compareDate(date, endDate) === -1) {
return 'middle'
} else {
return ''
}
}
function getWeektimeType(date, value) {
const [startDate, endDate] = value || []
if (startDate && compareDate(date, startDate) === 0) {
return 'start'
} else if (endDate && compareDate(date, endDate) === 0) {
return 'end'
} else if (startDate && endDate && compareDate(date, startDate) === 1 && compareDate(date, endDate) === -1) {
return 'middle'
} else {
return ''
}
}
function getWeekValue() {
if (props.type === 'week') {
return getWeekRange(props.value, props.firstDayOfWeek)
} else {
const [startDate, endDate] = (props.value as any) || []
if (startDate) {
const firstWeekRange = getWeekRange(startDate, props.firstDayOfWeek)
if (endDate) {
const endWeekRange = getWeekRange(endDate, props.firstDayOfWeek)
return [firstWeekRange[0], endWeekRange[1]]
} else {
return firstWeekRange
}
}
return []
}
}
function handleDateClick(index: number) {
const date = days.value[index]
switch (props.type) {
case 'date':
case 'datetime':
handleDateChange(date)
break
case 'dates':
handleDatesChange(date)
break
case 'daterange':
case 'datetimerange':
handleDateRangeChange(date)
break
case 'week':
handleWeekChange(date)
break
case 'weekrange':
handleWeekRangeChange(date)
break
default:
handleDateChange(date)
}
}
function getDate(date, isEnd: boolean = false) {
date = props.defaultTime && props.defaultTime.length > 0 ? getDateByDefaultTime(date, isEnd ? props.defaultTime[1] : props.defaultTime[0]) : date
if (date < props.minDate) return props.minDate
if (date > props.maxDate) return props.maxDate
return date
}
function handleDateChange(date) {
if (date.disabled) return
if (date.type !== 'selected') {
emit('change', {
value: getDate(date.date),
type: 'start'
})
}
}
function handleDatesChange(date) {
if (date.disabled) return
const value = deepClone(isArray(props.value) ? props.value : [])
if (date.type !== 'selected') {
value.push(getDate(date.date))
} else {
value.splice(value.indexOf(date.date), 1)
}
emit('change', {
value
})
}
function handleDateRangeChange(date) {
if (date.disabled) return
let value
let type
const [startDate, endDate] = deepClone(isArray(props.value) ? props.value : [])
const compare = compareDate(date.date, startDate)
//
if (!props.allowSameDay && compare === 0 && (props.type === 'daterange' || props.type === 'datetimerange') && !endDate) {
return
}
if (startDate && !endDate && compare > -1) {
//
if (props.maxRange && getDayOffset(date.date, startDate) > props.maxRange) {
const maxEndDate = getDayByOffset(startDate, props.maxRange - 1)
value = [startDate, getDate(maxEndDate, true)]
toast.show({
msg: props.rangePrompt || `选择天数不能超过${props.maxRange}`
})
} else {
value = [startDate, getDate(date.date, true)]
}
} else if (props.type === 'datetimerange' && startDate && endDate) {
//
if (compare === 0) {
type = 'start'
value = props.value
} else if (compareDate(date.date, endDate) === 0) {
type = 'end'
value = props.value
} else {
value = [getDate(date.date), null]
}
} else {
value = [getDate(date.date), null]
}
emit('change', {
value,
type: type || (value[1] ? 'end' : 'start')
})
}
function handleWeekChange(date) {
const [weekStart] = getWeekRange(date.date, props.firstDayOfWeek)
//
if (getFormatterDate(weekStart, new Date(weekStart).getDate()).disabled) return
emit('change', {
value: getDate(weekStart) + 24 * 60 * 60 * 1000
})
}
function handleWeekRangeChange(date) {
const [weekStartDate] = getWeekRange(date.date, props.firstDayOfWeek)
//
if (getFormatterDate(weekStartDate, new Date(weekStartDate).getDate()).disabled) return
let value
const [startDate, endDate] = deepClone(isArray(props.value) ? props.value : [])
const [startWeekStartDate] = startDate ? getWeekRange(startDate, props.firstDayOfWeek) : []
const compare = compareDate(weekStartDate, startWeekStartDate)
if (startDate && !endDate && compare > -1) {
if (!props.allowSameDay && compare === 0) return
value = [getDate(startWeekStartDate) + 24 * 60 * 60 * 1000, getDate(weekStartDate) + 24 * 60 * 60 * 1000]
} else {
value = [getDate(weekStartDate) + 24 * 60 * 60 * 1000, null]
}
emit('change', {
value
})
}
function getFormatterDate(date, day, type?: string) {
let dayObj = {
date: date,
text: day,
topInfo: '',
bottomInfo: '',
type,
disabled: compareDate(date, props.minDate) === -1 || compareDate(date, props.maxDate) === 1
}
if (props.formatter) {
if (getType(props.formatter) === 'function') {
dayObj = props.formatter(dayObj)
} else {
console.error('[wot-design] error(wd-calendar-view): the formatter prop of wd-calendar-view should be a function')
}
}
return dayObj
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,89 @@
@import '../../common/abstracts/variable';
@import '../../common/abstracts/mixin';
.wot-theme-dark {
@include b(month-panel) {
@include e(title) {
color: $-dark-color;
}
@include e(weeks) {
box-shadow: 0px 4px 8px 0 rgba(255, 255, 255, 0.02);
color: $-dark-color;
}
@include e(time-label) {
color: $-dark-color;
&::after{
background: $-dark-background4;
}
}
}
}
@include b(month-panel) {
font-size: $-calendar-fs;
@include e(title) {
padding: 5px 0;
text-align: center;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
padding: $-calendar-panel-padding;
}
@include e(weeks) {
display: flex;
height: $-calendar-week-height;
line-height: $-calendar-week-height;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.02);
color: $-calendar-week-color;
font-size: $-calendar-week-fs;
padding: $-calendar-panel-padding;
}
@include e(week) {
flex: 1;
text-align: center;
}
@include e(container) {
padding: $-calendar-panel-padding;
box-sizing: border-box;
}
@include e(time) {
display: flex;
box-shadow: 0px -4px 8px 0px rgba(0, 0, 0, 0.02);
}
@include e(time-label) {
position: relative;
flex: 1;
font-size: $-picker-column-fs;
text-align: center;
line-height: 125px;
color: $-picker-column-color;
&::after {
position: absolute;
content: '';
height: 35px;
top: 50%;
left: 0;
right: 0;
transform: translateY(-50%);
background: $-picker-column-select-bg;
z-index: 0;
}
}
@include e(time-text) {
position: relative;
z-index: 1;
}
@include e(time-picker) {
flex: 3;
}
}

@ -0,0 +1,353 @@
<template>
<view class="wd-month-panel">
<view v-if="showPanelTitle" class="wd-month-panel__title">
{{ title }}
</view>
<view class="wd-month-panel__weeks">
<view v-for="item in 7" :key="item" class="wd-month-panel__week">{{ weekLabel(item + firstDayOfWeek) }}</view>
</view>
<scroll-view
:class="`wd-month-panel__container ${!!timeType ? 'wd-month-panel__container--time' : ''}`"
:style="`height: ${scrollHeight}px`"
scroll-y
@scroll="monthScroll"
:scroll-top="scrollTop"
>
<view v-for="(item, index) in months(minDate, maxDate)" :key="index" :id="`month${index}`">
<month
:type="type"
:date="item.date"
:value="value"
:min-date="minDate"
:max-date="maxDate"
:first-day-of-week="firstDayOfWeek"
:formatter="formatter"
:max-range="maxRange"
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:default-time="defaultTime"
@change="handleDateChange"
/>
</view>
</scroll-view>
<view v-if="timeType" class="wd-month-panel__time">
<view v-if="type === 'datetimerange'" class="wd-month-panel__time-label">
<view class="wd-month-panel__time-text">{{ timeType === 'start' ? '开始' : '结束' }}</view>
</view>
<view class="wd-month-panel__time-picker">
<wd-picker-view
v-if="timeData.length"
v-model="timeValue"
:columns="timeData"
:columns-height="125"
@change="handleTimeChange"
@pickstart="handlePickStart"
@pickend="handlePickEnd"
/>
</view>
</view>
</view>
</template>
<script lang="ts">
export default {
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, nextTick, onMounted, ref, watch } from 'vue'
import { debounce, getType, isEqual } from '../../common/util'
import { compareMonth, formatMonthTitle, getMonthEndDay, getMonths, getTimeData, getWeekLabel } from '../utils'
import Month from '../month/month.vue'
import type { MonthInfo } from './type'
interface Props {
type: string
value: Array<number> | number | null
minDate: number
maxDate: number
firstDayOfWeek: number
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
maxRange?: number
rangePrompt?: string
allowSameDay?: boolean
showPanelTitle?: boolean
defaultTime: Array<number>
panelHeight: number
// eslint-disable-next-line @typescript-eslint/ban-types
timeFilter?: Function
hideSecond?: boolean
}
const props = withDefaults(defineProps<Props>(), {
allowSameDay: false,
showPanelTitle: false,
hideSecond: false
})
const title = ref<string>('')
const scrollTop = ref<number>(0) //
const timeValue = ref<Array<string>>([])
const timeData = ref<Array<string | string[]>>([])
const timeType = ref<string>('') //
const innerValue = ref<string | number[]>('') //
const weekLabel = computed(() => {
return (index: number) => {
return getWeekLabel(index - 1)
}
})
//
const scrollHeight = computed(() => {
const scrollHeight: number = timeType.value ? (props.panelHeight || 378) - 125 : props.panelHeight || 378
return scrollHeight
})
//
const months = computed(() => {
return (minDate: number, maxDate: number): MonthInfo[] => {
let months = getMonths(minDate, maxDate).map((month) => {
const offset = (7 + new Date(month).getDay() - props.firstDayOfWeek) % 7
const totalDay = getMonthEndDay(new Date(month).getFullYear(), new Date(month).getMonth() + 1)
return {
height: (offset + totalDay > 35 ? 64 * 6 : 64 * 5) + 45,
date: month
}
})
return months
}
})
watch(
() => props.type,
(val) => {
if (
(val === 'datetime' && props.value) ||
(val === 'datetimerange' && typeof props.value === 'object' && props.value && props.value.length > 0 && props.value[0])
) {
setTime(props.value, 'start')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.value,
(val) => {
if (isEqual(val, innerValue.value)) return
if ((props.type === 'datetime' && val) || (props.type === 'datetimerange' && val && typeof val === 'object' && val.length > 0 && val[0])) {
setTime(val, 'start')
}
},
{
deep: true,
immediate: true
}
)
onMounted(() => {
scrollIntoView()
})
const emit = defineEmits(['change', 'pickstart', 'pickend'])
const handleChange = debounce((value) => {
emit('change', {
value
})
}, 50)
function scrollIntoView() {
setTimeout(() => {
let activeDate
const type = getType(props.value)
if (type === 'array') {
activeDate = props.value![0]
} else if (type === 'number') {
activeDate = props.value
}
if (!activeDate) {
activeDate = Date.now()
}
const monthsInfo = months.value(props.minDate, props.maxDate)
let top: number = 0
for (let index = 0; index < monthsInfo.length; index++) {
if (compareMonth(monthsInfo[index].date, activeDate) === 0) {
break
}
top += monthsInfo[index] ? Number(monthsInfo[index].height) : 0
}
scrollTop.value = 0
nextTick(() => {
scrollTop.value = top
})
}, 50)
}
/**
* 获取时间 picker 的数据
* @param {timestamp|array} value 当前时间
* @param {string} type 类型是开始还是结束
*/
function getTime(value, type?: string) {
if (!props.value) {
return []
}
if (props.type === 'datetime') {
return getTimeData({
date: value,
minDate: props.minDate,
maxDate: props.maxDate,
filter: props.timeFilter,
isHideSecond: props.hideSecond
})
} else {
if (type === 'start' && typeof props.value === 'object') {
return getTimeData({
date: value[0],
minDate: props.minDate,
maxDate: props.value[1] ? props.value[1] : props.maxDate,
filter: props.timeFilter,
isHideSecond: props.hideSecond
})
} else {
return getTimeData({
date: value[1],
minDate: value[0],
maxDate: props.maxDate,
filter: props.timeFilter,
isHideSecond: props.hideSecond
})
}
}
}
/**
* 获取 date 的时分秒
* @param {timestamp} date 时间
* @param {string} type 类型是开始还是结束
*/
function getTimeValue(date, type) {
if (props.type === 'datetime') {
date = new Date(date)
} else {
if (type === 'start') {
date = new Date(date[0])
} else {
date = new Date(date[1])
}
}
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return props.hideSecond ? [hour, minute] : [hour, minute, second]
}
function setTime(value, type) {
if (getType(value) === 'array' && value[0] && value[1] && type === 'start' && timeType.value === 'start') {
type = 'end'
}
timeData.value = getTime(value, type) || []
timeValue.value = getTimeValue(value, type)
timeType.value = type
}
function handleDateChange({ value, type }) {
if (!isEqual(value, props.value)) {
//
innerValue.value = value
handleChange(value)
}
// datetime datetimerange timeData
if (props.type.indexOf('time') > -1) {
setTime(value, type)
}
}
function handleTimeChange({ value }) {
if (!props.value) {
return
}
if (props.type === 'datetime' && typeof props.value === 'number') {
const date = new Date(props.value)
date.setHours(value[0])
date.setMinutes(value[1])
date.setSeconds(props.hideSecond ? 0 : value[2])
const dateTime = date.getTime()
timeData.value = getTime(dateTime) || []
timeValue.value = value
handleChange(dateTime)
} else if (typeof props.value === 'object') {
const [start, end] = props.value!
const dataValue = timeType.value === 'start' ? start : end
const date = new Date(dataValue)
date.setHours(value[0])
date.setMinutes(value[1])
date.setSeconds(props.hideSecond ? 0 : value[2])
const dateTime = date.getTime()
if (dateTime === dataValue) return
const finalValue = [start, end]
if (timeType.value === 'start') {
finalValue[0] = dateTime
} else {
finalValue[1] = dateTime
}
timeData.value = getTime(finalValue, timeType.value) || []
timeValue.value = value
innerValue.value = finalValue //
handleChange(finalValue)
}
}
function handlePickStart() {
emit('pickstart')
}
function handlePickEnd() {
emit('pickend')
}
const monthScroll = (event: { detail: { scrollTop: number } }) => {
const monthsInfo = months.value(props.minDate, props.maxDate)
if (monthsInfo.length <= 1) {
return
}
const scrollTop = Math.max(0, event.detail.scrollTop)
doSetSubtitle(scrollTop, monthsInfo)
}
/**
* 设置小标题
* scrollTop 滚动条位置
*/
function doSetSubtitle(scrollTop: number, monthsInfo: MonthInfo[]) {
let height: number = 0 //
for (let index = 0; index < monthsInfo.length; index++) {
height = height + monthsInfo[index].height
if (scrollTop < height + 45) {
title.value = formatMonthTitle(monthsInfo[index].date)
return
}
}
}
defineExpose({
scrollIntoView
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,7 @@
/**
*
*/
export interface MonthInfo {
date: number
height: number
}

@ -0,0 +1,424 @@
import { getType, padZero } from '../common/util'
const weeks: string[] = ['日', '一', '二', '三', '四', '五', '六']
/**
*
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function compareDate(date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
const month1 = date1.getMonth()
const month2 = date2.getMonth()
const day1 = date1.getDate()
const day2 = date2.getDate()
if (year1 === year2) {
if (month1 === month2) {
return day1 === day2 ? 0 : day1 > day2 ? 1 : -1
}
return month1 === month2 ? 0 : month1 > month2 ? 1 : -1
}
return year1 > year2 ? 1 : -1
}
/**
*
* @param {string} type
*/
export function isRange(type) {
return type.indexOf('range') > -1
}
/**
*
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function compareMonth(date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
const month1 = date1.getMonth()
const month2 = date2.getMonth()
if (year1 === year2) {
return month1 === month2 ? 0 : month1 > month2 ? 1 : -1
}
return year1 > year2 ? 1 : -1
}
/**
*
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function compareYear(date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
return year1 === year2 ? 0 : year1 > year2 ? 1 : -1
}
/**
*
* @param {number} year
* @param {number} month
*/
export function getMonthEndDay(year, month) {
return 32 - new Date(year, month - 1, 32).getDate()
}
/**
*
* @param {timestamp} date
*/
export function formatMonthTitle(date) {
date = new Date(date)
const year = date.getFullYear()
const month = date.getMonth() + 1
return year + '年' + month + '月'
}
/**
*
* @param {number} index
*/
export function getWeekLabel(index) {
if (index >= 7) {
index = index % 7
}
return weeks[index]
}
/**
*
* @param {number} index
* @param {timestamp} date
* @param {number} firstDayOfWeek
*/
export function getFirstDayStyle(index: number, date: number, firstDayOfWeek: number) {
if (firstDayOfWeek >= 7) {
firstDayOfWeek = firstDayOfWeek % 7
}
if (index !== 0) return ''
const offset = (7 + new Date(date).getDay() - firstDayOfWeek) % 7
return 'margin-left: ' + (100 / 7) * offset + '%'
}
/**
*
* @param {timestamp} date
*/
export function formatYearTitle(date: number) {
const year = new Date(date).getFullYear()
return year + '年'
}
/**
*
* @param {timestamp} minDate
* @param {timestamp} maxDate
*/
export function getMonths(minDate, maxDate) {
const months: number[] = []
const month = new Date(minDate)
month.setDate(1)
while (compareMonth(month, maxDate) < 1) {
months.push(month.getTime())
month.setMonth(month.getMonth() + 1)
}
return months
}
/**
*
* @param {timestamp} minDate
* @param {timestamp} maxDate
*/
export function getYears(minDate: number, maxDate: number) {
const years: number[] = []
const year = new Date(minDate)
year.setMonth(0)
year.setDate(1)
while (compareYear(year, maxDate) < 1) {
years.push(year.getTime())
year.setFullYear(year.getFullYear() + 1)
}
return years
}
/**
*
* @param {timestamp} date
*/
export function getWeekRange(date, firstDayOfWeek) {
if (firstDayOfWeek >= 7) {
firstDayOfWeek = firstDayOfWeek % 7
}
date = new Date(date)
date.setHours(0, 0, 0, 0)
const year = date.getFullYear()
const month = date.getMonth()
const day = date.getDate()
const week = date.getDay()
const weekStart = new Date(year, month, day - ((7 + week - firstDayOfWeek) % 7))
const weekEnd = new Date(year, month, day + 6 - ((7 + week - firstDayOfWeek) % 7))
return [weekStart.getTime(), weekEnd.getTime()]
}
/**
*
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function getDayOffset(date1, date2) {
return (date1 - date2) / (24 * 60 * 60 * 1000) + 1
}
/**
*
* @param {timestamp} date
* @param {number} offset
*/
export function getDayByOffset(date, offset) {
date = new Date(date)
date.setDate(date.getDate() + offset)
return date.getTime()
}
/**
*
* @param {timestamp} date1
* @param {timestamp} date2
*/
export function getMonthOffset(date1, date2) {
date1 = new Date(date1)
date2 = new Date(date2)
const year1 = date1.getFullYear()
const year2 = date2.getFullYear()
let month1 = date1.getMonth()
const month2 = date2.getMonth()
month1 = (year1 - year2) * 12 + month1
return month1 - month2 + 1
}
/**
*
* @param {timestamp} date
* @param {number} offset
*/
export function getMonthByOffset(date, offset) {
date = new Date(date)
date.setMonth(date.getMonth() + offset)
return date.getTime()
}
/**
*
* @param {array|string|null} defaultTime
*/
export function getDefaultTime(defaultTime) {
if (getType(defaultTime) === 'array') {
const startTime = (defaultTime[0] || '00:00:00').split(':').map((item) => {
return parseInt(item)
})
const endTime = (defaultTime[1] || '00:00:00').split(':').map((item) => {
return parseInt(item)
})
return [startTime, endTime]
} else {
const time = (defaultTime || '00:00:00').split(':').map((item) => {
return parseInt(item)
})
return [time, time]
}
}
/**
*
* @param {timestamp} date
* @param {array} defaultTime
*/
export function getDateByDefaultTime(date, defaultTime) {
date = new Date(date)
date.setHours(defaultTime[0])
date.setMinutes(defaultTime[1])
date.setSeconds(defaultTime[2])
return date.getTime()
}
/**
* iteratee n
* @param {number} n
* @param {function} iteratee
*/
const times = (n, iteratee) => {
let index = -1
const result = Array(n < 0 ? 0 : n)
while (++index < n) {
result[index] = iteratee(index)
}
return result
}
/**
*
* @param {timestamp}} date
*/
const getTime = (date) => {
date = new Date(date)
return [date.getHours(), date.getMinutes(), date.getSeconds()]
}
/**
* picker
* @param {*} param0
*/
export function getTimeData({ date, minDate, maxDate, isHideSecond, filter } = {} as any) {
const compareMin = compareDate(date, minDate)
const compareMax = compareDate(date, maxDate)
let minHour = 0
let maxHour = 23
let minMinute = 0
let maxMinute = 59
let minSecond = 0
let maxSecond = 59
if (compareMin === 0) {
const minTime = getTime(minDate)
const currentTime = getTime(date)
minHour = minTime[0]
if (minTime[0] === currentTime[0]) {
minMinute = minTime[1]
if (minTime[1] === currentTime[1]) {
minSecond = minTime[2]
}
}
}
if (compareMax === 0) {
const maxTime = getTime(maxDate)
const currentTime = getTime(date)
maxHour = maxTime[0]
if (maxTime[0] === currentTime[0]) {
maxMinute = maxTime[1]
if (maxTime[1] === currentTime[1]) {
maxSecond = maxTime[2]
}
}
}
let columns: any[] = []
let hours = times(24, (index) => {
return {
label: `${padZero(index)}`,
value: index,
disabled: index < minHour || index > maxHour
}
})
let minutes = times(60, (index) => {
return {
label: `${padZero(index)}`,
value: index,
disabled: index < minMinute || index > maxMinute
}
})
let seconds
if (filter && getType(filter) === 'function') {
hours = filter({
type: 'hour',
values: hours
})
minutes = filter({
type: 'minute',
values: minutes
})
}
if (!isHideSecond) {
seconds = times(60, (index) => {
return {
label: `${padZero(index)}`,
value: index,
disabled: index < minSecond || index > maxSecond
}
})
if (filter && getType(filter) === 'function') {
seconds = filter({
type: 'second',
values: seconds
})
}
}
columns = isHideSecond ? [hours, minutes] : [hours, minutes, seconds]
return columns
}
/**
*
* @param {timestamp} date
*/
export function getWeekNumber(date) {
date = new Date(date)
date.setHours(0, 0, 0, 0)
// Thursday in current week decides the year.
date.setDate(date.getDate() + 3 - ((date.getDay() + 6) % 7))
// January 4 is always in week 1.
const week = new Date(date.getFullYear(), 0, 4)
// Adjust to Thursday in week 1 and count number of weeks from date to week 1.
// Rounding should be fine for Daylight Saving Time. Its shift should never be more than 12 hours.
return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + ((week.getDay() + 6) % 7)) / 7)
}
export function getItemClass(monthType, value, type) {
const classList = ['is-' + monthType]
if (type.indexOf('range') > -1) {
if (!value || !value[1]) {
classList.push('is-without-end')
}
}
return classList.join(' ')
}

@ -0,0 +1,155 @@
<template>
<view :class="`wd-calendar-view ${customClass}`">
<year-panel
v-if="type === 'month' || type === 'monthrange'"
ref="yearPanelRef"
:type="type"
:value="modelValue"
:min-date="minDate"
:max-date="maxDate"
:formatter="formatter"
:max-range="maxRange"
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:show-panel-title="showPanelTitle"
:default-time="formatDefauleTime"
:panel-height="panelHeight"
@change="handleChange"
/>
<month-panel
v-else
ref="monthPanelRef"
:type="type"
:value="modelValue"
:min-date="minDate"
:max-date="maxDate"
:first-day-of-week="firstDayOfWeek"
:formatter="formatter"
:max-range="maxRange"
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:show-panel-title="showPanelTitle"
:default-time="formatDefauleTime"
:panel-height="panelHeight"
:time-filter="timeFilter"
:hide-second="hideSecond"
@change="handleChange"
@pickstart="handlePickStart"
@pickend="handlePickEnd"
/>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-calendar-view',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { getDefaultTime } from './utils'
import yearPanel from './yearPanel/year-panel.vue'
import MonthPanel from './monthPanel/month-panel.vue'
type CalendarType = 'date' | 'dates' | 'datetime' | 'week' | 'month' | 'daterange' | 'datetimerange' | 'weekrange' | 'monthrange'
interface Props {
customClass?: string
// 13
modelValue: number | Array<number> | null
//
type?: CalendarType
// 13
minDate?: number
// 13
maxDate?: number
//
firstDayOfWeek?: number
//
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
// type
maxRange?: number
// type
rangePrompt?: string
// type
allowSameDay?: boolean
//
showPanelTitle?: boolean
// 使
defaultTime?: string | Array<string>
//
panelHeight?: number
// type 'datetime' 'datetimerange'
// eslint-disable-next-line @typescript-eslint/ban-types
timeFilter?: Function
// type 'datetime' 'datetimerange'
hideSecond?: boolean
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
type: 'date',
minDate: new Date(new Date().getFullYear(), new Date().getMonth() - 6, new Date().getDate()).getTime(),
maxDate: new Date(new Date().getFullYear(), new Date().getMonth() + 6, new Date().getDate(), 23, 59, 59).getTime(),
firstDayOfWeek: 0,
allowSameDay: false,
showPanelTitle: true,
defaultTime: '00:00:00',
panelHeight: 378,
hideSecond: false
})
const formatDefauleTime = ref<number[]>([])
const yearPanelRef = ref()
const monthPanelRef = ref()
watch(
() => props.defaultTime,
(newValue) => {
formatDefauleTime.value = getDefaultTime(newValue)
},
{
deep: true,
immediate: true
}
)
const emit = defineEmits(['change', 'update:modelValue', 'pickstart', 'pickend'])
//
function scrollIntoView() {
const panel = getPanel()
panel.scrollIntoView && panel.scrollIntoView()
}
function getPanel() {
return props.type.indexOf('month') > -1 ? yearPanelRef.value : monthPanelRef.value
}
function handleChange({ value }) {
emit('update:modelValue', value)
emit('change', {
value
})
}
function handlePickStart() {
emit('pickstart')
}
function handlePickEnd() {
emit('pickend')
}
defineExpose({
scrollIntoView
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,149 @@
@import '../../common/abstracts/variable';
@import '../../common/abstracts/mixin';
.wot-theme-dark {
@include b(year) {
@include e(title) {
color: $-dark-color;
}
@include e(months) {
color: $-dark-color;
}
@include e(month) {
@include when(disabled) {
.wd-year__month-text {
color: $-dark-color-gray;
}
}
}
}
}
@include b(year) {
@include e(title) {
display: flex;
align-items: center;
justify-content: center;
height: 45px;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
}
@include e(months) {
display: flex;
flex-wrap: wrap;
font-size: $-calendar-day-fs;
color: $-calendar-day-color;
}
@include e(month) {
position: relative;
width: 25%;
height: $-calendar-day-height;
line-height: $-calendar-day-height;
text-align: center;
@include when(disabled) {
.wd-year__month-text {
color: $-calendar-disabled-color;
}
}
@include when(current) {
color: $-calendar-active-color;
}
@include when(selected) {
color: #fff;
.wd-year__month-text {
border-radius: $-calendar-active-border;
background: $-calendar-active-color;
}
}
@include when(middle) {
background: $-calendar-range-color;
}
@include when(start) {
color: #fff;
&::after {
position: absolute;
top: 0;
right: 0;
left: 50%;
bottom: 0;
content: '';
background: $-calendar-range-color;
}
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
&.is-without-end::after {
display: none;
}
}
@include when(end) {
color: #fff;
&::after {
position: absolute;
top: 0;
left: 0;
right: 50%;
bottom: 0;
content: '';
background: $-calendar-range-color;
}
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
}
@include when(same) {
color: #fff;
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
}
}
@include e(month-text) {
width: $-calendar-month-width;
margin: 0 auto;
text-align: center;
}
@include e(month-top) {
position: absolute;
top: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
@include e(month-bottom) {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
line-height: 1.1;
font-size: $-calendar-info-fs;
text-align: center;
}
}

@ -0,0 +1,204 @@
<template>
<wd-toast selector="wd-year" />
<view class="wd-year year">
<view class="wd-year__title">{{ yearTitle(date) }}</view>
<view class="wd-year__months">
<view
v-for="(item, index) in months"
:key="index"
:class="`wd-year__month ${item.disabled ? 'is-disabled' : ''} ${item.type ? itemClass(item.type, value, type) : ''}`"
@click="handleDateClick(index)"
>
<view class="wd-year__month-top">{{ item.topInfo }}</view>
<view class="wd-year__month-text">{{ item.text }}</view>
<view class="wd-year__month-bottom">{{ item.bottomInfo }}</view>
</view>
</view>
</view>
</template>
<script lang="ts">
export default {
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, ref, watch } from 'vue'
import { deepClone, getType } from '../../common/util'
import { compareMonth, formatYearTitle, getDateByDefaultTime, getItemClass, getMonthByOffset, getMonthOffset } from '../utils'
import { useToast } from '../../wd-toast'
interface Props {
type: string
date: number
value: null | number | Array<number>
minDate: number
maxDate: number
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
maxRange?: number
rangePrompt?: string
allowSameDay?: boolean
defaultTime: Array<number>
}
const props = withDefaults(defineProps<Props>(), {
allowSameDay: false
})
const toast = useToast('wd-year')
const months = ref<Record<string, any>[]>([])
const itemClass = computed(() => {
return (monthType, value, type) => {
return getItemClass(monthType, value, type)
}
})
const yearTitle = computed(() => {
return (date) => {
return formatYearTitle(date)
}
})
const emit = defineEmits(['change'])
watch(
[() => props.type, () => props.date, () => props.value, () => props.minDate, () => props.maxDate, () => props.formatter],
() => {
setMonths()
},
{
deep: true,
immediate: true
}
)
function setMonths() {
const monthList: Record<string, any>[] = []
const date = new Date(props.date)
const year = date.getFullYear()
const value = props.value
const valueType = getType(value)
if (props.type.indexOf('range') > -1 && value && valueType !== 'array') {
console.error('[wot-design] value should be array when type is range')
return
}
for (let month = 0; month < 12; month++) {
const date = new Date(year, month, 1).getTime()
let type = getMonthType(date)
if (!type && compareMonth(date, Date.now()) === 0) {
type = 'current'
}
const monthObj = getFormatterDate(date, month, type)
monthList.push(monthObj)
}
months.value = deepClone(monthList)
}
function getMonthType(date) {
if (props.type === 'monthrange' && typeof props.value === 'object') {
const [startDate, endDate] = props.value || []
if (startDate && compareMonth(date, startDate) === 0) {
if (endDate && compareMonth(startDate, endDate) === 0) {
return 'same'
}
return 'start'
} else if (endDate && compareMonth(date, endDate) === 0) {
return 'end'
} else if (startDate && endDate && compareMonth(date, startDate) === 1 && compareMonth(date, endDate) === -1) {
return 'middle'
} else {
return ''
}
} else {
if (props.value && compareMonth(date, props.value) === 0) {
return 'selected'
} else {
return ''
}
}
}
function handleDateClick(index) {
const date = months.value[index]
if (date.disabled) return
switch (props.type) {
case 'month':
handleMonthChange(date)
break
case 'monthrange':
handleMonthRangeChange(date)
break
default:
handleMonthChange(date)
}
}
function getDate(date) {
return props.defaultTime && props.defaultTime.length > 0 ? getDateByDefaultTime(date, props.defaultTime[0]) : date
}
function handleMonthChange(date) {
if (date.type !== 'selected') {
emit('change', {
value: getDate(date.date)
})
}
}
function handleMonthRangeChange(date) {
let value
const [startDate, endDate] = typeof props.value === 'object' ? props.value || [] : []
const compare = compareMonth(date.date, startDate)
//
if (!props.allowSameDay && !endDate && compare === 0) return
if (startDate && !endDate && compare > -1) {
if (props.maxRange && getMonthOffset(date.date, startDate) > props.maxRange) {
const maxEndDate = getMonthByOffset(startDate, props.maxRange - 1)
value = [startDate, getDate(maxEndDate)]
toast.show({
msg: props.rangePrompt || `选择月份不能超过${props.maxRange}个月`
})
} else {
value = [startDate, getDate(date.date)]
}
} else {
value = [getDate(date.date), null]
}
emit('change', {
value
})
}
function getFormatterDate(date, month, type) {
let monthObj = {
date: date,
text: month + 1,
topInfo: '',
bottomInfo: '',
type,
disabled: compareMonth(date, props.minDate) === -1 || compareMonth(date, props.maxDate) === 1
}
if (props.formatter) {
if (getType(props.formatter) === 'function') {
monthObj = props.formatter(monthObj)
} else {
console.error('[wot-design] error(wd-calendar-view): the formatter prop of wd-calendar-view should be a function')
}
}
return monthObj
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,24 @@
@import '../../common/abstracts/variable';
@import '../../common/abstracts/mixin';
.wot-theme-dark {
@include b(year-panel) {
@include e(title) {
color: $-dark-color;
box-shadow: 0px 4px 8px 0 rgba(255, 255,255, 0.02);
}
}
}
@include b(year-panel) {
font-size: $-calendar-fs;
padding: $-calendar-panel-padding;
@include e(title) {
padding: 5px 0;
text-align: center;
font-size: $-calendar-panel-title-fs;
color: $-calendar-panel-title-color;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.02);
}
}

@ -0,0 +1,7 @@
/**
*
*/
export interface YearInfo {
date: number
height: number
}

@ -0,0 +1,166 @@
<template>
<view class="wd-year-panel">
<view v-if="showPanelTitle" class="wd-year-panel__title">{{ title }}</view>
<scroll-view class="wd-year-panel__container" :style="`height: ${scrollHeight}px`" scroll-y @scroll="yearScroll" :scroll-top="scrollTop">
<view v-for="(item, index) in years(minDate, maxDate)" :key="index" :id="`year${index}`">
<year
:type="type"
:date="item.date"
:value="value"
:min-date="minDate"
:max-date="maxDate"
:max-range="maxRange"
:formatter="formatter"
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:default-time="defaultTime"
@change="handleDateChange"
/>
</view>
</scroll-view>
</view>
</template>
<script lang="ts">
export default {
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, onMounted, ref, nextTick } from 'vue'
import { compareYear, formatYearTitle, getYears } from '../utils'
import { getType } from '../../common/util'
import Year from '../year/year.vue'
import type { YearInfo } from './type'
interface Props {
type: string
value: Array<number> | number | null
minDate: number
maxDate: number
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
maxRange?: number
rangePrompt?: string
allowSameDay?: boolean
showPanelTitle?: boolean
defaultTime: Array<number>
panelHeight: number
}
const props = withDefaults(defineProps<Props>(), {
allowSameDay: false,
showPanelTitle: false
})
const title = ref<string>('')
const scrollTop = ref<number>(0) //
//
const scrollHeight = computed(() => {
const scrollHeight: number = (props.panelHeight || 378) + (props.showPanelTitle ? 26 : 16)
return scrollHeight
})
const years = computed(() => {
return (minDate: number, maxDate: number): YearInfo[] => {
let years = getYears(minDate, maxDate).map((year) => {
return {
date: year,
height: 237
}
})
return years
}
})
const emit = defineEmits(['change'])
onMounted(() => {
scrollIntoView()
})
const requestAnimationFrame = (cb = () => void 0) => {
return new Promise((resolve, reject) => {
uni
.createSelectorQuery()
.selectViewport()
.boundingClientRect()
.exec(() => {
resolve(true)
cb()
})
})
}
function scrollIntoView() {
requestAnimationFrame().then(() => {
let activeDate
const type = getType(props.value)
if (type === 'array') {
activeDate = props.value![0]
} else if (type === 'number') {
activeDate = props.value
}
if (!activeDate) {
activeDate = Date.now()
}
const yearsInfo = years.value(props.minDate, props.maxDate)
let top: number = 0
for (let index = 0; index < yearsInfo.length; index++) {
if (compareYear(yearsInfo[index].date, activeDate) === 0) {
break
}
top += yearsInfo[index] ? Number(yearsInfo[index].height) : 0
}
scrollTop.value = 0
nextTick(() => {
scrollTop.value = top
})
})
}
const yearScroll = (e: Event) => {
const yearsInfo = years.value(props.minDate, props.maxDate)
if (yearsInfo.length <= 1) {
return
}
const scrollTop = Math.max(0, (e.target as Element).scrollTop)
doSetSubtitle(scrollTop, yearsInfo)
}
/**
* 设置小标题
* scrollTop 滚动条位置
*/
function doSetSubtitle(scrollTop: number, yearsInfo: YearInfo[]) {
let height: number = 0 //
for (let index = 0; index < yearsInfo.length; index++) {
height = height + yearsInfo[index].height
if (scrollTop < height + 45) {
title.value = formatYearTitle(yearsInfo[index].date)
return
}
}
}
function handleDateChange({ value }) {
emit('change', {
value
})
}
defineExpose({
scrollIntoView
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,241 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(calendar) {
@include e(cell) {
background-color: $-dark-background2;
color: $-dark-color;
}
@include e(value) {
color: $-dark-color;
}
@include e(title) {
color: $-dark-color;
}
:deep(.wd-calendar__arrow),
:deep(.wd-calendar__close) {
color: $-dark-color;
}
@include when(border) {
.wd-calendar__cell {
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
}
}
@include e(range-label-item) {
color: $-dark-color;
@include when(placeholder) {
color: $-dark-color-gray;
}
}
@include e(range-sperator) {
color: $-dark-color-gray;
}
}
}
@include b(calendar) {
@include when(border) {
.wd-calendar__cell {
@include halfPixelBorder('top', $-cell-padding);
}
}
@include e(cell) {
position: relative;
display: flex;
padding: $-cell-wrapper-padding $-cell-padding;
align-items: flex-start;
background-color: $-color-white;
text-decoration: none;
color: $-cell-title-color;
font-size: $-cell-title-fs;
overflow: hidden;
line-height: $-cell-line-height;
}
@include e(cell) {
@include when(disabled) {
.wd-calendar__value {
color: $-input-disabled-color;
}
}
@include when(align-right) {
.wd-calendar__value {
text-align: right;
}
}
@include when(error) {
.wd-calendar__value {
color: $-input-error-color;
}
:deep(.wd-calendar__arrow) {
color: $-input-error-color;
}
}
@include when(large) {
font-size: $-cell-title-fs-large;
:deep(.wd-calendar__arrow) {
font-size: $-cell-icon-size-large;
}
}
@include when(center) {
align-items: center;
:deep(.wd-calendar__arrow) {
margin-top: 0;
}
}
}
@include e(error-message){
color: $-form-item-error-message-color;
font-size: $-form-item-error-message-font-size;
line-height: $-form-item-error-message-line-height;
text-align: left;
vertical-align: middle;
}
@include e(label) {
position: relative;
width: $-input-cell-label-width;
margin-right: $-cell-padding;
color: $-cell-title-color;
box-sizing: border-box;
@include when(required) {
padding-left: 12px;
&::after {
position: absolute;
left: 0;
top: 2px;
content: '*';
font-size: $-cell-required-size;
line-height: 1.1;
color: $-cell-required-color;
}
}
}
@include e(value-wraper) {
display: flex;
}
@include e(value) {
flex: 1;
margin-right: 10px;
color: $-cell-value-color;
@include when(ellipsis) {
@include lineEllipsis;
}
@include m(placeholder) {
color: $-input-placeholder-color;
}
}
@include e(body) {
flex: 1;
}
@include edeep(arrow) {
display: block;
font-size: $-cell-icon-size;
color: $-cell-arrow-color;
line-height: $-cell-line-height;
}
@include e(header) {
position: relative;
overflow: hidden;
}
@include e(title) {
color: $-action-sheet-color;
height: $-action-sheet-title-height;
line-height: $-action-sheet-title-height;
text-align: center;
font-size: $-action-sheet-title-fs;
font-weight: $-action-sheet-weight;
}
@include edeep(close) {
position: absolute;
top: $-action-sheet-close-top;
right: $-action-sheet-close-right;
color: $-action-sheet-close-color;
font-size: $-action-sheet-close-fs;
transform: rotate(-45deg);
line-height: 1.1;
}
@include e(tabs) {
width: 222px;
margin: 10px auto 12px;
}
@include e(shortcuts) {
padding: 20px 0;
text-align: center;
}
@include edeep(tag) {
margin-right: 8px;
}
@include e(view) {
@include when(show-confirm) {
height: 394px;
@include when(range) {
height: 384px;
}
}
}
@include e(range-label) {
display: flex;
justify-content: center;
align-items: center;
font-size: 14px;
@include when(monthrange) {
padding-bottom: 10px;
box-shadow: 0px 4px 8px 0 rgba(0, 0, 0, 0.02);
}
}
@include e(range-label-item) {
flex: 1;
color: rgba(0, 0, 0, 0.85);
@include when(placeholder) {
color: rgba(0, 0, 0, 0.25);
}
}
@include e(range-sperator) {
margin: 0 24px;
color: rgba(0, 0, 0, 0.25);
}
@include e(confirm) {
padding: 12px 25px 14px;
}
}

@ -0,0 +1,503 @@
<template>
<view :class="`wd-calendar ${cell.border.value ? 'is-border' : ''} ${customClass}`">
<view class="wd-calendar__field" @click="open">
<slot v-if="useDefaultSlot"></slot>
<view
v-else
:class="`wd-calendar__cell ${disabled ? 'is-disabled' : ''} ${readonly ? 'is-readonly' : ''} ${alignRight ? 'is-align-right' : ''} ${
error ? 'is-error' : ''
} ${size ? 'is-' + size : ''} ${center ? 'is-center' : ''}`"
>
<view
v-if="label || useLabelSlot"
:class="`wd-calendar__label ${isRequired ? 'is-required' : ''} ${customLabelClass}`"
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
>
<block v-if="label">{{ label }}</block>
<slot v-else name="label"></slot>
</view>
<view class="wd-calendar__body">
<view class="wd-calendar__value-wraper">
<view
:class="`wd-calendar__value ${ellipsis ? 'is-ellipsis' : ''} ${customValueClass} ${showValue ? '' : 'wd-calendar__value--placeholder'}`"
>
{{ showValue || placeholder || '请选择' }}
</view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-calendar__arrow" name="arrow-right" />
</view>
<view v-if="errorMessage" class="wd-calendar__error-message">{{ errorMessage }}</view>
</view>
</view>
</view>
<wd-action-sheet
v-model="pickerShow"
:duration="250"
:close-on-click-modal="closeOnClickModal"
:safe-area-inset-bottom="safeAreaInsetBottom"
:z-index="zIndex"
@close="close"
>
<view class="wd-calendar__header">
<view v-if="!showTypeSwitch && shortcuts.length === 0" class="wd-calendar__title">{{ title || '' }}</view>
<view v-if="showTypeSwitch" class="wd-calendar__tabs">
<wd-tabs ref="calendarTabs" v-model="currentTab" @change="handleTypeChange">
<wd-tab title="日" name="日" />
<wd-tab title="周" name="周" />
<wd-tab title="月" name="月" />
</wd-tabs>
</view>
<view v-if="shortcuts.length > 0" class="wd-calendar__shortcuts">
<wd-tag
v-for="(item, index) in shortcuts"
:key="index"
custom-class="wd-calendar__tag"
type="primary"
plain
round
@click="handleShortcutClick(index)"
>
{{ item.text }}
</wd-tag>
</view>
<wd-icon custom-class="wd-calendar__close" name="add" @click="close" />
</view>
<view
v-if="inited"
:class="`wd-calendar__view ${currentType.indexOf('range') > -1 ? 'is-range' : ''} ${showConfirm ? 'is-show-confirm' : ''}`"
>
<view v-if="range(type)" :class="`wd-calendar__range-label ${type === 'monthrange' ? 'is-monthrange' : ''}`">
<view :class="`wd-calendar__range-label-item ${!calendarValue || !calendarValue[0] ? 'is-placeholder' : ''}`" style="text-align: right">
{{ rangeLabel[0] }}
</view>
<view class="wd-calendar__range-sperator">/</view>
<view :class="`wd-calendar__range-label-item ${!calendarValue || !calendarValue[1] ? 'is-placeholder' : ''}`">
{{ rangeLabel[1] }}
</view>
</view>
<wd-calendar-view
ref="calendarView"
v-model="calendarValue"
:type="currentType"
:min-date="minDate"
:max-date="maxDate"
:first-day-of-week="firstDayOfWeek"
:formatter="formatter"
:panel-height="panelHeight"
:max-range="maxRange"
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:default-time="defaultTime"
:time-filter="timeFilter"
:hide-second="hideSecond"
:show-panel-title="!range(type)"
@change="handleChange"
/>
</view>
<view v-if="showConfirm" class="wd-calendar__confirm">
<wd-button block :disabled="confirmBtnDisabled" @click="handleConfirm">{{ confirmText || '' }}</wd-button>
</view>
</wd-action-sheet>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-calendar',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { ref, computed, watch } from 'vue'
import { dayjs } from '../common/dayjs'
import { deepClone, isArray, isEqual, padZero } from '../common/util'
import { getWeekNumber, isRange } from '../wd-calendar-view/utils'
import { useCell } from '../composables/useCell'
import { FORM_KEY, FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent'
const defaultDisplayFormat = (value, type) => {
switch (type) {
case 'date':
return dayjs(value).format('YYYY-MM-DD')
case 'dates':
return value
.map((item) => {
return dayjs(item).format('YYYY-MM-DD')
})
.join(', ')
case 'daterange':
return `${value[0] ? dayjs(value[0]).format('YYYY-MM-DD') : '开始时间'}${value[1] ? dayjs(value[1]).format('YYYY-MM-DD') : '结束时间'}`
case 'datetime':
return dayjs(value).format('YYYY-MM-DD HH:mm:ss')
case 'datetimerange':
return `${value[0] ? dayjs(value[0]).format('YY年MM月DD日 HH:mm:ss') : '开始时间'}\n${
value[1] ? dayjs(value[1]).format('YY年MM月DD日 HH:mm:ss') : '结束时间'
}`
case 'week': {
const year = new Date(value).getFullYear()
const week = getWeekNumber(value)
return `${year}${padZero(week)}`
}
case 'weekrange': {
const year1 = new Date(value[0]).getFullYear()
const week1 = getWeekNumber(value[0])
const year2 = new Date(value[1]).getFullYear()
const week2 = getWeekNumber(value[1])
return `${value[0] ? `${year1}${padZero(week1)}` : '开始周'} - ${value[1] ? `${year2}${padZero(week2)}` : '结束周'}`
}
case 'month':
return dayjs(value).format('YYYY / MM')
case 'monthrange':
return `${value[0] ? dayjs(value[0]).format('YYYY / MM') : '开始月'}${value[1] ? dayjs(value[1]).format('YYYY / MM') : '结束月'}`
}
}
const formatRange = (value, rangeType, type) => {
switch (type) {
case 'daterange':
if (!value) {
return rangeType === 'end' ? '结束时间' : '开始时间'
}
return dayjs(value).format('YYYY年MM月DD日')
case 'datetimerange':
if (!value) {
return rangeType === 'end' ? '结束时间' : '开始时间'
}
return dayjs(value).format('YY年MM月DD日 HH:mm:ss')
case 'weekrange': {
if (!value) {
return rangeType === 'end' ? '结束周' : '开始周'
}
const date = new Date(value)
const year = date.getFullYear()
const week = getWeekNumber(value)
return year + '年第' + week + '周'
}
case 'monthrange':
if (!value) {
return rangeType === 'end' ? '结束月' : '开始月'
}
return dayjs(value).format('YYYY年MM月')
}
}
type CalendarType = 'date' | 'dates' | 'datetime' | 'week' | 'month' | 'daterange' | 'datetimerange' | 'weekrange' | 'monthrange'
interface Props {
customClass?: string
customViewClass?: string
customLabelClass?: string
customValueClass?: string
modelValue: null | number | Array<number>
type?: CalendarType
minDate?: number
maxDate?: number
firstDayOfWeek?: number
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
maxRange?: number
rangePrompt?: string
allowSameDay?: boolean
defaultTime?: string | Array<string>
// eslint-disable-next-line @typescript-eslint/ban-types
timeFilter?: Function
hideSecond?: boolean
label?: string
labelWidth?: string
useLabelSlot?: boolean
useDefaultSlot?: boolean
disabled?: boolean
readonly?: boolean
placeholder?: string
title?: string
alignRight?: boolean
error?: boolean
required?: boolean
size?: string
center?: boolean
closeOnClickModal?: boolean
zIndex?: number
showConfirm?: boolean
confirmText?: string
// eslint-disable-next-line @typescript-eslint/ban-types
displayFormat?: Function
// eslint-disable-next-line @typescript-eslint/ban-types
innerDisplayFormat?: Function
ellipsis?: boolean
showTypeSwitch?: boolean
shortcuts?: Array<Record<string, any>>
// eslint-disable-next-line @typescript-eslint/ban-types
onShortcutsClick?: Function
safeAreaInsetBottom?: boolean
// eslint-disable-next-line @typescript-eslint/ban-types
beforeConfirm?: Function
prop?: string
rules?: FormItemRule[]
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customViewClass: '',
customLabelClass: '',
customValueClass: '',
type: 'date',
minDate: new Date(new Date().getFullYear(), new Date().getMonth() - 6, new Date().getDate()).getTime(),
maxDate: new Date(new Date().getFullYear(), new Date().getMonth() + 6, new Date().getDate(), 23, 59, 59).getTime(),
firstDayOfWeek: 0,
allowSameDay: false,
hideSecond: false,
useLabelSlot: false,
useDefaultSlot: false,
disabled: false,
readonly: false,
alignRight: false,
error: false,
required: false,
center: false,
closeOnClickModal: true,
zIndex: 15,
showConfirm: true,
ellipsis: false,
showTypeSwitch: false,
shortcuts: () => [],
safeAreaInsetBottom: true,
rules: () => []
})
const pickerShow = ref<boolean>(false)
const calendarValue = ref<null | number | number[]>(null)
const lastCalendarValue = ref<null | number | number[]>(null)
const panelHeight = ref<number>(338)
const confirmBtnDisabled = ref<boolean>(true)
const showValue = ref<string>('')
const currentTab = ref<number>(0)
const lastTab = ref<number>(0)
const currentType = ref<CalendarType>('date')
const lastCurrentType = ref<CalendarType>('date')
const inited = ref<boolean>(false)
const rangeLabel = ref<Array<string>>([])
const cell = useCell()
const calendarView = ref()
const calendarTabs = ref()
watch(
() => props.modelValue,
(val, oldVal) => {
if (isEqual(val, oldVal)) return
calendarValue.value = deepClone(val)
confirmBtnDisabled.value = getConfirmBtnStatus(val)
setShowValue()
if (props.type.indexOf('range') > -1) {
setInnerLabel()
}
},
{
immediate: true
}
)
watch(
() => props.type,
(newValue, oldValue) => {
if (props.showTypeSwitch) {
const tabs = ['date', 'week', 'month']
const rangeTabs = ['daterange', 'weekrange', 'monthrange']
const index = newValue.indexOf('range') > -1 ? rangeTabs.indexOf(newValue) || 0 : tabs.indexOf(newValue)
currentTab.value = index
}
panelHeight.value = props.showConfirm ? 338 : 400
currentType.value = deepClone(newValue)
setShowValue()
if (props.type.indexOf('range') > -1) {
setInnerLabel()
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.showConfirm,
(val) => {
panelHeight.value = val ? 338 : 400
},
{
deep: true,
immediate: true
}
)
const { parent: form } = useParent(FORM_KEY)
//
const errorMessage = computed(() => {
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
return form.errorMessages[props.prop]
} else {
return ''
}
})
//
const isRequired = computed(() => {
let formRequired = false
if (form && form.rules) {
const rules = form.rules
for (const key in rules) {
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
}
}
}
return props.required || props.rules.some((rule) => rule.required) || formRequired
})
const range = computed(() => {
return (type) => {
return isRange(type)
}
})
const emit = defineEmits(['cancel', 'change', 'update:modelValue', 'confirm'])
function scrollIntoView() {
calendarView.value && calendarView.value && calendarView.value.$.exposed.scrollIntoView()
}
//
function open() {
const { disabled, readonly } = props
if (disabled || readonly) return
inited.value = true
pickerShow.value = true
lastCalendarValue.value = deepClone(calendarValue.value)
lastTab.value = currentTab.value
lastCurrentType.value = currentType.value
scrollIntoView()
setTimeout(() => {
if (props.showTypeSwitch) {
calendarTabs.value.scrollIntoView()
calendarTabs.value.updateLineStyle(false)
}
}, 250)
}
//
function close() {
pickerShow.value = false
scrollIntoView()
setTimeout(() => {
calendarValue.value = deepClone(lastCalendarValue.value)
currentTab.value = lastTab.value
currentType.value = lastCurrentType.value
confirmBtnDisabled.value = getConfirmBtnStatus(lastCalendarValue.value)
}, 250)
emit('cancel')
}
function handleTypeChange({ index }) {
const tabs = ['date', 'week', 'month']
const rangeTabs = ['daterange', 'weekrange', 'monthrange']
const type = props.type.indexOf('range') > -1 ? rangeTabs[index] : tabs[index]
currentTab.value = index
currentType.value = type as CalendarType
}
function getConfirmBtnStatus(value) {
let confirmBtnDisabled = false
//
if (
(props.type.indexOf('range') > -1 && (!value[0] || !value[1] || !value)) ||
(props.type === 'dates' && (value.length === 0 || !value)) ||
!value
) {
confirmBtnDisabled = true
}
return confirmBtnDisabled
}
function handleChange({ value }) {
calendarValue.value = deepClone(value)
confirmBtnDisabled.value = getConfirmBtnStatus(value)
emit('change', {
value
})
if (props.type.indexOf('range') > -1) {
setInnerLabel()
}
if (!props.showConfirm && !confirmBtnDisabled.value) {
handleConfirm()
}
}
function handleConfirm() {
if (props.beforeConfirm) {
props.beforeConfirm({
value: calendarValue.value,
resolve: (isPass) => {
isPass && onConfirm()
}
})
} else {
onConfirm()
}
}
function onConfirm() {
pickerShow.value = false
emit('update:modelValue', calendarValue.value)
emit('confirm', {
value: calendarValue.value
})
setShowValue()
}
function setInnerLabel() {
const [start, end] = deepClone(isArray(calendarValue.value) ? calendarValue.value : [])
rangeLabel.value = [start, end].map((item, index) => {
return (props.innerDisplayFormat || formatRange)(item, index === 0 ? 'start' : 'end', currentType.value)
})
}
function setShowValue() {
if ((!(calendarValue.value instanceof Array) && calendarValue.value) || (calendarValue.value instanceof Array && calendarValue.value.length)) {
showValue.value = (props.displayFormat || defaultDisplayFormat)(calendarValue.value, currentType.value)
} else {
showValue.value = ''
}
}
function handleShortcutClick(index) {
if (props.onShortcutsClick && typeof props.onShortcutsClick === 'function') {
calendarValue.value = deepClone(
props.onShortcutsClick({
item: props.shortcuts[index],
index
})
)
confirmBtnDisabled.value = getConfirmBtnStatus(calendarValue.value)
if (props.type.indexOf('range') > -1) {
setInnerLabel()
}
}
if (!props.showConfirm) {
handleConfirm()
}
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,71 @@
@import "../common/abstracts/variable.scss";
@import "../common/abstracts/_mixin.scss";
.wot-theme-dark {
@include b(card) {
background-color: $-dark-background2;
@include when(rectangle) {
.wd-card__content {
@include halfPixelBorder('top', 0, $-dark-border-color);
}
.wd-card__footer {
@include halfPixelBorder('top', 0, $-dark-border-color);
}
}
@include e(title-content) {
color: $-dark-color;
}
@include e(content) {
color: $-dark-color3;
}
}
}
@include b(card) {
padding: $-card-padding;
background-color: $-card-bg;
line-height: $-card-line-height;
margin: $-card-margin;
border-radius: $-card-radius;
box-shadow: $-card-shadow-color;
font-size: $-card-fs;
margin-bottom: 12px;
@include when(rectangle) {
margin-left: 0;
margin-right: 0;
border-radius: 0;
box-shadow: none;
.wd-card__title-content {
font-size: $-card-fs;
}
.wd-card__content {
position: relative;
padding: $-card-rectangle-content-padding;
@include halfPixelBorder('top', 0, $-card-content-border-color);
}
.wd-card__footer {
position: relative;
padding: $-card-rectangle-footer-padding;
@include halfPixelBorder('top', 0, $-card-content-border-color);
}
}
@include e(title-content) {
padding: 16px 0;
color: $-card-title-color;
font-size: $-card-title-fs;
}
@include e(content) {
color: $-card-content-color;
line-height: $-card-content-line-height;
}
@include e(footer) {
padding: $-card-footer-padding;
text-align: right;
}
}

@ -0,0 +1,50 @@
<template>
<view :class="['wd-card', type == 'rectangle' ? 'is-rectangle' : '', customClass]">
<view :class="['wd-card__title-content', customTitleClass]">
<view class="wd-card__title">
<text v-if="title">{{ title }}</text>
<slot v-else name="title"></slot>
</view>
</view>
<view :class="`wd-card__content ${customContentClass}`">
<slot></slot>
</view>
<view :class="`wd-card__footer ${customFooterClass}`">
<slot name="footer"></slot>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-card',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
interface Props {
title?: string
type?: string
customClass?: string
customTitleClass?: string
customContentClass?: string
customFooterClass?: string
}
const props = withDefaults(defineProps<Props>(), {
type: '',
customClass: '',
customTitleClass: '',
customContentClass: '',
customFooterClass: ''
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,56 @@
@import '../common/abstracts/variable.scss';
@import '../common/abstracts/_mixin.scss';
.wot-theme-dark {
@include b(cell-group) {
background-color: $-dark-background2;
@include when(border) {
.wd-cell-group__title {
@include halfPixelBorder('bottom', 0, $-dark-border-color);
}
}
@include e(title) {
background: $-dark-background2;
color: $-dark-color;
}
@include e(right) {
color: $-dark-color3;
}
@include e(body) {
background: $-dark-background2;
}
}
}
@include b(cell-group) {
background-color: $-color-white;
@include when(border) {
.wd-cell-group__title {
@include halfPixelBorder;
}
}
@include e(title) {
position: relative;
display: flex;
justify-content: space-between;
padding: $-cell-group-padding;
background: $-color-white;
font-size: $-cell-group-title-fs;
color: $-cell-group-title-color;
font-weight: $-fw-medium;
line-height: 1.43;
}
@include e(right) {
color: $-cell-group-value-color;
font-size: $-cell-group-value-fs;
}
@include e(body) {
background: $-color-white;
}
}

@ -0,0 +1,56 @@
<template>
<view :class="['wd-cell-group', border ? 'is-border' : '', customClass]">
<view v-if="title || value || useSlot" class="wd-cell-group__title">
<!--左侧标题-->
<view class="wd-cell-group__left">
<text v-if="title">{{ title }}</text>
<slot v-else name="title"></slot>
</view>
<!--右侧标题-->
<view class="wd-cell-group__right">
<text v-if="value">{{ value }}</text>
<slot v-else name="value"></slot>
</view>
</view>
<view class="wd-cell-group__body">
<slot></slot>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-cell-group',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { getCurrentInstance, provide, ref } from 'vue'
interface Props {
customClass?: string
title?: string
value?: string
useSlot?: boolean
border?: boolean
}
const props = withDefaults(defineProps<Props>(), {
useSlot: false,
border: false,
customClass: ''
})
const cellList = ref<any>([]) // cell
provide('cell-list', cellList)
const { proxy } = getCurrentInstance() as any
provide('cell-group', proxy)
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,188 @@
@import '../common/abstracts/variable.scss';
@import '../common/abstracts/_mixin.scss';
.wot-theme-dark {
@include b(cell) {
background-color: $-dark-background2;
color: $-dark-color;
@include e(value) {
color: $-dark-color3;
}
@include e(label) {
color: $-dark-color3;
}
@include when(hover) {
background-color: $-dark-background4;
}
@include when(border) {
.wd-cell__wrapper {
@include halfPixelBorder('top', 0, $-dark-border-color);
}
}
:deep(.wd-cell__arrow-right) {
color: $-dark-color;
}
}
}
@include b(cell) {
position: relative;
padding-left: $-cell-padding;
background-color: $-color-white;
text-decoration: none;
color: $-cell-title-color;
line-height: $-cell-line-height;
-webkit-tap-highlight-color: transparent;
@include when(border) {
.wd-cell__wrapper {
@include halfPixelBorder('top');
}
}
@include e(wrapper) {
position: relative;
display: flex;
padding: $-cell-wrapper-padding $-cell-padding $-cell-wrapper-padding 0;
justify-content: space-between;
align-items: flex-start;
@include when(vertical) {
display: block;
.wd-cell__right {
margin-top: $-cell-vertical-top;
}
.wd-cell__value {
text-align: left;
}
.wd-cell__left {
margin-right: 0;
}
}
@include when(label) {
padding: $-cell-wrapper-padding-with-label $-cell-padding $-cell-wrapper-padding-with-label 0;
}
}
@include e(left) {
position: relative;
flex: 1;
display: flex;
font-size: $-cell-title-fs;
box-sizing: border-box;
margin-right: $-cell-padding;
@include when(required) {
padding-left: 12px;
&::after {
position: absolute;
content: '*';
top: 0;
left: 0;
font-size: $-cell-required-size;
color: $-cell-required-color;
}
}
}
@include e(right) {
position: relative;
flex: 1;
}
@include e(title) {
flex: 1;
width: 100%;
font-size: $-cell-title-fs;
}
@include e(label) {
margin-top: 2px;
font-size: $-cell-label-fs;
color: $-cell-label-color;
@include lineEllipsis;
}
@include edeep(icon) {
display: block;
position: relative;
margin-right: $-cell-icon-right;
font-size: $-cell-icon-size;
height: $-cell-line-height;
line-height: $-cell-line-height;
}
@include e(body){
display: flex;
}
@include e(value) {
position: relative;
flex: 1;
font-size: $-cell-value-fs;
color: $-cell-value-color;
text-align: right;
vertical-align: middle;
}
@include edeep(arrow-right) {
display: block;
margin-left: 8px;
width: $-cell-arrow-size;
font-size: $-cell-arrow-size;
color: $-cell-arrow-color;
height: $-cell-line-height;
line-height: $-cell-line-height;
}
@include e(error-message){
color: $-form-item-error-message-color;
font-size: $-form-item-error-message-font-size;
line-height: $-form-item-error-message-line-height;
text-align: left;
vertical-align: middle;
}
@include when(link) {
-webkit-tap-highlight-color: $-cell-tap-bg;
}
@include when(hover) {
background-color: $-cell-tap-bg;
}
@include when(large) {
.wd-cell__title {
font-size: $-cell-title-fs-large;
}
.wd-cell__wrapper {
padding-top: $-cell-wrapper-padding-large;
padding-bottom: $-cell-wrapper-padding-large;
}
.wd-cell__label {
font-size: $-cell-label-fs-large;
}
:deep(.wd-cell__icon) {
font-size: $-cell-icon-size-large;
}
}
@include when(center) {
.wd-cell__wrapper {
align-items: center;
}
}
}

@ -0,0 +1,157 @@
<template>
<view
:class="['wd-cell', isBorder ? 'is-border' : '', size ? 'is-' + size : '', center ? 'is-center' : '', customClass]"
:hover-class="isLink || clickable ? 'is-hover' : 'none'"
hover-stay-time="70"
@click="onClick"
>
<view :class="['wd-cell__wrapper', vertical ? 'is-vertical' : '']">
<view
:class="['wd-cell__left', isRequired ? 'is-required' : '']"
:style="titleWidth ? 'min-width:' + titleWidth + ';max-width:' + titleWidth + ';' : ''"
>
<!--左侧icon部位-->
<wd-icon v-if="icon" :name="icon" :custom-class="`wd-cell__icon ${customIconClass}`"></wd-icon>
<slot v-else name="icon" />
<view class="wd-cell__title">
<!--title BEGIN-->
<view v-if="title" :class="customTitleClass">{{ title }}</view>
<slot v-else name="title"></slot>
<!--title END-->
<!--label BEGIN-->
<view v-if="label" :class="`wd-cell__label ${customLabelClass}`">{{ label }}</view>
<slot v-else name="label" />
<!--label END-->
</view>
</view>
<!--right content BEGIN-->
<view class="wd-cell__right">
<view class="wd-cell__body">
<!--文案内容-->
<view :class="`wd-cell__value ${customValueClass}`">
<template v-if="value">{{ value }}</template>
<slot v-else></slot>
</view>
<!--箭头-->
<wd-icon v-if="isLink" custom-class="wd-cell__arrow-right" name="arrow-right" />
<slot v-else name="right-icon" />
</view>
<view v-if="errorMessage" class="wd-cell__error-message">{{ errorMessage }}</view>
</view>
<!--right content END-->
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-cell',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed } from 'vue'
import { useCell } from '../composables/useCell'
import { useParent } from '../composables/useParent'
import { FORM_KEY, FormItemRule } from '../wd-form/types'
interface Props {
title?: string
value?: string
icon?: string
label?: string
isLink?: boolean
to?: string
replace?: boolean
clickable?: boolean
size?: string
border?: boolean
titleWidth?: string
center?: boolean
required?: boolean
vertical?: boolean
prop?: string
rules?: FormItemRule[]
customClass?: string
customIconClass?: string
customLabelClass?: string
customValueClass?: string
customTitleClass?: string
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customIconClass: '',
customLabelClass: '',
customValueClass: '',
customTitleClass: '',
isLink: false,
clickable: false,
replace: false,
center: false,
required: false,
vertical: false,
rules: () => []
})
const cell = useCell()
const isBorder = computed(() => {
return cell.border.value
})
const { parent: form } = useParent(FORM_KEY)
const errorMessage = computed(() => {
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
return form.errorMessages[props.prop]
} else {
return ''
}
})
//
const isRequired = computed(() => {
let formRequired = false
if (form && form.rules) {
const rules = form.rules
for (const key in rules) {
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
}
}
}
return props.required || props.rules.some((rule) => rule.required) || formRequired
})
const emit = defineEmits(['click'])
/**
* @description 点击cell的handle
*/
function onClick() {
const url = props.to
if (props.clickable || props.isLink) {
emit('click')
}
if (url && props.isLink) {
if (props.replace) {
uni.redirectTo({ url })
} else {
uni.navigateTo({ url })
}
}
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,20 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
.wot-theme-dark {
@include b(checkbox-group) {
background-color: $-dark-background2;
}
}
@include b(checkbox-group) {
background-color: $-checkbox-bg;
// 20px 15px 12px
@include when(button) {
width: 100%;
padding: 8px 3px 20px 15px;
box-sizing: border-box;
overflow: hidden;
height: auto;
}
}

@ -0,0 +1,187 @@
<template>
<view :class="`wd-checkbox-group ${shape === 'button' && cell ? 'is-button' : ''} ${customClass}`">
<slot />
</view>
</template>
<script lang="ts">
export default {
name: 'wd-checkbox-group',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { getCurrentInstance, provide, watch } from 'vue'
import { checkNumRange, debounce, deepClone } from '../common/util'
type checkShape = 'circle' | 'square' | 'button'
interface Props {
customClass?: string
modelValue: Array<string | number | boolean>
cell?: boolean
shape?: checkShape
checkedColor?: string
disabled?: boolean
min?: number
max?: number
inline?: boolean
size?: string
name?: string
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
modelValue: () => [],
cell: false,
shape: 'circle',
checkedColor: '#4D80F0',
disabled: false,
min: 0,
max: 0,
inline: false
})
const children: any[] = [] //
const { proxy } = getCurrentInstance() as any
/**
* @description 修正子组件的 isChecked finalDisabled
* @param {array} values
*/
const resetChildren = debounce(function (values) {
values = values || props.modelValue
children &&
children.forEach((child) => {
// value
const isChecked = values.indexOf(child.modelValue) > -1
child.$.exposed.setChecked(isChecked)
})
}, 50)
watch(
() => props.modelValue,
(newValue) => {
// value
if (new Set(newValue).size !== newValue.length) {
// eslint-disable-next-line quotes
console.error("checkboxGroup's bound value includes same value")
}
if (newValue.length < props.min) {
// eslint-disable-next-line quotes
console.error("checkboxGroup's bound value's length can't be less than min")
}
if (props.max !== 0 && newValue.length > props.max) {
// eslint-disable-next-line quotes
console.error("checkboxGroup's bound value's length can't be large than max")
}
// value
children && children.length > 0 && resetChildren()
},
{ deep: true, immediate: true }
)
watch(
() => props.shape,
(newValue) => {
const type = ['circle', 'square', 'button']
if (type.indexOf(newValue) === -1) console.error(`shape must be one of ${type.toString()}`)
},
{ deep: true, immediate: true }
)
watch(
[() => props.disabled, () => props.inline, () => props.size],
() => {
resetChildren()
},
{ deep: true, immediate: true }
)
watch(
() => props.min,
(newValue) => {
checkNumRange(newValue, 'min')
resetChildren()
},
{ deep: true, immediate: true }
)
watch(
() => props.max,
(newValue) => {
checkNumRange(newValue, 'max')
resetChildren()
},
{ deep: true, immediate: true }
)
const emit = defineEmits(['change', 'update:modelValue'])
/**
* 设置子项
* @param child
*/
function setChild(child) {
const hasChild = children.findIndex((check) => {
return check.$.uid === child.$.uid
})
if (hasChild <= -1) {
children.push(child)
} else {
children[hasChild] = child
}
const index = children.findIndex((check) => {
return check.$.uid === child.$.uid
})
// isFirsttrue
if (index === 0) {
child.$.exposed.setFirst(true)
}
// isLasttrueisLast
if (index === children.length - 1) {
child.$.exposed.setLast(true)
const [prevChild] = children.slice(-2, -1)
prevChild && prevChild.$.exposed.setLast(false)
}
resetChildren()
}
/**
* @description 子节点通知父节点修改子节点选中状态
* @param {any} value 子组件的标识符
*/
function changeSelectState(value) {
const temp: (string | number | boolean)[] = deepClone(props.modelValue)
const index = temp.indexOf(value)
if (index > -1) {
// value
temp.splice(index, 1)
} else {
// value
temp.push(value)
}
emit('update:modelValue', temp)
// disabled
resetChildren(temp)
emit('change', {
value: temp
})
}
provide('checkGroup', proxy)
defineExpose({
setChild,
changeSelectState,
children
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,292 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
.wot-theme-dark {
@include b(checkbox) {
@include e(shape) {
background: transparent;
border-color: $-checkbox-border-color;
color: $-checkbox-check-color;
}
@include e(label) {
color: $-dark-color;
}
@include when(disabled) {
.wd-checkbox__shape {
border-color: $-dark-color-gray;
background: $-dark-background4;
}
.wd-checkbox__label {
color: $-dark-color-gray;
}
:deep(.wd-checkbox__check) {
color: $-dark-color-gray;
}
@include when(checked) {
.wd-checkbox__shape {
color: $-dark-color-gray;
}
.wd-checkbox__label {
color: $-dark-color-gray;
}
}
@include when(button) {
.wd-checkbox__label {
border-color: #c8c9cc;
background: #3a3a3c;
color: $-dark-color-gray;
}
@include when(checked) {
.wd-checkbox__label {
border-color: #c8c9cc;
background: #3a3a3c;
color: #c8c9cc;
}
}
}
}
@include when(button) {
.wd-checkbox__label {
background-color: $-dark-background;
}
@include when(checked) {
.wd-checkbox__label {
background-color: $-dark-background2;
}
}
}
}
}
@include b(checkbox) {
display: block;
margin-bottom: $-checkbox-margin;
font-size: 0;
-webkit-tap-highlight-color: transparent;
line-height: 1.2;
@include when(last-child) {
margin-bottom: 0;
}
@include e(shape) {
position: relative;
display: inline-block;
width: $-checkbox-size;
height: $-checkbox-size;
border: 2px solid $-checkbox-border-color;
border-radius: 50%;
color: $-checkbox-check-color;
background: $-checkbox-bg;
vertical-align: middle;
transition: background 0.2s;
box-sizing: border-box;
@include when(square) {
border-radius: $-checkbox-square-radius;
}
}
@include e(input) {
position: absolute;
width: 0;
height: 0;
margin: 0;
opacity: 0;
}
@include edeep(btn-check) {
display: inline-block;
font-size: $-checkbox-icon-size;
margin-right: 4px;
height: $-checkbox-icon-size;
width: $-checkbox-icon-size;
vertical-align: middle;
}
@include e(txt) {
display: inline-block;
vertical-align: middle;
line-height: 20px;
@include lineEllipsis;
}
@include e(label) {
position: relative;
display: inline-block;
margin-left: $-checkbox-label-margin;
vertical-align: middle;
font-size: $-checkbox-label-fs;
color: $-checkbox-label-color;
}
@include edeep(check) {
color: $-checkbox-check-color;
font-size: $-checkbox-icon-size;
opacity: 0;
transition: opacity 0.2s;
}
@include when(checked) {
.wd-checkbox__shape {
color: $-checkbox-checked-color;
background: currentColor;
border-color: currentColor;
}
:deep(.wd-checkbox__check) {
opacity: 1;
position: absolute;
left: 0;
top: -1px;
transform: translateX(-4.5%);
}
}
@include when(button) {
display: inline-block;
margin-bottom: 0;
margin-right: $-checkbox-margin;
vertical-align: top;
font-size: $-checkbox-button-font-size;
@include when(last-child) {
margin-right: 0;
}
.wd-checkbox__shape {
width: 0;
height: 0;
overflow: hidden;
opacity: 0;
border: none;
}
.wd-checkbox__label {
display: inline-flex;
flex-direction: row;
justify-content: center;
align-items: center;
min-width: $-checkbox-button-min-width;
height: $-checkbox-button-height;
font-size: $-checkbox-button-font-size;
margin-left: 0;
padding: 5px 15px;
border: 1px solid $-checkbox-button-border;
background-color: $-checkbox-button-bg;
border-radius: $-checkbox-button-radius;
transition: color 0.2s, border 0.2s;
box-sizing: border-box;
}
@include when(checked) {
.wd-checkbox__label {
color: $-checkbox-checked-color;
background-color: $-checkbox-bg;
border-color: $-checkbox-checked-color;
border-color: currentColor;
}
}
}
@include when(inline) {
display: inline-block;
margin-bottom: 0;
margin-right: $-checkbox-margin;
@include when(last-child) {
margin-right: 0;
}
}
@include when(disabled) {
.wd-checkbox__shape {
border-color: $-checkbox-border-color;
background: $-checkbox-disabled-check-bg;
}
.wd-checkbox__label {
color: $-checkbox-disabled-label-color;
}
@include when(checked) {
.wd-checkbox__shape {
color: $-checkbox-disabled-check-color;
}
.wd-checkbox__label {
color: $-checkbox-disabled-label-color;
}
}
@include when(button) {
.wd-checkbox__label {
background: $-checkbox-disabled-color;
border-color: $-checkbox-button-border;
color: $-checkbox-disabled-label-color;
}
@include when(checked) {
.wd-checkbox__label {
border-color: $-checkbox-button-disabled-border;
}
}
}
}
// START
@include when(cell-box) {
padding: 13px 15px;
margin: 0;
@include when(large) {
padding: 14px 15px;
}
}
@include when(button-box) {
display: inline-flex;
width: 33.3333%;
padding: 12px 12px 0 0;
box-sizing: border-box;
.wd-checkbox__label {
width: 100%;
}
&:last-child::after {
content: "";
display: table;
clear: both;
}
}
@include when(large) {
.wd-checkbox__shape {
width: $-checkbox-large-size;
height: $-checkbox-large-size;
font-size: $-checkbox-large-size;
}
.wd-checkbox__label {
font-size: $-checkbox-large-label-fs;
}
:deep(.wd-checkbox__check) {
top: 0;
left: 1px;
}
}
}

@ -0,0 +1,249 @@
<template>
<view
:class="`wd-checkbox ${innerCell ? 'is-cell-box' : ''} ${innerShape === 'button' ? 'is-button-box' : ''} ${isChecked ? 'is-checked' : ''} ${
isFirst ? 'is-first-child' : ''
} ${isLast ? 'is-last-child' : ''} ${innerInline ? 'is-inline' : ''} ${innerShape === 'button' ? 'is-button' : ''} ${
innerDisabled ? 'is-disabled' : ''
} ${innerSize ? 'is-' + innerSize : ''} ${customClass}`"
@click="toggle"
>
<!--shape为button时移除wd-checkbox__shape只保留wd-checkbox__label-->
<view
v-if="innerShape !== 'button'"
:class="`wd-checkbox__shape ${innerShape === 'square' ? 'is-square' : ''} ${customShapeClass}`"
:style="isChecked && !innerDisabled && innerCheckedColor ? 'color :' + innerCheckedColor : ''"
>
<wd-icon custom-class="wd-checkbox__check" name="check-bold" size="14px" />
</view>
<!--shape为button时只保留wd-checkbox__label-->
<view
:class="`wd-checkbox__label ${customLabelClass}`"
:style="isChecked && innerShape === 'button' && !innerDisabled && innerCheckedColor ? 'color:' + innerCheckedColor : ''"
>
<!--button选中时展示的icon-->
<wd-icon v-if="innerShape === 'button' && isChecked" custom-class="wd-checkbox__btn-check" name="check-bold" size="14px" />
<!--文案-->
<view class="wd-checkbox__txt" :style="maxWidth ? 'max-width:' + maxWidth : ''">
<slot></slot>
</view>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-checkbox',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, getCurrentInstance, inject, onBeforeMount, onMounted, ref, watch } from 'vue'
type checkShape = 'circle' | 'square' | 'button'
interface Props {
customLabelClass?: string
customShapeClass?: string
customClass?: string
modelValue: string | number | boolean
shape?: checkShape
checkedColor?: string
disabled?: boolean | null
trueValue?: string | number | boolean
falseValue?: string | number | boolean
size?: string
maxWidth?: string
}
const props = withDefaults(defineProps<Props>(), {
customLabelClass: '',
customShapeClass: '',
customClass: '',
shape: 'circle',
trueValue: true,
falseValue: false,
disabled: null
})
const isChecked = ref<boolean>(false) //
const inited = ref<boolean>(false)
// last-childfirst-child
const isFirst = ref<boolean>(false)
const isLast = ref<boolean>(false)
const parent = inject<any>('checkGroup', null)
const { proxy } = getCurrentInstance() as any
watch(
() => props.modelValue,
(newValue) => {
if (!inited.value) return
// 使
if (parent && parent.$.exposed.resetChildren) {
checkName()
return parent.$.exposed.resetChildren()
}
isChecked.value = newValue === props.trueValue
}
)
watch(
() => props.shape,
(newValue) => {
const type = ['circle', 'square', 'button']
if (type.indexOf(newValue) === -1) console.error(`shape must be one of ${type.toString()}`)
}
)
const innerShape = computed(() => {
if (!props.shape && parent && parent.shape) {
return parent.shape
} else {
return props.shape
}
})
const innerCheckedColor = computed(() => {
if (!props.checkedColor && parent && parent.checkedColor) {
return parent.checkedColor
} else {
return props.checkedColor
}
})
const innerDisabled = computed(() => {
let innerDisabled = props.disabled
if (parent) {
if (
// max group
(parent.max && parent.modelValue.length >= parent.max && !isChecked.value) ||
// min group
(parent.min && parent.modelValue.length <= parent.min && isChecked.value) ||
// disabled disabled
props.disabled === true ||
// disabled disabled
(parent.disabled && props.disabled === null)
) {
innerDisabled = true
}
}
return innerDisabled
})
const innerInline = computed(() => {
if (parent && parent.inline) {
return parent.inline
} else {
return false
}
})
const innerCell = computed(() => {
if (parent && parent.cell) {
return parent.cell
} else {
return false
}
})
const innerSize = computed(() => {
if (!props.size && parent && parent.size) {
return parent.size
} else {
return props.size
}
})
onBeforeMount(() => {
// eslint-disable-next-line quotes
if (props.modelValue === null) console.error("checkbox's value must be set")
inited.value = true
})
onMounted(() => {
// isChecked
if (!parent) {
isChecked.value = props.modelValue === props.trueValue
isFirst.value = true
isLast.value = true
}
if (parent) {
parent.$.exposed.setChild && parent.$.exposed.setChild(proxy)
isChecked.value = props.modelValue === parent.modelValue
}
})
const emit = defineEmits(['change', 'update:modelValue'])
/**
* @description 检测checkbox绑定的value是否和其它checkbox的value冲突
* @param {Object} self 自身
* @param myName 自己的标识符
*/
function checkName() {
parent &&
parent.$.exposed.children &&
parent.$.exposed.children.forEach((child) => {
if (child.$.uid !== proxy.$.uid && child.value === props.modelValue) {
console.error(`The checkbox's bound value: ${props.modelValue} has been used`)
}
})
}
/**
* @description 点击checkbox的Event handle
*/
function toggle() {
if (innerDisabled.value) return
// 使checkboxchange
if (parent) {
emit('change', {
value: !isChecked.value
})
parent.$.exposed.changeSelectState(props.modelValue)
} else {
const newVal = props.modelValue === props.trueValue ? props.falseValue : props.trueValue
emit('update:modelValue', newVal)
emit('change', {
value: newVal
})
}
}
/**
* 设置是否为第一个
* @param first
*/
function setFirst(first: boolean) {
isFirst.value = first
}
/**
* 设置是否为最后一个
* @param first
*/
function setLast(last: boolean) {
isLast.value = last
}
/**
* 设置是否选中
* @param checked 是否选中
*/
function setChecked(checked: boolean) {
isChecked.value = checked
}
defineExpose({
setFirst,
setLast,
setChecked
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,18 @@
@import "../common/abstracts/variable.scss";
@import "../common/abstracts/_mixin.scss";
@include b(circle) {
position: relative;
display: inline-block;
text-align: center;
@include e(text) {
position: absolute;
z-index: 1;
top: 50%;
left: 0;
width: 100%;
transform: translateY(-50%);
color: $-circle-text-color;
}
}

@ -0,0 +1,343 @@
<template>
<view :class="`wd-circle ${customClass}`">
<canvas :width="canvasSize" :height="canvasSize" :style="style" :id="canvasId" :canvas-id="canvasId"></canvas>
<view v-if="!text" class="wd-circle__text">
<!-- 自定义提示内容 -->
<slot></slot>
</view>
<!-- #ifdef MP-WEIXIN -->
<cover-view v-else class="wd-circle__text">
{{ text }}
</cover-view>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<!-- eslint-disable-next-line vue/valid-v-else -->
<text v-else class="wd-circle__text">
{{ text }}
</text>
<!-- #endif -->
</view>
</template>
<script lang="ts">
export default {
name: 'wd-circle',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
// Circle
import { computed, getCurrentInstance, onBeforeMount, onMounted, onUnmounted, ref, watch } from 'vue'
import { addUnit, isObj, objToStyle, uuid } from '../common/util'
// 0100
function format(rate: number) {
return Math.min(Math.max(rate, 0), 100)
}
//
const PERIMETER = 2 * Math.PI
//
const BEGIN_ANGLE = -Math.PI / 2
const STEP = 1
// "butt" | "round" | "square"
type StrokeLinecapType = 'butt' | 'round' | 'square'
interface Props {
//
modelValue: number
// class
customClass?: string
// style
customStyle?: string
//
rate?: number | string
// px
size?: number
//
color?: string | Record<string, string>
//
layerColor?: string
//
fill?: string
// rate/s
speed?: number
//
text?: string
// px
strokeWidth?: number
// "butt" | "round" | "square"
strokeLinecap?: StrokeLinecapType
//
clockwise?: boolean
}
const props = withDefaults(defineProps<Props>(), {
//
modelValue: 0,
// class
customClass: '',
// style
customStyle: '',
//
rate: 100,
// px
size: 100,
//
color: '#4d80f0',
//
layerColor: '#EBEEF5',
//
fill: '#ffffff',
// rate/s
speed: 50,
// px
strokeWidth: 10,
//
strokeLinecap: 'round',
//
clockwise: true
})
const progressColor = ref<string | CanvasGradient>('') //
const pixel = ref<number>(1) //
const currentValue = ref<number>(0) //
const interval = ref<any>(null) //
const canvasId = ref<string>(uuid()) // canvasId
let ctx: UniApp.CanvasContext | null = null
// canvas
const canvasSize = computed(() => {
return props.size * pixel.value
})
// Circle
const style = computed(() => {
const style = {
width: addUnit(props.size),
height: addUnit(props.size)
}
return `${objToStyle(style)}; ${props.customStyle}`
})
//
watch(
() => props.modelValue,
() => {
reRender()
},
{ immediate: true }
)
// Circle
watch(
() => props.size,
() => {
let timer = setTimeout(() => {
drawCircle(currentValue.value)
clearTimeout(timer)
}, 50)
},
{ immediate: false }
)
//
watch(
() => props.color,
() => {
drawCircle(currentValue.value)
},
{ immediate: false, deep: true }
)
//
watch(
() => props.layerColor,
() => {
drawCircle(currentValue.value)
},
{ immediate: false }
)
//
watch(
() => props.strokeWidth,
() => {
drawCircle(currentValue.value)
},
{ immediate: false }
)
//
watch(
() => props.clockwise,
() => {
drawCircle(currentValue.value)
},
{ immediate: false }
)
// #ifdef MP-ALIPAY
onBeforeMount(() => {
pixel.value = uni.getSystemInfoSync().pixelRatio
})
// #endif
onMounted(() => {
currentValue.value = props.modelValue
drawCircle(currentValue.value)
})
onUnmounted(() => {
clearTimeInterval()
})
const { proxy } = getCurrentInstance() as any
/**
* 获取canvas上下文
*/
function getContext() {
if (!ctx) {
ctx = uni.createCanvasContext(canvasId.value, proxy)
}
return Promise.resolve(ctx)
}
/**
* 设置canvas
*/
function presetCanvas(context: any, strokeStyle: string | CanvasGradient, beginAngle: number, endAngle: number, fill?: string) {
const canvasSize = props.size * pixel.value
let strokeWidth = props.strokeWidth * pixel.value
const position = canvasSize / 2
if (!fill) {
strokeWidth = strokeWidth / 2
}
const radius = position - strokeWidth / 2
context.strokeStyle = strokeStyle
context.setLineWidth(strokeWidth)
context.setLineCap(props.strokeLinecap)
context.beginPath()
context.arc(position, position, radius, beginAngle, endAngle, !props.clockwise)
context.stroke()
if (fill) {
context.setLineWidth(strokeWidth)
context.setFillStyle(fill)
context.fill()
}
}
/**
* 渲染管道
*/
function renderLayerCircle(context: UniApp.CanvasContext) {
presetCanvas(context, props.layerColor, 0, PERIMETER, props.fill)
}
/**
* 渲染进度条
*/
function renderHoverCircle(context: UniApp.CanvasContext, formatValue: number) {
const canvasSize = props.size * pixel.value
//
const progress = PERIMETER * (formatValue / 100)
const endAngle = props.clockwise ? BEGIN_ANGLE + progress : 3 * Math.PI - (BEGIN_ANGLE + progress)
//
if (isObj(props.color)) {
const LinearColor = context.createLinearGradient(canvasSize, 0, 0, 0)
Object.keys(props.color)
.sort((a, b) => parseFloat(a) - parseFloat(b))
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, props.color[key]))
progressColor.value = LinearColor
} else {
progressColor.value = props.color
}
presetCanvas(context, progressColor.value, BEGIN_ANGLE, endAngle)
}
/**
* 渲染圆点
* 进度值为0时渲染一个圆点
*/
function renderDot(context: UniApp.CanvasContext) {
const canvasSize = props.size * pixel.value
const strokeWidth = props.strokeWidth * pixel.value // =
const position = canvasSize / 2 //
//
if (isObj(props.color)) {
const LinearColor = context.createLinearGradient(canvasSize, 0, 0, 0)
Object.keys(props.color)
.sort((a, b) => parseFloat(a) - parseFloat(b))
.map((key) => LinearColor.addColorStop(parseFloat(key) / 100, props.color[key]))
progressColor.value = LinearColor
} else {
progressColor.value = props.color
}
context.beginPath()
context.arc(position, strokeWidth / 4, strokeWidth / 4, 0, PERIMETER)
context.setFillStyle(progressColor.value)
context.fill()
}
/**
* 画圆
*/
function drawCircle(currentValue: number) {
const canvasSize = props.size * pixel.value
getContext().then((context) => {
context.clearRect(0, 0, canvasSize, canvasSize)
renderLayerCircle(context)
const formatValue = format(currentValue)
if (formatValue !== 0) {
renderHoverCircle(context, formatValue)
} else {
renderDot(context)
}
context.draw()
})
}
/**
* Circle组件渲染
* 当前进度值变化时重新渲染Circle组件
*/
function reRender() {
//
if (props.speed <= 0 || props.speed > 1000) {
drawCircle(props.modelValue)
return
}
clearTimeInterval()
currentValue.value = currentValue.value || 0
const run = () => {
interval.value = setTimeout(() => {
if (currentValue.value !== props.modelValue) {
if (Math.abs(currentValue.value - props.modelValue) < STEP) {
currentValue.value = props.modelValue
} else if (currentValue.value < props.modelValue) {
currentValue.value += STEP
} else {
currentValue.value -= STEP
}
drawCircle(currentValue.value)
run()
} else {
clearTimeInterval()
}
}, 1000 / props.speed)
}
run()
}
/**
* 清除定时器
*/
function clearTimeInterval() {
interval.value && clearTimeout(interval.value)
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,228 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(col-picker) {
@include when(border) {
.wd-col-picker__cell {
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
}
}
@include e(cell) {
background-color: $-dark-background2;
color: $-dark-color;
@include when(disabled) {
.wd-col-picker__value {
color: $-dark-color3;
}
}
}
@include e(value) {
color: $-dark-color;
@include m(placeholder) {
color: $-dark-color-gray;
}
}
:deep(.wd-col-picker__arrow) {
color: $-dark-color;
}
@include e(list) {
color: $-dark-color;
}
@include e(selected) {
color: $-dark-color;
}
}
}
@include b(col-picker) {
@include when(border) {
.wd-col-picker__cell {
@include halfPixelBorder('top', $-cell-padding);
}
}
@include e(cell) {
position: relative;
display: flex;
padding: $-cell-wrapper-padding $-cell-padding;
align-items: flex-start;
background-color: $-color-white;
text-decoration: none;
color: $-cell-title-color;
font-size: $-cell-title-fs;
overflow: hidden;
line-height: $-cell-line-height;
}
@include e(cell) {
@include when(disabled) {
.wd-col-picker__value {
color: $-input-disabled-color;
}
}
@include when(align-right) {
.wd-col-picker__value {
text-align: right;
}
}
@include when(error) {
.wd-col-picker__value {
color: $-input-error-color;
}
:deep(.wd-col-picker__arrow) {
color: $-input-error-color;
}
}
@include when(large) {
font-size: $-cell-title-fs-large;
:deep(.wd-col-picker__arrow) {
font-size: $-cell-icon-size-large;
}
}
}
@include e(error-message){
color: $-form-item-error-message-color;
font-size: $-form-item-error-message-font-size;
line-height: $-form-item-error-message-line-height;
text-align: left;
vertical-align: middle;
}
@include e(label) {
position: relative;
width: $-input-cell-label-width;
margin-right: $-cell-padding;
color: $-cell-title-color;
box-sizing: border-box;
@include when(required) {
padding-left: 12px;
&::after {
position: absolute;
left: 0;
top: 2px;
content: '*';
font-size: $-cell-required-size;
line-height: 1.1;
color: $-cell-required-color;
}
}
}
@include e(value-wraper) {
display: flex;
}
@include e(value) {
flex: 1;
margin-right: 10px;
color: $-cell-value-color;
@include when(ellipsis) {
@include lineEllipsis;
}
@include m(placeholder) {
color: $-input-placeholder-color;
}
}
@include e(body) {
flex: 1;
}
@include edeep(arrow) {
display: block;
font-size: $-cell-icon-size;
color: $-cell-arrow-color;
line-height: $-cell-line-height;
}
@include e(selected) {
height: $-col-picker-selected-height;
font-size: $-col-picker-selected-fs;
color: $-col-picker-selected-color;
overflow: hidden;
}
@include e(selected-container){
position: relative;
display: flex;
user-select: none;
}
@include e(selected-item) {
flex: 0 0 auto;
height: $-col-picker-selected-height;
line-height: $-col-picker-selected-height;
padding: $-col-picker-selected-padding;
@include when(selected) {
font-weight: $-col-picker-selected-fw;
}
}
@include e(selected-line) {
position: absolute;
bottom: 5px;
width: $-col-picker-line-width;
left: 0;
height: $-col-picker-line-height;
background: $-col-picker-line-color;
z-index: 1;
border-radius: calc($-col-picker-line-height / 2);
box-shadow: $-col-picker-line-box-shadow;
}
@include e(list-container){
position: relative;
}
@include e(list) {
height: $-col-picker-list-height;
padding-bottom: $-col-picker-list-padding-bottom;
box-sizing: border-box;
overflow: auto;
color: $-col-picker-list-color;
font-size: $-col-picker-list-fs;
-webkit-overflow-scrolling: touch;
}
@include e(list-item) {
display: flex;
padding: $-col-picker-list-item-padding;
align-items: flex-start;
@include when(selected) {
color: $-col-picker-list-color-checked;
:deep(.wd-col-picker__checked) {
opacity: 1;
}
}
@include when(disabled) {
color: $-col-picker-list-color-disabled;
}
}
@include e(list-item-label) {
line-height: 1.285;
}
@include e(list-item-tip) {
margin-top: 2px;
font-size: $-col-picker-list-fs-tip;
color: $-col-picker-list-color-tip;
}
@include edeep(checked) {
display: block;
margin-left: 4px;
font-size: $-col-picker-list-checked-icon-size;
color: $-col-picker-list-color-checked;
opacity: 0;
}
@include e(loading) {
display: flex;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
align-items: center;
justify-content: center;
}
}

@ -0,0 +1,551 @@
<template>
<view :class="`wd-col-picker ${cell.border.value ? 'is-border' : ''} ${customClass}`">
<view class="wd-col-picker__field" @click="showPicker">
<slot v-if="useDefaultSlot"></slot>
<view
v-else
:class="`wd-col-picker__cell ${disabled && 'is-disabled'} ${readonly && 'is-readonly'} ${alignRight && 'is-align-right'} ${
error && 'is-error'
} ${size && 'is-' + size}`"
>
<view
v-if="label || useLabelSlot"
:class="`wd-col-picker__label ${isRequired && 'is-required'} ${customLabelClass}`"
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
>
<block v-if="label">{{ label }}</block>
<slot v-else name="label"></slot>
</view>
<view class="wd-col-picker__body">
<view class="wd-col-picker__value-wraper">
<view
:class="`wd-col-picker__value ${ellipsis && 'is-ellipsis'} ${customValueClass} ${showValue ? '' : 'wd-col-picker__value--placeholder'}`"
>
{{ showValue || placeholder || '请选择' }}
</view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-col-picker__arrow" name="arrow-right" />
</view>
<view v-if="errorMessage" class="wd-col-picker__error-message">{{ errorMessage }}</view>
</view>
</view>
</view>
<wd-action-sheet
v-model="pickerShow"
:duration="250"
:title="title || '请选择'"
:close-on-click-modal="closeOnClickModal"
:z-index="zIndex"
:safe-area-inset-bottom="safeAreaInsetBottom"
@close="handlePickerClose"
>
<view class="wd-col-picker__selected">
<scroll-view :scroll-x="true" scroll-with-animation :scroll-left="scrollLeft">
<view class="wd-col-picker__selected-container">
<view
v-for="(select, colIndex) in selectList"
:key="colIndex"
:class="`wd-col-picker__selected-item ${colIndex === currentCol && 'is-selected'}`"
@click="handleColClick(colIndex)"
>
{{ selectShowList[colIndex] || '请选择' }}
</view>
<view class="wd-col-picker__selected-line" :style="lineStyle"></view>
</view>
</scroll-view>
</view>
<view class="wd-col-picker__list-container">
<view
v-for="(col, colIndex) in selectList"
:key="colIndex"
class="wd-col-picker__list"
:style="colIndex === currentCol ? 'display: block;' : 'display: none;'"
>
<view
v-for="(item, index) in col"
:key="index"
:class="`wd-col-picker__list-item ${pickerColSelected[colIndex] && item[valueKey] === pickerColSelected[colIndex] && 'is-selected'} ${
item.disabled && 'is-disabled'
}`"
@click="chooseItem(colIndex, index)"
>
<view>
<view class="wd-col-picker__list-item-label">{{ item[labelKey] }}</view>
<view v-if="item[tipKey]" class="wd-col-picker__list-item-tip">{{ item[tipKey] }}</view>
</view>
<wd-icon custom-class="wd-col-picker__checked" name="check"></wd-icon>
</view>
<view v-if="loading" class="wd-col-picker__loading">
<wd-loading :color="loadingColor" />
</view>
</view>
</view>
</wd-action-sheet>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-col-picker',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, getCurrentInstance, onMounted, ref, watch } from 'vue'
import { debounce, getRect, getType } from '../common/util'
import { useCell } from '../composables/useCell'
import { FORM_KEY, FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent'
const $container = '.wd-col-picker__selected-container'
const $item = '.wd-col-picker__selected-item'
interface Props {
customClass?: string
customViewClass?: string
customLabelClass?: string
customValueClass?: string
modelValue: Array<string | number>
columns: Array<Array<Record<string, any>>>
label?: string
labelWidth?: string
useLabelSlot?: boolean
useDefaultSlot?: boolean
disabled?: boolean
readonly?: boolean
placeholder?: string
title?: string
// item resolve finish
// eslint-disable-next-line @typescript-eslint/ban-types
columnChange?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
displayFormat?: Function
// eslint-disable-next-line @typescript-eslint/ban-types
beforeConfirm?: Function
alignRight?: boolean
error?: boolean
required?: boolean
size?: string
valueKey?: string
labelKey?: string
tipKey?: string
loadingColor?: string
closeOnClickModal?: boolean
autoComplete?: boolean
zIndex?: number
safeAreaInsetBottom?: boolean
ellipsis?: boolean
prop?: string
rules?: FormItemRule[]
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customViewClass: '',
customLabelClass: '',
customValueClass: '',
columns: () => [],
useLabelSlot: false,
useDefaultSlot: false,
disabled: false,
readonly: false,
placeholder: '请选择',
alignRight: false,
error: false,
required: false,
valueKey: 'value',
labelKey: 'label',
tipKey: 'tip',
loadingColor: '#4D80F0',
closeOnClickModal: true,
autoComplete: false,
zIndex: 15,
safeAreaInsetBottom: true,
ellipsis: false,
labelWidth: '33%',
rules: () => []
})
const pickerShow = ref<boolean>(false)
const currentCol = ref<number>(0)
const selectList = ref<Record<string, any>[]>([])
const pickerColSelected = ref<(string | number)[]>([])
const selectShowList = ref<Record<string, any>[]>([])
const loading = ref<boolean>(false)
const showValue = ref<string>('')
const isChange = ref<boolean>(false)
const lastSelectList = ref<Record<string, any>[]>([])
const lastPickerColSelected = ref<(string | number)[]>([])
const lineStyle = ref<string>('')
const scrollLeft = ref<number>(0)
const inited = ref<boolean>(false)
const isCompleting = ref<boolean>(false)
const { proxy } = getCurrentInstance() as any
const cell = useCell()
const updateLineAndScroll = debounce(function (animation = true) {
setLineStyle(animation)
lineScrollIntoView()
}, 50)
watch(
() => props.modelValue,
(newValue) => {
if (newValue === pickerColSelected.value) return
pickerColSelected.value = newValue
newValue.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)[props.labelKey]
})
setShowValue(newValue)
handleAutoComplete()
},
{
deep: true,
immediate: true
}
)
watch(
() => props.columns,
(newValue, oldValue) => {
if (newValue.length && !(newValue[0] instanceof Array)) {
console.error('[wot design] error(wd-col-picker): the columns props of wd-col-picker should be a two-dimensional array')
return
}
if (newValue.length === 0 && !oldValue) return
const newSelectedList = newValue.slice(0)
selectList.value = newSelectedList
selectShowList.value = pickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, newSelectedList)[props.labelKey]
})
lastSelectList.value = newSelectedList
if (newSelectedList.length > 0) {
currentCol.value = newSelectedList.length - 1
setShowValue(props.modelValue)
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.columnChange,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of columnChange must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.displayFormat,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of displayFormat must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.beforeConfirm,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of beforeConfirm must be Function')
}
},
{
deep: true,
immediate: true
}
)
const { parent: form } = useParent(FORM_KEY)
//
const errorMessage = computed(() => {
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
return form.errorMessages[props.prop]
} else {
return ''
}
})
//
const isRequired = computed(() => {
let formRequired = false
if (form && form.rules) {
const rules = form.rules
for (const key in rules) {
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
}
}
}
return props.required || props.rules.some((rule) => rule.required) || formRequired
})
const emit = defineEmits(['close', 'update:modelValue', 'confirm'])
onMounted(() => {
inited.value = true
})
//
function open() {
showPicker()
}
//
function close() {
handlePickerClose()
}
function handlePickerClose() {
pickerShow.value = false
// popuppopup 250
if (isChange.value) {
setTimeout(() => {
selectList.value = lastSelectList.value.slice(0)
pickerColSelected.value = lastPickerColSelected.value.slice(0)
selectShowList.value = lastPickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, lastSelectList)[props.labelKey]
})
currentCol.value = lastSelectList.value.length - 1
isChange.value = false
}, 250)
}
emit('close')
}
function showPicker() {
const { disabled, readonly } = props
if (disabled || readonly) return
pickerShow.value = true
lastPickerColSelected.value = pickerColSelected.value.slice(0)
lastSelectList.value = selectList.value.slice(0)
setTimeout(() => {
updateLineAndScroll()
}, 30)
}
function getSelectedItem(value, colIndex, selectList) {
const { valueKey, labelKey } = props
if (selectList[colIndex]) {
const selecteds = selectList[colIndex].filter((item) => {
return item[valueKey] === value
})
if (selecteds.length > 0) {
return selecteds[0]
}
}
return {
[valueKey]: value,
[labelKey]: ''
}
}
function chooseItem(colIndex, index) {
const item = selectList.value[colIndex][index]
if (item.disabled) return
const newPickerColSelected = pickerColSelected.value.slice(0, colIndex)
newPickerColSelected.push(item[props.valueKey])
isChange.value = true
pickerColSelected.value = newPickerColSelected
selectList.value = selectList.value.slice(0, colIndex + 1)
selectShowList.value = newPickerColSelected.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)[props.labelKey]
})
handleColChange(colIndex, item, index)
}
function handleColChange(colIndex, item, index, callback?) {
loading.value = true
const { columnChange, beforeConfirm } = props
columnChange &&
columnChange({
selectedItem: item,
index: colIndex,
rowIndex: index,
resolve: (nextColumn) => {
if (!(nextColumn instanceof Array)) {
console.error('[wot design] error(wd-col-picker): the data of each column of wd-col-picker should be an array')
return
}
const newSelectList = selectList.value.slice(0)
newSelectList[colIndex + 1] = nextColumn
selectList.value = newSelectList
loading.value = false
currentCol.value = colIndex + 1
updateLineAndScroll(true)
if (typeof callback === 'function') {
isCompleting.value = false
selectShowList.value = pickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)[props.labelKey]
})
callback()
}
},
finish: (isOk) => {
//
if (typeof callback === 'function') {
loading.value = false
isCompleting.value = false
return
}
if (getType(isOk) === 'boolean' && !isOk) {
loading.value = false
return
}
if (beforeConfirm) {
beforeConfirm(
pickerColSelected.value,
pickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)
}),
(isPass) => {
if (isPass) {
onConfirm()
} else {
loading.value = false
}
}
)
} else {
onConfirm()
}
}
})
}
function onConfirm() {
isChange.value = false
loading.value = false
pickerShow.value = false
emit('update:modelValue', pickerColSelected.value)
setShowValue(pickerColSelected.value)
emit('confirm', {
value: pickerColSelected.value,
selectedItems: pickerColSelected.value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)
})
})
}
function handleColClick(index: number) {
isChange.value = true
currentCol.value = index
updateLineAndScroll(true)
}
/**
* @description 更新navBar underline的偏移量
* @param {Boolean} animation 是否伴随动画
*/
function setLineStyle(animation = true) {
if (!inited.value) return
getRect($item, true, proxy).then((rects: any) => {
const rect = rects[currentCol.value]
// const width = lineWidth || (slidableNum < items.length ? rect.width : (rect.width - 14))
const width = 16
let left = rects.slice(0, currentCol.value).reduce((prev, curr) => prev + curr.width, 0)
left += (rect.width - width) / 2
const transition = animation ? 'transition: width 300ms ease, transform 300ms ease;' : ''
const lineStyleTemp = `
transform: translateX(${left}px);
${transition}
`
//
if (lineStyle.value !== lineStyleTemp) {
lineStyle.value = lineStyleTemp
}
})
}
/**
* @description scroll-view滑动到active的tab_nav
*/
function lineScrollIntoView() {
if (!inited.value) return
Promise.all([getRect($item, true, proxy), getRect($container, false, proxy)]).then(([navItemsRects, navRect]) => {
if ((navItemsRects as any).length === 0) return
//
const selectItem = navItemsRects[currentCol.value]
//
const offsetLeft = (navItemsRects as any).slice(0, currentCol).reduce((prev, curr) => prev + curr.width, 0)
// scroll-viewselectItem
scrollLeft.value = offsetLeft - ((navRect as any).width - selectItem.width) / 2
})
}
function setShowValue(value) {
const selectedItems = value.map((item, colIndex) => {
return getSelectedItem(item, colIndex, selectList.value)
})
if (props.displayFormat) {
showValue.value = props.displayFormat(selectedItems)
} else {
showValue.value = selectedItems
.map((item) => {
return item[props.labelKey]
})
.join('')
}
}
//
function diffColumns(colIndex) {
// colIndex -1 item >=0 value
const item = colIndex === -1 ? {} : { [props.valueKey]: props.modelValue[colIndex] }
handleColChange(colIndex, item, -1, () => {
// columns value colIndex + 1
if (selectList.value.length < props.modelValue.length) {
diffColumns(colIndex + 1)
} else {
setShowValue(pickerColSelected.value)
}
})
}
function handleAutoComplete() {
if (props.autoComplete) {
// columns value columnChange
if (selectList.value.length < props.modelValue.length || selectList.value.length === 0) {
// isCompleting
if (!isCompleting.value) {
// columns colIndex -1
const colIndex = selectList.value.length === 0 ? -1 : selectList.value.length - 1
diffColumns(colIndex)
}
isCompleting.value = true
}
}
}
defineExpose({
close,
open
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,19 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
$i: 1;
@include b(col) {
float: left;
box-sizing: border-box;
}
@while $i <= 24 {
.wd-col__#{$i} {
width: calc(100% / 24 * $i);
}
.wd-col__offset-#{$i} {
margin-left: calc(100% / 24 * $i);
}
$i: $i + 1;
}

@ -0,0 +1,78 @@
<!--
* @Author: weisheng
* @Date: 2023-06-13 11:34:35
* @LastEditTime: 2023-11-20 13:32:00
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-col\wd-col.vue
* 记得注释
-->
<template>
<view :class="['wd-col', span && 'wd-col__' + span, offset && 'wd-col__offset-' + offset, customClass]" :style="style">
<!-- 每一列 -->
<slot />
</view>
</template>
<script lang="ts">
export default {
name: 'wd-col',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { inject, provide, watch } from 'vue'
import { ref } from 'vue'
interface Props {
span?: number
offset?: number
customClass?: string
}
const props = withDefaults(defineProps<Props>(), {
span: 24,
offset: 0,
customClass: ''
})
const style = ref<string>('')
const row: any = inject('$row')
watch([() => props.span, () => props.offset], () => {
check()
})
watch(
() => row.gutter,
(newVal) => {
setGutter(newVal || 0)
},
{ deep: true, immediate: true }
)
function check() {
const { span, offset } = props
if (span < 0 || offset < 0) {
console.error('[wot-design] warning(wd-col): attribute span/offset must be greater than or equal to 0')
}
}
function setGutter(gutter: number) {
const padding = `${gutter / 2}px`
const customStyle = gutter > 0 ? `padding-left: ${padding}; padding-right: ${padding};background-clip: content-box;` : ''
if (customStyle !== style.value) {
style.value = customStyle
}
}
provide('setGutter', setGutter) //
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,83 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(collapse-item) {
@include halfPixelBorder('top', 0, $-dark-border-color);
@include e(title) {
color: $-dark-color;
}
@include e(body) {
color: $-dark-color3;
}
@include when(disabled) {
.wd-collapse-item__title {
color: $-dark-color-gray;
}
.wd-collapse-item__arrow {
color: $-dark-color-gray;
}
}
}
}
@include b(collapse-item) {
position: relative;
@include halfPixelBorder('top');
@include e(header) {
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
padding: $-collapse-header-padding;
overflow: hidden;
user-select: none;
}
@include e(title) {
color: $-collapse-title-color;
font-weight: $-fw-medium;
font-size: $-collapse-title-fs;
}
@include edeep(arrow) {
display: block;
font-size: $-collapse-arrow-size;
color: $-collapse-arrow-color;
transition: transform 0.3s;
@include when(retract) {
transform: rotate(-180deg);
}
}
@include e(wrapper) {
position: relative;
transition: height 0.3s ease-in-out;
overflow: hidden;
will-change: height;
}
@include e(body) {
color: $-collapse-body-color;
font-size: $-collapse-body-fs;
padding: $-collapse-body-padding;
line-height: 1.43;
}
@include when(disabled) {
.wd-collapse-item__title {
color: $-collapse-disabled-color;
}
.wd-collapse-item__arrow {
color: $-collapse-disabled-color;
}
}
}

@ -0,0 +1,189 @@
<!--
* @Author: weisheng
* @Date: 2023-08-01 11:12:05
* @LastEditTime: 2023-10-29 17:42:06
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-collapse-item\wd-collapse-item.vue
* 记得注释
-->
<template>
<view :class="`wd-collapse-item ${disabled ? 'is-disabled' : ''} is-border ${customClass}`">
<view :class="`wd-collapse-item__header ${isFirst ? 'wd-collapse-item__header-first' : ''}`" @click="handleClick">
<text class="wd-collapse-item__title">{{ title }}</text>
<wd-icon name="arrow-down" :custom-class="`wd-collapse-item__arrow ${expanded ? 'is-retract' : ''}`" />
</view>
<view class="wd-collapse-item__wrapper" :style="contentStyle">
<view class="wd-collapse-item__body">
<slot />
</view>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-collapse-item',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { type Ref, computed, getCurrentInstance, inject, onMounted, ref, watch } from 'vue'
import { getRect, isArray, isDef, isPromise, objToStyle } from '../common/util'
const $body = '.wd-collapse-item__body'
interface Props {
customClass?: string
title?: string
disabled?: boolean
name: string
// false Promise
// eslint-disable-next-line @typescript-eslint/ban-types
beforeExpend?: Function
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
disabled: false
})
const parent = inject<any>('wdcollapse')
// eslint-disable-next-line @typescript-eslint/ban-types
const setChange: Function | undefined = inject('set-change') //
// eslint-disable-next-line @typescript-eslint/ban-types
const setChild: Function | undefined = inject('set-child') // children
const height = ref<string | number>('')
const show = ref<boolean>(true)
const firstItem = inject<Ref<string>>('firstItem')
const expanded = ref<boolean>(false)
const transD = ref<string>('0.3s')
const { proxy } = getCurrentInstance() as any
/**
* 容器样式(动画)
*/
const isFirst = computed(() => {
return firstItem && firstItem.value === props.name
})
/**
* 容器样式(动画)
*/
const contentStyle = computed(() => {
let style: Record<string, string> = {
height: expanded.value ? height.value + 'px' : '0px',
'transition-duration': transD.value
}
return objToStyle(style)
})
watch(
() => parent.modelValue,
(newVal: string | string[]) => {
const name = props.name
if (isDef(newVal)) {
if (typeof newVal === 'string' && newVal === name) {
doResetHeight($body)
expanded.value = true
} else if (isArray(newVal) && newVal.indexOf(name as string) >= 0) {
doResetHeight($body)
expanded.value = true
} else {
expanded.value = false
}
} else {
expanded.value = false
}
}
)
onMounted(() => {
init()
})
/**
* 初始化将组件信息注入父组件
*/
function init() {
doResetHeight($body)
updateExpended()
let name = props.name
setChild && setChild({ name: name, expanded: expanded.value })
}
/**
* 更新展开状态
*/
function updateExpended() {
if (parent) {
let { modelValue } = parent
let name = props.name
if (modelValue) {
if (typeof modelValue === 'string' && modelValue === name) {
expanded.value = true
} else if (isArray(modelValue) && modelValue.indexOf(name) >= 0) {
expanded.value = true
}
}
}
}
/**
* 控制折叠面板滚动
* @param {String} select 选择器名称
* @param {Boolean} firstRender 是否首次渲染
*/
function doResetHeight(select) {
getRect(select, false, proxy).then((rect: any) => {
if (!rect) return
const { height: rectHeight } = rect
height.value = rectHeight
})
}
//
function handleClick() {
if (props.disabled) return
let name = props.name
const nexexpanded = !expanded.value //
if (nexexpanded) {
if (props.beforeExpend) {
const response: any = props.beforeExpend(name)
if (!response) {
return
}
if (isPromise(response)) {
response.then(() => {
setChange && setChange({ name: name, expanded: !expanded.value })
})
} else {
setChange && setChange({ name: name, expanded: !expanded.value })
}
} else {
setChange && setChange({ name: name, expanded: !expanded.value })
}
} else {
setChange && setChange({ name: name, expanded: !expanded.value })
}
}
//
function onTransitionend() {
if (!expanded.value) {
show.value = false
} else {
height.value = ''
}
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,55 @@
@import "../common/abstracts/variable";
@import "../common/abstracts/mixin";
.wot-theme-dark {
@include b(collapse) {
background: $-dark-background2;
@include e(content) {
color: $-dark-color3;
}
}
}
@include b(collapse) {
background: $-color-white;
@include when(viewmore) {
padding: $-collapse-side-padding;
}
@include e(content) {
font-size: $-collapse-body-fs;
color: $-collapse-body-color;
@include when(retract) {
display: -webkit-box;
-webkit-box-orient: vertical;
overflow: hidden;
font-size: $-collapse-retract-fs;
}
}
@include e(more) {
display: inline-block;
font-size: $-collapse-retract-fs;
margin-top: 8px;
color: $-collapse-more-color;
user-select: none;
}
@include e(more-txt) {
display: inline-block;
vertical-align: middle;
margin-right: 4px;
}
@include e(arrow) {
display: inline-block;
vertical-align: middle;
transition: transform 0.1s;
font-size: $-collapse-arrow-size;
height: $-collapse-arrow-size;
line-height: $-collapse-arrow-size;
@include when(retract) {
transform: rotate(-180deg);
}
}
}

@ -0,0 +1,7 @@
/**
*
*/
export interface CollapseItem {
name: string
expanded: boolean
}

@ -0,0 +1,194 @@
<!--
* @Author: weisheng
* @Date: 2023-08-01 11:12:05
* @LastEditTime: 2023-11-20 13:33:11
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-collapse\wd-collapse.vue
* 记得注释
-->
<template>
<view :class="`wd-collapse ${viewmore ? 'is-viewmore' : ''} ${customClass}`">
<!-- 普通或手风琴 -->
<block v-if="!viewmore">
<slot></slot>
</block>
<!-- 查看更多模式 -->
<view v-else>
<view
:class="`wd-collapse__content ${!modelValue ? 'is-retract' : ''} `"
:style="`-webkit-line-clamp: ${contentLineNum}; -webkit-box-orient: vertical`"
>
<slot></slot>
</view>
<view class="wd-collapse__more" @click="handleMore">
<!-- 自定义展开按钮 -->
<view v-if="useMoreSlot" :class="customMoreSlotClass">
<slot name="more"></slot>
</view>
<!-- 显示展开或折叠按钮 -->
<block v-else>
<span class="wd-collapse__more-txt">{{ !modelValue ? '展开' : '折叠' }}</span>
<view :class="`wd-collapse__arrow ${modelValue ? 'is-retract' : ''}`">
<wd-icon name="arrow-down"></wd-icon>
</view>
</block>
</view>
</view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-collapse',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, provide, ref, watch } from 'vue'
import type { CollapseItem } from './types'
import { deepClone, isBoolean } from '../common/util'
interface Props {
customClass?: string
customMoreSlotClass?: string
modelValue: string | Array<string> | boolean
accordion?: boolean
viewmore?: boolean
useMoreSlot?: boolean
lineNum?: number
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customMoreSlotClass: '',
accordion: false,
viewmore: false,
useMoreSlot: false,
lineNum: 2
})
const contentLineNum = ref<number>(0) //
const children: CollapseItem[] = [] //
const firstItem = ref<string>('') //
const { proxy } = getCurrentInstance() as any
const emit = defineEmits(['change', 'update:modelValue'])
watch(
() => props.modelValue,
(newVal) => {
const { viewmore, accordion } = props
// value string
if (accordion && typeof newVal !== 'string') {
console.error('accordion value must be string')
} else if (!accordion && !viewmore && checkType(newVal) !== 'Array') {
console.error('value must be Array')
}
},
{ deep: true, immediate: true }
)
watch(
() => props.lineNum,
(newVal) => {
if (newVal <= 0) {
console.error('lineNum must greater than 0')
}
},
{ deep: true, immediate: true }
)
onBeforeMount(() => {
const { lineNum, viewmore, modelValue } = props
contentLineNum.value = viewmore && !modelValue ? lineNum : 0
})
/**
* 设置子项
* @param child
*/
function setChild(child: CollapseItem) {
const hasChild = children.findIndex((item) => {
return item.name === child.name
})
if (hasChild === -1) {
const repeat = checkRepeat(children, child.name, 'name')
if (repeat > -1) {
console.error('Name attribute cannot be defined repeatedly')
}
children.push(child)
} else {
children[hasChild] = child
}
if (children.length) {
firstItem.value = children[0].name
}
}
function checkType(value) {
return Object.prototype.toString.call(value).slice(8, -1)
}
/**
* 检查是否存在重复属性
* @param {Array} currentList
* @param {String} checkValue 比较的重复值
* @param {String} key 键名
*/
function checkRepeat(currentList: CollapseItem[], checkValue: string, key: string): number {
return currentList.findIndex((item) => item[key] === checkValue)
}
/**
* 子项状态变更
* @param child 子项
*/
function setChange(child: CollapseItem) {
let activeNames: string | string[] | boolean = deepClone(props.modelValue || '')
if (!isBoolean(activeNames)) {
if (props.accordion) {
activeNames = child.expanded ? child.name : ''
} else {
activeNames = child.expanded
? Array.from(new Set((activeNames || []).concat(child.name)))
: ((activeNames as string[]) || []).filter((activeName: string | number) => activeName !== child.name)
}
}
emit('update:modelValue', activeNames)
emit('change', {
value: activeNames
})
}
/**
* 查看更多点击
*/
function handleMore() {
emit('update:modelValue', !props.modelValue)
emit('change', {
value: !props.modelValue
})
}
provide('wdcollapse', proxy)
provide('firstItem', firstItem)
provide('set-child', setChild) //
provide('set-change', setChange) //
defineExpose({
children,
setChild,
checkRepeat
// switchValue
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,89 @@
<!--
* @Author: weisheng
* @Date: 2023-06-13 11:34:35
* @LastEditTime: 2023-08-16 10:51:08
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-config-provider\wd-config-provider.vue
* 记得注释
-->
<template>
<view :class="themeClass" :style="themeStyle">
<slot />
</view>
</template>
<script lang="ts">
export default {
name: 'wd-config-provider',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed } from 'vue'
type ConfigProviderTheme = 'light' | 'dark'
interface Props {
theme?: ConfigProviderTheme
themeVars?: Record<string, any>
}
const props = withDefaults(defineProps<Props>(), {
theme: 'light',
themeVars: () => {
return {}
}
})
const themeClass = computed(() => {
return `wot-theme-${props.theme}`
})
const themeStyle = computed(() => {
return mapThemeVarsToCSSVars(props.themeVars)
})
const kebabCase = (str: string): string => {
str = str.replace(str.charAt(0), str.charAt(0).toLocaleLowerCase())
return str.replace(/([a-z])([A-Z])/g, (_, p1, p2) => p1 + '-' + p2.toLowerCase())
}
const colorRgb = (str: string) => {
if (!str) return
var sColor = str.toLowerCase()
//
var reg = /^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/
// 16
if (sColor && reg.test(sColor)) {
if (sColor.length === 4) {
var sColorNew = '#'
for (let i = 1; i < 4; i += 1) {
sColorNew += sColor.slice(i, i + 1).concat(sColor.slice(i, i + 1))
}
sColor = sColorNew
}
//
var sColorChange: number[] = []
for (let i = 1; i < 7; i += 2) {
sColorChange.push(parseInt('0x' + sColor.slice(i, i + 2)))
}
return sColorChange.join(',')
}
return null
}
const mapThemeVarsToCSSVars = (themeVars: Record<string, string>) => {
if (!themeVars) return
const cssVars: Record<string, string> = {}
Object.keys(themeVars).forEach((key) => {
cssVars[`--wot-${kebabCase(key)}`] = themeVars[key]
})
return cssVars
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,15 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(count-down) {
color: $-dark-color;
}
}
@include b(count-down) {
color: $-count-down-text-color;
font-size: $-count-down-font-size;
line-height: $-count-down-line-height;
}

@ -0,0 +1,56 @@
import { padZero } from '../common/util'
export type TimeData = {
days: number
hours: number
minutes: number
seconds: number
milliseconds: number
}
export function parseFormat(format: string, timeData: TimeData): string {
const { days } = timeData
let { hours, minutes, seconds, milliseconds } = timeData
if (format.includes('DD')) {
format = format.replace('DD', padZero(days))
} else {
hours += days * 24
}
if (format.includes('HH')) {
format = format.replace('HH', padZero(hours))
} else {
minutes += hours * 60
}
if (format.includes('mm')) {
format = format.replace('mm', padZero(minutes))
} else {
seconds += minutes * 60
}
if (format.includes('ss')) {
format = format.replace('ss', padZero(seconds))
} else {
milliseconds += seconds * 1000
}
if (format.includes('S')) {
const ms = padZero(milliseconds, 3)
if (format.includes('SSS')) {
format = format.replace('SSS', ms)
} else if (format.includes('SS')) {
format = format.replace('SS', ms.slice(0, 2))
} else {
format = format.replace('S', ms.charAt(0))
}
}
return format
}
export function isSameSecond(time1: number, time2: number): boolean {
return Math.floor(time1 / 1000) === Math.floor(time2 / 1000)
}

@ -0,0 +1,76 @@
<template>
<view :class="`wd-count-down ${customClass}`" :style="customStyle">
<slot :current="current" v-if="$slots.default" />
<block v-else>{{ timeText }}</block>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-count-down',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script setup lang="ts">
import { watch, computed } from 'vue'
import { parseFormat } from './utils'
import { useCountDown } from '../composables/useCountDown'
interface Props {
//
time: number
//
millisecond?: boolean
//
format?: string
//
autoStart?: boolean
//
customClass?: string
//
customStyle?: string
}
const props = withDefaults(defineProps<Props>(), {
format: 'HH:mm:ss',
autoStart: true,
millisecond: false,
customClass: ''
})
const emit = defineEmits(['change', 'finish'])
const { start, pause, reset, current } = useCountDown({
time: props.time,
millisecond: props.millisecond,
onChange: (current) => emit('change', current),
onFinish: () => emit('finish')
})
const timeText = computed(() => parseFormat(props.format, current.value))
const resetTime = () => {
reset(props.time)
if (props.autoStart) {
start()
}
}
watch(() => props.time, resetTime, { immediate: true })
defineExpose({
start,
pause,
reset: resetTime
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,80 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
:deep(.wd-curtain){
display: inline-block;
border-radius: $-curtain-content-radius;
overflow-y: visible;
background: transparent;
font-size: 0;
}
@include bdeep(curtain) {
@include e(content) {
position: relative;
display: inline-block;
background: transparent;
border-radius: $-curtain-content-radius;
}
@include e(content-link) {
display: block;
border-radius: $-curtain-content-radius;
}
@include e(content-img) {
display: block;
width: auto;
height: auto;
border-radius: $-curtain-content-radius;
}
@include edeep(content-close) {
position: absolute;
margin: 0;
padding: 6px;
top: 10px;
right: 10px;
color: $-curtain-content-close-color;
-webkit-tap-highlight-color: transparent;
&.top {
margin: 0 0 0 -18px;
top: -62px;
right: unset;
left: 50%;
bottom: unset;
}
&.top-left {
margin: 0;
top: -62px;
right: unset;
left: -6px;
bottom: unset;
}
&.top-right {
margin: 0;
top: -62px;
right: -6px;
left: unset;
bottom: unset;
}
&.bottom {
margin: 0 0 0 -18px;
top: unset;
right: unset;
left: 50%;
bottom: -62px;
}
&.bottom-left {
margin: 0;
top: unset;
right: unset;
left: -6px;
bottom: -62px;
}
&.bottom-right {
margin: 0;
top: unset;
right: -6px;
left: unset;
bottom: -62px;
}
}
}

@ -0,0 +1,178 @@
<template>
<view>
<wd-popup
v-model="show"
transition="zoom-in"
position="center"
:close-on-click-modal="closeOnClickModal"
:hide-when-close="hideWhenClose"
@before-enter="beforeenter"
@enter="enter"
@after-enter="afterenter"
@before-leave="beforeleave"
@leave="leave"
@after-leave="afterleave"
@close="close"
@click-modal="clickModal"
:custom-class="`wd-curtain ${customClass}`"
>
<view class="wd-curtain__content">
<image :src="src" class="wd-curtain__content-img" :style="imgStyle" @click="clickImage" @error="imgErr" @load="imgLoad"></image>
<wd-icon name="close-outline" size="24px" :custom-class="`wd-curtain__content-close ${closePosition}`" @click="close" />
</view>
</wd-popup>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-curtain',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { ref, watch } from 'vue'
type ClosePosition = 'inset' | 'top' | 'bottom' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
interface Props {
customClass?: string
value: boolean
closePosition?: ClosePosition
src?: string
to?: string
width?: number
closeOnClickModal?: boolean
hideWhenClose?: boolean
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
value: false,
closePosition: 'inset',
closeOnClickModal: false,
hideWhenClose: true
})
const show = ref<boolean>(false)
const imgSucc = ref<boolean>(true)
const imgStyle = ref<string>('')
const imgScale = ref<number>(1)
watch(
() => props.value,
() => {
computedShowImg()
},
{
deep: true,
immediate: true
}
)
watch(
() => props.width,
() => {
computeImgStyle()
},
{
deep: true,
immediate: true
}
)
const emit = defineEmits([
'beforeenter',
'enter',
'afterenter',
'beforeleave',
'leave',
'afterleave',
'close',
'closed',
'click-modal',
'load',
'error',
'click'
])
function computedShowImg() {
if (props.value && imgSucc.value) {
show.value = true
} else {
show.value = false
close()
}
}
function computeImgStyle() {
let style = ''
if (props.width) {
style += `width: ${props.width}px ;`
style += `height: ${props.width / imgScale.value}px`
}
imgStyle.value = style
}
function beforeenter() {
emit('beforeenter')
}
function enter() {
emit('enter')
}
function afterenter() {
emit('afterenter')
}
function beforeleave() {
emit('beforeleave')
}
function leave() {
emit('leave')
}
function afterleave() {
emit('afterleave')
emit('closed')
}
function close() {
show.value = false
emit('close')
}
function clickModal() {
emit('click-modal')
}
function imgLoad(event) {
const { height, width } = event.detail
imgScale.value = width / height
imgSucc.value = true
computeImgStyle()
emit('load')
}
function imgErr() {
imgSucc.value = false
emit('error')
}
function clickImage() {
if (props.to) {
uni.navigateTo({
url: props.to
})
}
emit('click')
close()
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,24 @@
export type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime'
/**
* @description 便 pickerView
* @param value
* @param type picker
* @return {Array} pickerValue
*/
export function getPickerValue(value, type) {
const values: number[] = []
const date = new Date(value)
if (type === 'time') {
const pair = value.split(':')
values.push(parseInt(pair[0]), parseInt(pair[1]))
} else {
values.push(date.getFullYear(), date.getMonth() + 1)
if (type === 'date') {
values.push(date.getDate())
} else if (type === 'datetime') {
values.push(date.getDate(), date.getHours(), date.getMinutes())
}
}
return values
}

@ -0,0 +1,562 @@
<template>
<view>
<wd-picker-view
:custom-class="customClass"
ref="datePickerview"
v-model="pickerValue"
:columns="columns"
:columns-height="columnsHeight"
:columnChange="columnChange"
:loading="loading"
:loading-color="loadingColor"
@change="onChange"
@pickstart="onPickStart"
@pickend="onPickEnd"
></wd-picker-view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-datetime-picker-view',
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
</script>
<script lang="ts" setup>
import { getCurrentInstance, onBeforeMount, ref, watch } from 'vue'
import { debounce, getType, isDef, padZero, range } from '../common/util'
import { type DateTimeType, getPickerValue } from './type'
//
/** @description 判断时间戳是否合法 */
const isValidDate = (date) => isDef(date) && !Number.isNaN(date)
/**
* @description 生成n个元素并使用iterator接口进行填充
* @param n
* @param iteratee
* @return {any[]}
*/
const times = (n, iteratee) => {
let index = -1
const length = n < 0 ? 0 : n
const result = Array(length)
while (++index < n) {
result[index] = iteratee(index)
}
return result
}
/**
* @description 获取某年某月有多少天
* @param {Number} year
* @param {Number} month
* @return {Number} day
*/
const getMonthEndDay = (year, month) => {
return 32 - new Date(year, month - 1, 32).getDate()
}
interface Props {
customClass?: string
// type time Date
modelValue: string | number | Date
// PickerViewProps
//
loading?: boolean
loadingColor?: string
//
columnsHeight?: number
// value key
valueKey?: string
// key
labelKey?: string
// PickerViewProps
//
type?: DateTimeType
//
// eslint-disable-next-line @typescript-eslint/ban-types
filter?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
columnFormatter?: Function
// 20(x-10)11
minDate?: number
// 20(x+10)11
maxDate?: number
//
minHour?: number
//
maxHour?: number
//
minMinute?: number
//
maxMinute?: number
startSymbol?: boolean
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
// PickerViewProps
//
loading: false,
loadingColor: '#4D80F0',
//
columnsHeight: 217,
// value key
valueKey: 'value',
// key
labelKey: 'label',
type: 'datetime',
minDate: new Date(new Date().getFullYear() - 10, 0, 1).getTime(),
maxDate: new Date(new Date().getFullYear() + 10, 11, 31).getTime(),
minHour: 0,
maxHour: 23,
minMinute: 0,
maxMinute: 59,
startSymbol: false
})
// pickerview
const datePickerview = ref()
//
const innerValue = ref<null | number>(null)
// pickerViewcolumns
const columns = ref<Array<string | number>>([])
// pickerViewvalue
const pickerValue = ref<string | number | boolean | Array<string | number | boolean>>([])
// created hook
const created = ref<boolean>(false)
const { proxy } = getCurrentInstance() as any
/**
* @description updateValue 防抖函数的占位符
*/
const updateValue = debounce(() => {
// created hookobserver
if (!created.value) return
const val = correctValue(props.modelValue)
const isEqual = val === innerValue.value
if (!isEqual) {
updateColumnValue(val)
} else {
columns.value = updateColumns()
}
}, 50)
watch(
() => props.modelValue,
(val, oldVal) => {
if (val === oldVal) return
// picker
const value = correctValue(val)
updateColumnValue(value)
},
{ deep: true, immediate: true }
)
watch(
() => props.type,
(target) => {
const type = ['date', 'year-month', 'time', 'datetime']
if (type.indexOf(target) === -1) {
console.error(`type must be one of ${type}`)
}
// type
updateValue()
},
{ deep: true, immediate: true }
)
watch(
() => props.filter,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of filter must be Function')
}
updateValue()
},
{ deep: true, immediate: true }
)
watch(
() => props.formatter,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of formatter must be Function')
}
updateValue()
},
{ deep: true, immediate: true }
)
watch(
() => props.columnFormatter,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of columnFormatter must be Function')
}
updateValue()
},
{ deep: true, immediate: true }
)
watch(
[
() => props.minDate,
() => props.maxDate,
() => props.minHour,
() => props.maxHour,
() => props.minMinute,
() => props.minMinute,
() => props.maxMinute
],
() => {
updateValue()
},
{
deep: true,
immediate: true
}
)
onBeforeMount(() => {
// observerrender
created.value = true
const innerValue = correctValue(props.modelValue)
updateColumnValue(innerValue)
})
// onMounted(() => {
// // render
// const innerValue = correctValue(props.modelValue)
// updateColumnValue(innerValue)
// })
const emit = defineEmits(['change', 'pickstart', 'pickend', 'update:modelValue'])
/** pickerView触发change事件同步修改pickerValue */
function onChange({ value }) {
// pickerViewvalue
pickerValue.value = value
// pickerValue => innerValue
const result = updateInnerValue()
emit('update:modelValue', result)
// valuepicker change value date
emit('change', {
value: result,
picker: proxy.$.exposed
})
}
/**
* @description 使用formatter格式化getOriginColumns的结果
* @return {Array<Array<Number>>} 用于传入picker的columns
*/
function updateColumns() {
const { formatter, columnFormatter } = props
if (columnFormatter) {
return columnFormatter(proxy.$.exposed)
} else {
return getOriginColumns().map((column) => {
return column.values.map((value) => {
return {
label: formatter ? formatter(column.type, padZero(value)) : padZero(value),
value
}
})
})
}
}
/**
* 设置数据列
* @param columnList 数据列
*/
function setColumns(columnList) {
columns.value = columnList
}
/**
* @description 根据getRanges得到的范围计算所有的列的数据
* @return {{values: any[], type: String}[]}
*/
function getOriginColumns() {
const { filter } = props
return getRanges().map(({ type, range }) => {
let values = times(range[1] - range[0] + 1, (index) => {
return range[0] + index
})
if (filter) {
values = filter(type, values)
}
return {
type,
values
}
})
}
/**
* @description 根据时间戳生成年月日时分的边界范围
* @return {Array<{type:String,range:Array<Number>}>}
*/
function getRanges() {
if (props.type === 'time') {
return [
{
type: 'hour',
range: [props.minHour, props.maxHour]
},
{
type: 'minute',
range: [props.minMinute, props.maxMinute]
}
]
}
const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = getBoundary('max', innerValue.value)
const { minYear, minDate, minMonth, minHour, minMinute } = getBoundary('min', innerValue.value)
const result = [
{
type: 'year',
range: [minYear, maxYear]
},
{
type: 'month',
range: [minMonth, maxMonth]
},
{
type: 'date',
range: [minDate, maxDate]
},
{
type: 'hour',
range: [minHour, maxHour]
},
{
type: 'minute',
range: [minMinute, maxMinute]
}
]
if (props.type === 'date') result.splice(3, 2)
if (props.type === 'year-month') result.splice(2, 3)
return result
}
/**
* @description 修正时间入参判定是否为规范时间类型
* @param {String | Number} value
* @return {String | Number} innerValue
*/
function correctValue(value) {
const isDateType = props.type !== 'time'
if (isDateType && !isValidDate(value)) {
// Date使
value = props.minDate
} else if (!isDateType && !value) {
// Date使
value = `${padZero(props.minHour)}:00`
}
// typetime
if (!isDateType) {
// Date
let [hour, minute] = value.split(':')
hour = padZero(range(hour, props.minHour, props.maxHour))
minute = padZero(range(minute, props.minMinute, props.maxMinute))
return `${hour}:${minute}`
}
// date type
value = Math.min(Math.max(value, props.minDate), props.maxDate)
return value
}
/**
* @description 根据时间戳计算所有选项的范围
* @param {'min'|'max'} type 类型
* @param {Number} innerValue 时间戳
*/
function getBoundary(type, innerValue) {
const value = new Date(innerValue)
const boundary = new Date(props[`${type}Date`])
const year = boundary.getFullYear()
let month = 1
let date = 1
let hour = 0
let minute = 0
if (type === 'max') {
month = 12
date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1)
hour = 23
minute = 59
}
if (value.getFullYear() === year) {
month = boundary.getMonth() + 1
if (value.getMonth() + 1 === month) {
date = boundary.getDate()
if (value.getDate() === date) {
hour = boundary.getHours()
if (value.getHours() === hour) {
minute = boundary.getMinutes()
}
}
}
}
return {
[`${type}Year`]: year,
[`${type}Month`]: month,
[`${type}Date`]: date,
[`${type}Hour`]: hour,
[`${type}Minute`]: minute
}
}
/**
* @description 根据传入的value以及type初始化innerValue期间会使用format格式化数据
* @param value
* @return {Array}
*/
function updateColumnValue(value) {
const values = getPickerValue(value, props.type)
// pickerViewvalue,columns
if (props.modelValue !== value) {
emit('update:modelValue', value)
emit('change', {
value,
picker: proxy.$.exposed
})
}
innerValue.value = value
columns.value = updateColumns()
pickerValue.value = values
}
/**
* @description 根据当前的选中项 处理innerValue
* @return {date} innerValue
*/
function updateInnerValue() {
const { type } = props
let values: Array<number> = []
let innerValue = ''
values = datePickerview.value.getValues()
if (type === 'time') {
innerValue = `${padZero(values[0])}:${padZero(values[1])}`
return innerValue
}
// 0
const year = values[0] && parseInt(String(values[0]))
// 1
const month = values[1] && parseInt(String(values[1]))
const maxDate = getMonthEndDay(year, month)
// date 2
let date: string | number = 1
if (type !== 'year-month') {
date = (values[2] && parseInt(String(values[2]))) > maxDate ? maxDate : values[2] && parseInt(String(values[2]))
}
// 34
let hour = 0
let minute = 0
if (type === 'datetime') {
hour = values[3] && parseInt(String(values[3]))
minute = values[4] && parseInt(String(values[4]))
}
const value = new Date(year, month - 1, date, hour, minute)
innerValue = correctValue(value)
return innerValue
}
/**
* @description 选中项改变多级联动
*/
function columnChange(picker) {
// time year-mouth
if (props.type === 'time' || props.type === 'year-month') {
return
}
/** 重新计算年月日时分秒,修正时间。 */
const values = picker.getValues()
const year = values[0]
const month = values[1]
const maxDate = getMonthEndDay(year, month)
let date = values[2]
date = date > maxDate ? maxDate : date
let hour = 0
let minute = 0
if (props.type === 'datetime') {
hour = values[3]
minute = values[4]
}
const value = new Date(year, month - 1, date, hour, minute)
/** 根据计算选中项的时间戳,重新计算所有的选项列表 */
//
innerValue.value = correctValue(value)
// innerValue
const newColumns = updateColumns().slice(0, 3)
//
const selectedIndex = picker.selectedIndex.value.slice(0)
/**
* 选中年会修改对应的年份的月数和月份对应的日期
* 选中月会修改月份对应的日数
*/
newColumns.forEach((columns, index) => {
const nextColumnIndex = index + 1
const nextColumnData = newColumns[nextColumnIndex]
// ``
if (index === 2) return
picker.setColumnData(
nextColumnIndex,
nextColumnData,
selectedIndex[nextColumnIndex] <= nextColumnData.length - 1 ? selectedIndex[nextColumnIndex] : 0
)
})
}
function onPickStart() {
emit('pickstart')
}
function onPickEnd() {
emit('pickend')
}
function getSelects() {
return datePickerview.value && datePickerview.value.getSelects ? datePickerview.value.getSelects() : undefined
}
defineExpose({
updateColumns,
setColumns,
getSelects,
correctValue,
getPickerValue,
getOriginColumns,
formatter: props.formatter,
startSymbol: props.startSymbol
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,241 @@
@import "./../common/abstracts/_mixin.scss";
@import "./../common/abstracts/variable.scss";
.wot-theme-dark {
@include b(picker) {
@include e(cell) {
background-color: $-dark-background2;
color: $-dark-color;
}
@include e(label) {
color: $-dark-color;
}
@include e(value) {
color: $-dark-color;
}
@include e(placeholder) {
color: $-dark-color-gray;
}
@include when(border) {
.wd-picker__cell {
@include halfPixelBorder('top', $-cell-padding, $-dark-border-color);
}
}
:deep(.wd-picker__arrow){
color: $-dark-color;
}
@include e(action) {
@include m(cancel) {
color: $-dark-color;
}
}
@include e(region) {
color: $-dark-color;
@include when(active) {
background: $-picker-region-bg-active-color;
color: $-dark-color;
}
}
}
}
@include b(picker) {
@include edeep(popup) {
border-radius: 16px 16px 0px 0px;
}
@include e(wraper) {
padding-bottom: var(--window-bottom);
}
@include when(border) {
.wd-picker__cell {
@include halfPixelBorder("top", $-cell-padding);
}
}
@include when(large) {
.wd-picker__cell {
font-size: $-cell-title-fs-large;
}
:deep(.wd-picker__arrow) {
font-size: $-cell-icon-size-large;
}
}
@include when(error) {
.wd-picker__value,
.wd-picker__placeholder {
color: $-input-error-color;
}
:deep(.wd-picker__arrow){
color: $-input-error-color;
}
}
@include when(align-right) {
.wd-picker__value {
text-align: right;
}
}
@include e(cell) {
position: relative;
display: flex;
padding: $-cell-wrapper-padding $-cell-padding;
align-items: flex-start;
background-color: $-color-white;
text-decoration: none;
color: $-cell-title-color;
font-size: $-cell-title-fs;
overflow: hidden;
line-height: $-cell-line-height;
@include when(disabled) {
.wd-picker__value {
color: $-input-disabled-color;
}
}
}
@include when(disabled) {
.wd-picker__value {
color: $-picker-column-disabled-color;
}
}
@include e(error-message){
color: $-form-item-error-message-color;
font-size: $-form-item-error-message-font-size;
line-height: $-form-item-error-message-line-height;
text-align: left;
vertical-align: middle;
}
@include e(label) {
position: relative;
width: $-input-cell-label-width;
margin-right: $-cell-padding;
color: $-cell-title-color;
box-sizing: border-box;
@include when(required) {
padding-left: 12px;
&::after {
position: absolute;
left: 0;
top: 2px;
content: "*";
font-size: $-cell-required-size;
line-height: 1.1;
color: $-cell-required-color;
}
}
}
@include e(value-wraper) {
display: flex;
}
@include e(value) {
flex: 1;
margin-right: 10px;
color: $-cell-value-color;
@include when(ellipsis) {
@include lineEllipsis;
}
}
@include e(body) {
flex: 1;
}
@include e(placeholder) {
color: $-input-placeholder-color;
}
@include edeep(arrow) {
display: block;
font-size: $-cell-icon-size;
color: $-cell-arrow-color;
line-height: $-cell-line-height;
}
@include e(toolbar) {
position: relative;
display: flex;
font-size: $-picker-toolbar-fs;
height: $-picker-toolbar-height;
line-height: $-picker-action-height;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
}
@include e(action) {
display: block;
border: none;
outline: none;
font-size: $-picker-toolbar-fs;
color: $-picker-toolbar-finish-color;
background: transparent;
padding: 24px 15px 14px 15px;
@include m(cancel) {
color: $-picker-toolbar-cancel-color;
}
@include when(loading) {
color: $-picker-loading-button-color;
}
}
@include e(title) {
display: block;
float: 1;
color: $-picker-toolbar-title-color;
}
@include e(region-tabs) {
display: flex;
}
@include e(region) {
width: 50%;
display: inline-block;
color: $-picker-region-color;
text-align: center;
padding: 14px 0;
font-size: $-picker-region-fs;
line-height: 16px;
transition: all 0.15s ease-out;
@include when(active) {
background: $-picker-region-bg-active-color;
color: $-color-white;
}
}
@include e(region-time) {
font-size: 16px;
margin-top: 2px;
}
@include e(hidden) {
visibility: hidden;
overflow: hidden;
height: 0;
}
@include e(show) {
visibility: visible;
height: auto;
}
}

@ -0,0 +1,849 @@
<template>
<view
:class="`wd-picker ${disabled ? 'is-disabled' : ''} ${size ? 'is-' + size : ''} ${cell.border.value ? 'is-border' : ''} ${
alignRight ? 'is-align-right' : ''
} ${error ? 'is-error' : ''} ${customClass}`"
>
<!--文案-->
<view class="wd-picker__field" @click="showPopup">
<slot v-if="useDefaultSlot"></slot>
<view v-else class="wd-picker__cell">
<view
v-if="label || useLabelSlot"
:class="`wd-picker__label ${customLabelClass} ${isRequired ? 'is-required' : ''}`"
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
>
<block v-if="label">{{ label }}</block>
<slot v-else name="label"></slot>
</view>
<view class="wd-picker__body">
<view class="wd-picker__value-wraper">
<view :class="`wd-picker__value ${customValueClass}`">
<view v-if="region">
<text :class="showValue[0] ? '' : 'wd-picker__placeholder'">{{ showValue[0] ? showValue[0] : placeholder }}</text>
<text :class="showValue[1] ? '' : 'wd-picker__placeholder'">{{ showValue[1] ? showValue[1] : placeholder }}</text>
</view>
<view v-else :class="showValue ? '' : 'wd-picker__placeholder'">
{{ showValue ? showValue : placeholder }}
</view>
</view>
<wd-icon v-if="!disabled && !readonly" custom-class="wd-picker__arrow" name="arrow-right" />
</view>
<view v-if="errorMessage" class="wd-picker__error-message">{{ errorMessage }}</view>
</view>
</view>
</view>
<!--弹出层picker-view 在隐藏时修改值会触发多次change事件从而导致所有列选中第一项因此picker在关闭时不隐藏 -->
<wd-popup
v-model="popupShow"
position="bottom"
:hide-when-close="false"
:close-on-click-modal="closeOnClickModal"
:safe-area-inset-bottom="safeAreaInsetBottom"
:z-index="zIndex"
@close="onCancel"
custom-class="wd-picker__popup"
>
<view class="wd-picker__wraper">
<!--toolBar-->
<view class="wd-picker__toolbar" @touchmove="noop">
<!--取消按钮-->
<view class="wd-picker__action wd-picker__action--cancel" @click="onCancel">
{{ cancelButtonText }}
</view>
<!--标题-->
<view v-if="title" class="wd-picker__title">{{ title }}</view>
<!--确定按钮-->
<view :class="`wd-picker__action ${loading || isLoading ? 'is-loading' : ''}`" @click="onConfirm">
{{ confirmButtonText }}
</view>
</view>
<!-- 区域选择tab展示 -->
<view v-if="region" class="wd-picker__region-tabs">
<view :class="`wd-picker__region ${showStart ? 'is-active' : ''} `" @click="tabChange">
<view>开始时间</view>
<view class="wd-picker__region-time">{{ showTabLabel[0] }}</view>
</view>
<view :class="`wd-picker__region ${showStart ? '' : 'is-active'}`" @click="tabChange">
<view>结束时间</view>
<view class="wd-picker__region-time">{{ showTabLabel[1] }}</view>
</view>
</view>
<!--datetimePickerView-->
<view :class="showStart ? 'wd-picker__show' : 'wd-picker__hidden'">
<wd-datetime-picker-view
:custom-class="customViewClass"
ref="datetimePickerView"
:type="type"
v-model="innerValue"
:loading="loading || isLoading"
:loading-color="loadingColor"
:columns-height="columnsHeight"
:value-key="valueKey"
:label-key="labelKey"
:formatter="formatter"
:filter="filter"
:column-formatter="getType(modelValue) === 'array' ? customColumnFormatter : undefined"
:max-hour="maxHour"
:min-hour="minHour"
:max-date="maxDate"
:min-date="minDate"
:max-minute="maxMinute"
:min-minute="minMinute"
:start-symbol="true"
@change="onChangeStart"
@pickstart="onPickStart"
@pickend="onPickEnd"
/>
</view>
<view :class="showStart ? 'wd-picker__hidden' : 'wd-picker__show'">
<wd-datetime-picker-view
:custom-class="customViewClass"
ref="datetimePickerView1"
:type="type"
v-model="endInnerValue"
:loading="loading || isLoading"
:loading-color="loadingColor"
:columns-height="columnsHeight"
:value-key="valueKey"
:label-key="labelKey"
:formatter="formatter"
:filter="filter"
:column-formatter="getType(modelValue) === 'array' ? customColumnFormatter : undefined"
:max-hour="maxHour"
:min-hour="minHour"
:max-date="maxDate"
:min-date="minDate"
:max-minute="maxMinute"
:min-minute="minMinute"
:start-symbol="false"
@change="onChangeEnd"
@pickstart="onPickStart"
@pickend="onPickEnd"
/>
</view>
</view>
</wd-popup>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-datetime-picker',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, ref, watch } from 'vue'
import { deepClone, getType, isArray, isDef, isEqual, padZero } from '../common/util'
import { useCell } from '../composables/useCell'
import { type DateTimeType, getPickerValue } from '../wd-datetime-picker-view/type'
import { FORM_KEY, FormItemRule } from '../wd-form/types'
import { useParent } from '../composables/useParent'
interface Props {
customClass?: string
customViewClass?: string
customLabelClass?: string
customValueClass?: string
//
label?: string
//
placeholder?: string
//
disabled?: boolean
//
readonly?: boolean
loading?: boolean
loadingColor?: string
/* popup */
//
title?: string
//
cancelButtonText?: string
//
confirmButtonText?: string
//
required?: boolean
size?: string
labelWidth?: string
useDefaultSlot?: boolean
useLabelSlot?: boolean
error?: boolean
alignRight?: boolean
closeOnClickModal?: boolean
safeAreaInsetBottom?: boolean
ellipsis?: boolean
//
columnsHeight?: number
// value key
valueKey?: string
// key
labelKey?: string
// type time
modelValue: string | number | Date | Array<string | number | Date>
//
type?: DateTimeType
// 20(x-10)11
minDate?: number
// 20(x+10)11
maxDate?: number
//
minHour?: number
//
maxHour?: number
//
minMinute?: number
//
maxMinute?: number
//
// eslint-disable-next-line @typescript-eslint/ban-types
filter?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
formatter?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
displayFormat?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
beforeConfirm?: Function
//
// eslint-disable-next-line @typescript-eslint/ban-types
displayFormatTabLabel?: Function
defaultValue?: string | number | Date | Array<string | number | Date>
zIndex?: number
prop?: string
rules?: FormItemRule[]
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customViewClass: '',
customLabelClass: '',
customValueClass: '',
//
placeholder: '请选择',
//
disabled: false,
//
readonly: false,
loading: false,
loadingColor: '#4D80F0',
/* popup */
//
cancelButtonText: '取消',
//
confirmButtonText: '完成',
//
required: false,
labelWidth: '33%',
useDefaultSlot: false,
useLabelSlot: false,
error: false,
alignRight: false,
closeOnClickModal: true,
safeAreaInsetBottom: true,
ellipsis: false,
//
columnsHeight: 217,
// value key
valueKey: 'value',
// key
labelKey: 'label',
//
type: 'datetime',
// 20(x-10)11
minDate: new Date(new Date().getFullYear() - 10, 0, 1).getTime(),
// 20(x+10)11
maxDate: new Date(new Date().getFullYear() + 10, 11, 31).getTime(),
//
minHour: 0,
//
maxHour: 23,
//
minMinute: 0,
//
maxMinute: 59,
zIndex: 15,
rules: () => []
})
const datetimePickerView = ref()
const datetimePickerView1 = ref()
const showValue = ref<string | Date | Array<string | Date>>([])
const popupShow = ref<boolean>(false)
const showStart = ref<boolean>(true)
const region = ref<boolean>(false)
const showTabLabel = ref<string[]>([])
const innerValue = ref<string>('')
const endInnerValue = ref<string>('')
const isPicking = ref<boolean>(false) // pickview
const hasConfirmed = ref<boolean>(false) //
const isLoading = ref<boolean>(false) //
const { proxy } = getCurrentInstance() as any
const cell = useCell()
watch(
() => props.modelValue,
(val, oldVal) => {
if (isEqual(val, oldVal)) return
if (getType(val) === 'array') {
region.value = true
innerValue.value = deepClone(getDefaultInnerValue(true))
endInnerValue.value = deepClone(getDefaultInnerValue(true, true))
} else {
// value
innerValue.value = deepClone(getDefaultInnerValue())
}
nextTick(() => {
setShowValue(false, false, true)
})
},
{
deep: true,
immediate: true
}
)
watch(
() => props.displayFormat,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of displayFormat must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.filter,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of filter must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.formatter,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of formatter must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.beforeConfirm,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of beforeConfirm must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.displayFormatTabLabel,
(fn) => {
if (fn && getType(fn) !== 'function') {
console.error('The type of displayFormatTabLabel must be Function')
}
},
{
deep: true,
immediate: true
}
)
watch(
() => props.defaultValue,
(val) => {
if (getType(val) === 'array' || region.value) {
innerValue.value = deepClone(getDefaultInnerValue(true))
endInnerValue.value = deepClone(getDefaultInnerValue(true, true))
} else {
innerValue.value = deepClone(getDefaultInnerValue())
}
},
{
deep: true,
immediate: true
}
)
const { parent: form } = useParent(FORM_KEY)
//
const errorMessage = computed(() => {
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
return form.errorMessages[props.prop]
} else {
return ''
}
})
//
const isRequired = computed(() => {
let formRequired = false
if (form && form.rules) {
const rules = form.rules
for (const key in rules) {
if (Object.prototype.hasOwnProperty.call(rules, key) && key === props.prop && Array.isArray(rules[key])) {
formRequired = rules[key].some((rule: FormItemRule) => rule.required)
}
}
}
return props.required || props.rules.some((rule) => rule.required) || formRequired
})
const emit = defineEmits(['change', 'open', 'toggle', 'cancel', 'confirm', 'update:modelValue'])
/**
* @description 自定义列项筛选规则对每列单项进行禁用校验最终返回传入PickerView的columns数组
* @param {Component} picker datetimePickerView对象
* @return {Array} columns
*/
const customColumnFormatter = (picker) => {
if (!picker) {
return
}
const { type } = props
const { startSymbol, formatter } = picker
// pickervalueinnerValue
const start = picker.correctValue(innerValue.value)
const end = picker.correctValue(endInnerValue.value)
/**
* 如果是上方picekr 那么将下方picker的值作为下边界
* 如果是下方picekr 那么将上方picker的值作为上边界
*/
const currentValue = startSymbol ? picker.getPickerValue(start, type) : picker.getPickerValue(end, type)
const boundary = startSymbol ? picker.getPickerValue(end, type) : picker.getPickerValue(start, type)
// picekr
const columns = picker.getOriginColumns()
const mapColumns = (columns) => {
// index
return columns.map((column, cIndex) => {
return column.values.map((value, index) => {
const disabled = columnDisabledRules(startSymbol, columns, cIndex, value, currentValue, boundary)
return {
label: formatter ? formatter(column.type, padZero(value)) : padZero(value),
value,
disabled
}
})
})
}
return mapColumns(columns)
}
onBeforeMount(() => {
const { modelValue: value } = props
if (getType(value) === 'array') {
region.value = true
innerValue.value = deepClone(getDefaultInnerValue(true))
endInnerValue.value = deepClone(getDefaultInnerValue(true, true))
} else {
innerValue.value = deepClone(getDefaultInnerValue())
}
})
onMounted(() => {
setShowValue(false, false, true)
})
/**
* @description 根据传入的pickerpicker组件获取当前cell展示值
*/
function getSelects(picker: 'before' | 'after') {
let value = picker === 'before' ? innerValue.value : endInnerValue.value
let selected: number[] = []
if (value) {
selected = getPickerValue(value, props.type)
}
let selects = selected.map((value) => {
return {
[props.labelKey]: padZero(value),
[props.valueKey]: value
}
})
return selects
}
function noop() {}
function getDefaultInnerValue(isRegion?: boolean, isEnd?: boolean) {
const { modelValue: value, defaultValue } = props
if (isRegion) {
if (isEnd) {
return value[1] || (defaultValue && isArray(defaultValue) ? defaultValue[1] : '')
} else {
return value[0] || (defaultValue && isArray(defaultValue) ? defaultValue[0] : '')
}
} else {
return isDef(value || defaultValue) ? value || defaultValue : ''
}
}
//
function open() {
showPopup()
}
//
function close() {
onCancel()
}
/**
* @description 展示popup小程序有个bug在picker-view弹出时设置value会触发change事件而且会将picker-view的value多次触发change重置为第一项
*/
function showPopup() {
if (props.disabled || props.readonly) return
emit('open')
if (region.value) {
popupShow.value = true
showStart.value = true
innerValue.value = deepClone(getDefaultInnerValue(true, false))
endInnerValue.value = deepClone(getDefaultInnerValue(true, true))
} else {
popupShow.value = true
innerValue.value = deepClone(getDefaultInnerValue())
}
setShowValue(true, false, true)
}
/**
* @description 区域选择时tab标签切换时触发
*/
function tabChange() {
showStart.value = !showStart.value
// datetimepickerView
const picker = showStart.value ? datetimePickerView.value : datetimePickerView1.value
picker.setColumns(picker.updateColumns())
emit('toggle', showStart.value ? innerValue.value : endInnerValue.value)
}
/**
* @description datetimePickerView change 事件
*/
function onChangeStart({ value }) {
innerValue.value = deepClone(value)
if (region.value) {
showTabLabel.value = [setTabLabel(), deepClone(showTabLabel.value[1])]
emit('change', {
value: [value, endInnerValue.value]
})
datetimePickerView.value && datetimePickerView.value.setColumns(datetimePickerView.value.updateColumns())
datetimePickerView1.value && datetimePickerView1.value.setColumns(datetimePickerView1.value.updateColumns())
} else {
// emit('update:modelValue', innerValue.value)
emit('change', {
value: innerValue.value
})
}
}
/**
* @description 区域选择 下方 datetimePickerView change 事件
*/
function onChangeEnd({ value }) {
endInnerValue.value = deepClone(value)
showTabLabel.value = [deepClone(showTabLabel.value[0]), setTabLabel(1)]
// emit('update:modelValue', [innerValue.value, value])
emit('change', {
value: [innerValue.value, value]
})
datetimePickerView.value && datetimePickerView.value.setColumns(datetimePickerView.value.updateColumns())
datetimePickerView1.value && datetimePickerView1.value.setColumns(datetimePickerView1.value.updateColumns())
}
/**
* @description 点击取消按钮触发关闭popup触发cancel事件
*/
function onCancel() {
popupShow.value = false
setTimeout(() => {
if (region.value) {
innerValue.value = deepClone(getDefaultInnerValue(true))
endInnerValue.value = deepClone(getDefaultInnerValue(true, true))
} else {
innerValue.value = deepClone(getDefaultInnerValue())
}
}, 200)
emit('cancel')
}
/** picker触发confirm事件同步触发confirm事件 */
function onConfirm() {
if (props.loading || isLoading.value) return
// pickview
if (isPicking.value) {
hasConfirmed.value = true
return
}
const { beforeConfirm } = props
if (beforeConfirm) {
beforeConfirm(
region.value ? [innerValue.value, endInnerValue.value] : innerValue.value,
(isPass: boolean) => {
isPass && handleConfirm()
},
proxy.$.exposed
)
} else {
handleConfirm()
}
}
function onPickStart() {
isPicking.value = true
}
function onPickEnd() {
isPicking.value = false
//
setTimeout(() => {
if (hasConfirmed.value) {
hasConfirmed.value = false
onConfirm()
}
}, 50)
}
function handleConfirm() {
if (props.loading || isLoading.value || props.disabled) {
popupShow.value = false
return
}
const value = region.value ? [innerValue.value, endInnerValue.value] : innerValue.value
popupShow.value = false
emit('update:modelValue', value)
emit('confirm', {
value
})
setShowValue(false, true)
}
/**
* @description 设置区域选择 tab 标签展示值
* @param {Number} index 索引标志位有三个有效值; 0(默认):上方picker索引; 1:下方picker索引;
* @return {String} showTabLabel
*/
function setTabLabel(index: number = 0) {
if (region.value) {
let items = []
if (index === 0) {
items =
(datetimePickerView.value && datetimePickerView.value.getSelects && datetimePickerView.value.getSelects()) ||
(innerValue.value && getSelects('before'))
} else {
items =
(datetimePickerView1.value && datetimePickerView1.value.getSelects && datetimePickerView1.value.getSelects()) ||
(endInnerValue.value && getSelects('after'))
}
return defaultDisplayFormat(items, true)
}
}
/**
* @description 设置展示值
* @param {Boolean} tab 是否修改tab展示值尽在区域选择情况下生效
* @param {Boolean} isConfirm 是否提交当前修改
*/
function setShowValue(tab: boolean = false, isConfirm: boolean = false, beforeMount: boolean = false) {
if (region.value) {
const items = beforeMount
? (innerValue.value && getSelects('before')) || []
: (datetimePickerView.value && datetimePickerView.value.getSelects && datetimePickerView.value.getSelects()) || []
const endItems = beforeMount
? (endInnerValue.value && getSelects('after')) || []
: (datetimePickerView1.value && datetimePickerView1.value.getSelects && datetimePickerView1.value.getSelects()) || []
showValue.value = tab
? showValue.value
: [props.modelValue[0] || isConfirm ? defaultDisplayFormat(items) : '', props.modelValue[1] || isConfirm ? defaultDisplayFormat(endItems) : '']
showTabLabel.value = [defaultDisplayFormat(items, true), defaultDisplayFormat(endItems, true)]
} else {
const items = beforeMount
? (innerValue.value && getSelects('before')) || []
: (datetimePickerView.value && datetimePickerView.value.getSelects && datetimePickerView.value.getSelects()) || []
showValue.value = deepClone(props.modelValue || isConfirm ? defaultDisplayFormat(items) : '')
}
}
/**
* @description 设置展示值
* @param {Object} items 获取到的选中项 包含 { value, label }
* @param {Boolean} tabLabel 当前返回的是否是展示tab上的标签
* @return {String} showValue / showTabLabel
*/
function defaultDisplayFormat(items, tabLabel = false) {
if (items.length === 0) return ''
if (tabLabel && props.displayFormatTabLabel) {
return props.displayFormatTabLabel(items)
}
if (props.displayFormat) {
return props.displayFormat(items)
}
// 使formatterdefaultDisplayFormat
if (props.formatter) {
/**
* 不建议使用 this.picker.picker.getLabels() 拉取
* 在初始展示时需要使用模拟 nextTick 来等待内部 pickerView 渲染后labels才可得到format后的labels
* 但使用模拟nextTick会造成页面延迟展示问题对用户感知来讲不友好因此不适用该方法
*/
const map = (items) => {
const typeMaps = {
datetime: ['year', 'month', 'date', 'hour', 'minute'],
date: ['year', 'month', 'date'],
time: ['hour', 'minute'],
'year-month': ['year', 'month']
}
return items.map((item, index) => {
return props.formatter!(typeMaps[props.type][index], item.value)
})
}
return map(items).join('')
}
switch (props.type) {
case 'date':
return `${items[0].label}-${items[1].label}-${items[2].label}`
case 'year-month':
return `${items[0].label}-${items[1].label}`
case 'time':
return `${items[0].label}:${items[1].label}`
case 'datetime':
return `${items[0].label}-${items[1].label}-${items[2].label} ${items[3].label}:${items[4].label}`
}
}
/**
* @description 区域选择time禁用规则根据传入的位置标志以及日期类型 返回该节点是否禁用
* @param {String} isStart 时间段类型 truestart | falseend
* @param {Array} column 当前遍历到的列数组
* @param {Number} cindex 外层column的索引对应每一个类型
* @param {Number / String} value 遍历到的当前值
* @param {Array} currentValue 当前选中的值 this.pickerValue
* @param {Array} boundary 当前变量的限制值决定禁用的边界值
* @return {Boolean} disabled
*/
function columnDisabledRules(isStart, columns, cIndex, value, currentValue, boundary) {
const { type } = props
// 0 1 2 3 4
// startPicker , endPicker , startPicker boundary min->boundary
// endPicker , startPicker , endPicker boundary boundary->max
const column = columns[cIndex]
// ranges[0][0] minYear ranges[0][1] maxYear
if (type === 'datetime') {
const year = boundary[0]
const month = boundary[1]
const date = boundary[2]
const hour = boundary[3]
const minute = boundary[4]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
if (column.type === 'month' && currentValue[0] === year) {
return isStart ? value > month : value < month
}
if (column.type === 'date' && currentValue[0] === year && currentValue[1] === month) {
return isStart ? value > date : value < date
}
if (column.type === 'hour' && currentValue[0] === year && currentValue[1] === month && currentValue[2] === date) {
return isStart ? value > hour : value < hour
}
if (column.type === 'minute' && currentValue[0] === year && currentValue[1] === month && currentValue[2] === date && currentValue[3] === hour) {
return isStart ? value > minute : value < minute
}
} else if (type === 'year-month') {
const year = boundary[0]
const month = boundary[1]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
if (column.type === 'month' && currentValue[0] === year) {
return isStart ? value > month : value < month
}
} else if (type === 'date') {
const year = boundary[0]
const month = boundary[1]
const date = boundary[2]
if (column.type === 'year') {
return isStart ? value > year : value < year
}
if (column.type === 'month' && currentValue[0] === year) {
return isStart ? value > month : value < month
}
if (column.type === 'date' && currentValue[0] === year && currentValue[1] === month) {
return isStart ? value > date : value < date
}
} else if (type === 'time') {
const hour = boundary[0]
const minute = boundary[1]
if (column.type === 'hour') {
return isStart ? value > hour : value < hour
}
if (column.type === 'minute' && currentValue[0] === hour) {
return isStart ? value > minute : value < minute
}
}
return false
}
function setLoading(loading) {
isLoading.value = loading
}
defineExpose({
open,
close,
setLoading
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,32 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(divider) {
color: $-dark-color3;
@include e(line) {
background: $-dark-color-gray;
}
}
}
@include b(divider) {
position: relative;
display: flex;
padding: $-divider-padding;
align-items: center;
color: $-divider-color;
font-size: $-divider-fs;
@include e(line) {
display: block;
flex: 1;
height: 1px;
transform: scaleY(0.5);
background: $-divider-line-color;
}
@include e(content) {
padding: 0 12px;
}
}

@ -0,0 +1,35 @@
<template>
<view :class="`wd-divider ${customClass}`">
<view class="wd-divider__line" :style="color ? 'background: ' + color : ''"></view>
<view class="wd-divider__content" :style="color ? 'color: ' + color : ''">
<slot></slot>
</view>
<view class="wd-divider__line" :style="color ? 'background: ' + color : ''"></view>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-divider',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
interface Props {
customClass?: string
color?: string
}
const props = withDefaults(defineProps<Props>(), {
color: '',
customClass: ''
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,66 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(drop-item) {
color: $-dark-color;
@include e(tip) {
color: $-dark-color3;
}
}
}
@include b(drop-item) {
position: fixed;
right: 0;
left: 0;
overflow: hidden;
font-size: $-drop-menu-item-fs;
color: $-drop-menu-item-color;
width: 100%;
z-index: 101;
.wd-drop-item__popup {
position: absolute;
max-height: 80%;
}
@include e(option) {
display: flex;
height: $-drop-menu-item-height;
line-height: $-drop-menu-item-height;
padding: 0 $-drop-menu-side-padding;
justify-content: space-between;
align-items: center;
transition: color .2s;
@include when(active) {
color: $-drop-menu-item-color-active;
}
}
@include e(title){
display: block;
}
@include e(tip) {
display: inline-block;
color: $-drop-menu-item-color-tip;
font-size: $-drop-menu-item-fs-tip;
margin-left: 2px;
}
@include edeep(icon){
display: block;
font-size: $-drop-menu-option-check-size;
}
@include e(modal) {
position: fixed;
left: 0;
right: 0;
background: rgba(0, 0, 0, 0.7);
height: 100%;
}
}

@ -0,0 +1,244 @@
<template>
<view v-if="showWrapper" :class="`wd-drop-item ${customClass}`" :style="`z-index: ${zIndex}; ${positionStyle}`">
<wd-popup
v-model="showPop"
:z-index="zIndex"
:duration="duration"
:position="position"
custom-style="position: absolute; max-height: 80%;"
modal-style="position: absolute;"
:modal="modal"
:close-on-click-modal="closeOnClickModal"
@click-modal="close"
@before-enter="handleOpen"
@after-enter="handleOpened"
@before-leave="handleClose"
@after-leave="onPopupClose"
>
<view v-if="options.length">
<view
v-for="(item, index) in options"
:key="index"
@click="choose(index)"
:class="`wd-drop-item__option ${(item[valueKey] !== '' ? item[valueKey] : item) === modelValue ? 'is-active' : ''}`"
>
<view :class="`wd-drop-item__title ${customTitle}`">
<text>{{ item[labelKey] ? item[labelKey] : item }}</text>
<text v-if="item[tipKey]" class="wd-drop-item__tip">{{ item[tipKey] }}</text>
</view>
<wd-icon
v-if="(item[valueKey] !== '' ? item[valueKey] : item) === modelValue"
:name="iconName"
size="20px"
:class="`wd-drop-item__icon ${customIcon}`"
/>
</view>
</view>
<slot v-else />
</wd-popup>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-drop-menu-item',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, getCurrentInstance, inject, onBeforeMount, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { pushToQueue, removeFromQueue } from '../common/clickoutside'
import { type Queue, queueKey } from '../composables/useQueue'
import { debounce } from '../common/util'
import type { PopupType } from '../wd-popup/type'
interface Props {
customClass?: string
customTitle?: string
customIcon?: string
// value
modelValue?: string | number
// array || String
options?: Array<Record<string, any>>
useDropItemSlot?: boolean
disabled?: boolean
iconName?: string
title?: string
valueKey?: string
labelKey?: string
tipKey?: string
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customTitle: '',
customIcon: '',
options: () => [],
useDropItemSlot: false,
disabled: false,
iconName: 'check',
valueKey: 'value',
labelKey: 'label',
tipKey: 'tip'
})
const queue = inject<Queue | null>(queueKey, null)
const showWrapper = ref<boolean>(false)
const showPop = ref<boolean>(false)
const position = ref<PopupType>()
const transName = ref<string>('')
const zIndex = ref<number>(12)
const displayTitle = ref<string>('')
const modal = ref<boolean>(true)
const closeOnClickModal = ref<boolean>(true)
const duration = ref<number>(0)
const parent = inject<any>('dropMenu')
const { proxy } = getCurrentInstance() as any
const updateTitle = debounce(function () {
setDisplayTitle()
parent && parent.$.exposed.updateTitle && parent.$.exposed.updateTitle()
}, 50)
watch(
() => props.modelValue,
(newValue) => {
if (typeof newValue !== 'number' && typeof newValue !== 'string') {
console.error('[wot-design] warning(wd-drop-menu-item): the type of value should be a number or a string.')
return
}
updateTitle()
},
{
deep: true,
immediate: true
}
)
watch(
[() => props.options, () => props.title],
() => {
updateTitle()
},
{
deep: true,
immediate: true
}
)
onBeforeMount(() => {
if (queue && queue.pushToQueue) {
queue.pushToQueue(proxy)
} else {
pushToQueue(proxy)
}
if (parent) {
parent.$.exposed.setChild && parent.$.exposed.setChild(proxy)
updateTitle()
}
})
onBeforeUnmount(() => {
if (queue && queue.removeFromQueue) {
queue.removeFromQueue(proxy)
} else {
removeFromQueue(proxy)
}
})
onMounted(() => {
setDisplayTitle()
})
const emit = defineEmits(['change', 'update:modelValue', 'open', 'opened', 'closed', 'close'])
function setDisplayTitle() {
const { title, modelValue, options, valueKey, labelKey } = props
if (title) {
displayTitle.value = title
return
}
for (let i = 0, len = options.length; i < len; i++) {
if (modelValue === options[i][valueKey]) {
displayTitle.value = options[i][labelKey]
return
}
}
console.error('[wot-design] warning(wd-drop-menu-item): no value is matched in the options option.')
}
/**
* 父组件更改子组件内部
* @param show
*/
function setShowPop(show: boolean) {
showPop.value = show
}
// value
function choose(index: number) {
if (props.disabled) return
const { valueKey } = props
const item = props.options[index]
emit('update:modelValue', item[valueKey] !== '' && item[valueKey] !== undefined ? item[valueKey] : item)
close()
emit('change', {
value: item[valueKey] !== '' && item[valueKey] !== undefined ? item[valueKey] : item,
selectedItem: item
})
parent.$.exposed.updateTitle()
}
//
function close() {
showPop.value = false
parent.$.exposed.fold()
}
const positionStyle = computed(() => {
let style: string = ''
if (showPop.value) {
style =
parent.direction === 'down'
? `top: calc(var(--window-top) + ${parent.$.exposed.offset.value}px); bottom: 0;`
: `top: 0; bottom: calc(var(--window-bottom) + ${parent.$.exposed.offset.value}px)`
} else {
style = ''
}
return style
})
function open() {
showWrapper.value = true
showPop.value = true
modal.value = parent.modal
duration.value = parent.duration
closeOnClickModal.value = parent.closeOnClickModal
position.value = parent.direction === 'down' ? 'top' : 'bottom'
emit('open')
}
function onPopupClose() {
showWrapper.value = false
emit('closed')
}
function handleOpen() {
emit('open')
}
function handleOpened() {
emit('opened')
}
function handleClose() {
emit('close')
}
defineExpose({ setShowPop, open, close, displayTitle })
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,88 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(drop-menu) {
color: $-dark-color;
@include e(list) {
background-color: $-dark-background2;
}
@include e(item) {
@include when(disabled) {
color: $-dark-color-gray;
}
}
}
}
@include b(drop-menu) {
box-sizing: border-box;
color: $-drop-menu-color;
font-size: $-drop-menu-fs;
position: relative;
@include e(list) {
display: flex;
text-align: center;
background-color: #fff;
}
@include e(item) {
flex: 1;
min-width: 0;
height: $-drop-menu-height;
line-height: $-drop-menu-height;
text-align: center;
@include when(active) {
font-weight: $-fw-medium;
.wd-drop-menu__item-title::after {
opacity: 1;
}
:deep(.wd-drop-menu__arrow) {
transform: scale(0.6) rotate(-180deg);
transform-origin: center center;
}
}
@include when(disabled) {
color: $-drop-menu-disabled-color;
}
}
@include e(item-title) {
position: relative;
display: inline-block;
max-width: 100%;
padding: 0 $-drop-menu-side-padding;
box-sizing: border-box;
&::after {
position: absolute;
content: '';
width: 19px;
height: $-drop-menu-line-height;
bottom: 6px;
left: 50%;
transform: translate(-50%, 0);
background: $-drop-menu-line-color;
border-radius: $-drop-menu-line-height;
transition: opacity .15s;
opacity: 0;
}
}
@include e(item-title-text) {
position: relative;
@include lineEllipsis;
}
@include edeep(arrow) {
position: absolute;
display: inline-block;
top: 0;
right: -4px;
transition: transform 0.3s;
transform: scale(0.6);
}
}

@ -0,0 +1,176 @@
<template>
<view :style="customStyle" :class="`wd-drop-menu ${customClass}`" @click.stop="noop">
<view class="wd-drop-menu__list">
<view
v-for="(item, index) in titleList"
:key="index"
@click="toggle(item.uid)"
:class="`wd-drop-menu__item ${item.disabled ? 'is-disabled' : ''} ${currentUid === item.uid ? 'is-active' : ''}`"
>
<view class="wd-drop-menu__item-title">
<view class="wd-drop-menu__item-title-text">{{ item.title }}</view>
<wd-icon name="arrow-down" size="14px" custom-class="wd-drop-menu__arrow" />
</view>
</view>
</view>
<slot />
</view>
</template>
<script lang="ts">
export default {
name: 'wd-drop-menu',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { getCurrentInstance, inject, onBeforeMount, onMounted, provide, ref, watch } from 'vue'
import { closeOther } from '../common/clickoutside'
import { type Queue, queueKey } from '../composables/useQueue'
import { getRect } from '../common/util'
type DropDirction = 'up' | 'down'
interface Props {
customClass?: string
customStyle?: string
zIndex?: number
direction?: DropDirction
modal?: boolean
closeOnClickModal?: boolean
duration?: number
}
const props = withDefaults(defineProps<Props>(), {
customClass: '',
customStyle: '',
zIndex: 12,
direction: 'down',
modal: true,
closeOnClickModal: true,
duration: 200
})
const queue = inject<Queue | null>(queueKey, null)
//
const titleList = ref<Record<string, any>[]>([])
// -1
const currentUid = ref<string | null>(null)
const offset = ref<number>(0)
const windowHeight = ref<number>(0)
const children: any[] = []
const { proxy } = getCurrentInstance() as any
watch(
() => props.direction,
(newValue) => {
if (newValue !== 'up' && newValue !== 'down') {
// eslint-disable-next-line quotes
console.error("[wot design] warning(wd-drop-menu): direction must be 'up' or 'down'")
}
},
{ deep: true, immediate: true }
)
onBeforeMount(() => {
windowHeight.value = uni.getSystemInfoSync().windowHeight
})
onMounted(() => {
updateTitle()
})
/**
* 设置子项
* @param child
*/
function setChild(child) {
const hasChild = children.findIndex((drop) => {
return drop.$.uid === child.$.uid
})
if (hasChild <= -1) {
children.push(child)
} else {
children[hasChild] = child
}
}
function noop() {}
function toggle(uid: string) {
//
const child = children.find((child) => {
return child.$.uid === uid
})
// menu, menu
if (child && !child.disabled) {
if (queue && queue.closeOther) {
queue.closeOther(child)
} else {
closeOther(child)
}
fold(child)
}
}
/**
* 控制菜单内容是否展开
* @param {Number} currentIndex 当前选的索引
*/
function fold(child?: any) {
currentUid.value = child ? child.$.uid : null
if (!child) {
children.forEach((item, index) => {
item.$.exposed.setShowPop(false)
})
return
}
getRect('.wd-drop-menu', false, proxy).then((rect: any) => {
if (!rect) return
const { top, bottom } = rect
if (props.direction === 'down') {
offset.value = bottom
} else {
offset.value = windowHeight.value - top
}
//
children.forEach((item, index) => {
if (child.$.uid === item.$.uid) {
item.$.exposed.open()
} else {
item.$.exposed.setShowPop(false)
}
})
})
}
// value
function updateTitle() {
const titleListTemp: Record<string, any>[] = []
children.forEach((item, index) => {
titleListTemp.push({
uid: item.$.uid,
title: item.$.exposed.displayTitle.value,
disabled: item.disabled
})
})
titleList.value = titleListTemp
}
defineExpose({
setChild,
offset,
fold,
updateTitle
})
provide('dropMenu', proxy)
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,154 @@
@import "../common/abstracts/variable";
@import "../common/abstracts/mixin";
.wot-theme-dark {
@include b(fab) {}
}
@include b(fab) {
position: fixed;
z-index: 99;
@include edeep(trigger) {
min-width: auto !important;
box-sizing: border-box;
width: $-fab-trigger-width !important;
height: $-fab-trigger-height !important;
border-radius: calc($-fab-trigger-height / 2) !important;
}
:deep() {
@include e(actions) {
position: absolute;
z-index: 0;
display: flex;
justify-content: center;
align-items: center;
padding: $-fab-actions-padding 0;
@include m(left, right) {
height: 100%;
top: 0;
padding: 0 $-fab-actions-padding;
}
@include m(left) {
flex-direction: row-reverse;
right: 100%;
}
@include m(right) {
flex-direction: row;
left: 100%;
}
@include m(top, bottom) {
width: 100%;
left: 0;
}
@include m(top) {
flex-direction: column-reverse;
bottom: 100%;
}
@include m(bottom) {
flex-direction: column;
top: 100%;
}
}
//
@include e(transition-enter-active, transition-leave-active) {
transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
@include e(transition-enter) {
@include m(top) {
opacity: 0;
transform: translateY(40px);
}
@include m(bottom) {
opacity: 0;
transform: translateY(-40px);
}
@include m(left) {
opacity: 0;
transform: translateX(40px);
}
@include m(right) {
opacity: 0;
transform: translateX(-40px);
}
}
@include e(transition-leave-to) {
@include m(top) {
opacity: 0;
transform: translateY(40px);
}
@include m(bottom) {
opacity: 0;
transform: translateY(-40px);
}
@include m(left) {
opacity: 0;
transform: translateX(40px);
}
@include m(right) {
opacity: 0;
transform: translateX(-40px);
}
}
}
@include edeep(icon) {
font-size: 20px;
}
@include m(left-top) {
top: $-fab-top;
left: $-fab-left;
/* #ifdef H5 */
top: calc($-fab-top + var(--window-top));
left: calc($-fab-left + var(--window-left));
/* #endif */
}
@include m(right-top) {
top: $-fab-top;
right: $-fab-right;
/* #ifdef H5 */
top: calc($-fab-top + var(--window-top));
right: calc($-fab-right + var(--window-right));
/* #endif */
}
@include m(left-bottom) {
bottom: $-fab-bottom;
left: $-fab-left;
/* #ifdef H5 */
bottom: calc($-fab-bottom + var(--window-bottom));
left: calc($-fab-left + var(--window-left));
/* #endif */
}
@include m(right-bottom) {
bottom: $-fab-bottom;
right: $-fab-right;
/* #ifdef H5 */
bottom: calc($-fab-bottom + var(--window-bottom));
right: calc($-fab-right + var(--window-right));
/* #endif */
}
}

@ -0,0 +1,156 @@
<template>
<view :class="`wd-fab wd-fab--${position} ${customClass}`" :style="rootStyle" @click.stop="">
<view @click.stop="">
<wd-button @click="handleClick" custom-class="wd-fab__trigger" round :type="type" :disabled="disabled">
<wd-icon custom-class="wd-fab__icon" :name="isActive ? activeIcon : inactiveIcon"></wd-icon>
</wd-button>
</view>
<wd-transition
:enter-class="`wd-fab__transition-enter--${direction}`"
enter-active-class="wd-fab__transition-enter-active"
:leave-to-class="`wd-fab__transition-leave-to--${direction}`"
leave-active-class="wd-fab__transition-leave-active"
:custom-class="`wd-fab__actions wd-fab__actions--${direction}`"
:show="isActive"
:duration="300"
name=""
>
<slot></slot>
</wd-transition>
</view>
</template>
<script lang="ts">
export default {
name: 'wd-fab',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { type CSSProperties, computed, onBeforeMount, ref, watch, inject, getCurrentInstance, onBeforeUnmount } from 'vue'
import { isDef, objToStyle } from '../common/util'
import { type Queue, queueKey } from '../composables/useQueue'
import { closeOther, pushToQueue, removeFromQueue } from '../common/clickoutside'
type FabType = 'primary' | 'success' | 'info' | 'warning' | 'error' | 'default'
type FabPosition = 'left-top' | 'right-top' | 'left-bottom' | 'right-bottom'
type FabDirection = 'top' | 'right' | 'bottom' | 'left'
interface Props {
//
active?: boolean
// default primary info success warning error
type?: FabType
// left-top right-top left-bottom right-bottom
position?: FabPosition
// top bottom left right
direction?: FabDirection
//
disabled?: boolean
//
inactiveIcon?: string
//
activeIcon?: string
//
zIndex?: number
//
customClass?: string
//
customStyle?: string
}
const props = withDefaults(defineProps<Props>(), {
active: false,
type: 'primary',
position: 'right-bottom',
direction: 'top',
inactiveIcon: 'add',
activeIcon: 'close',
disabled: false,
zIndex: 99,
customClass: '',
customStyle: ''
})
const isActive = ref<boolean>(false) //
const queue = inject<Queue | null>(queueKey, null)
const { proxy } = getCurrentInstance() as any
watch(
() => props.active,
(newValue) => {
isActive.value = newValue
},
{ immediate: true, deep: true }
)
watch(
() => isActive.value,
(newValue) => {
if (newValue) {
if (queue && queue.closeOther) {
queue.closeOther(proxy)
} else {
closeOther(proxy)
}
}
}
)
const rootStyle = computed(() => {
const style: CSSProperties = {}
if (isDef(props.zIndex)) {
style['z-index'] = props.zIndex
}
return `${objToStyle(style)};${props.customStyle}`
})
onBeforeMount(() => {
if (queue && queue.pushToQueue) {
queue.pushToQueue(proxy)
} else {
pushToQueue(proxy)
}
})
onBeforeUnmount(() => {
if (queue && queue.removeFromQueue) {
queue.removeFromQueue(proxy)
} else {
removeFromQueue(proxy)
}
})
const emit = defineEmits(['update:active'])
function handleClick() {
if (props.disabled) {
return
}
isActive.value = !isActive.value
emit('update:active', isActive.value)
}
function open() {
isActive.value = true
emit('update:active', true)
}
function close() {
isActive.value = false
emit('update:active', false)
}
defineExpose({
open,
close
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,18 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
.wot-theme-dark {
@include b(form-item) {
}
}
@include bdeep(form-item) {
@include e(error-message){
color: $-form-item-error-message-color;
font-size: $-form-item-error-message-font-size;
line-height: $-form-item-error-message-line-height;
text-align: left;
vertical-align: middle;
}
}

@ -0,0 +1,82 @@
<!--
* @Author: weisheng
* @Date: 2023-12-14 11:21:58
* @LastEditTime: 2023-12-17 15:16:03
* @LastEditors: weisheng
* @Description:
* @FilePath: \wot-design-uni\src\uni_modules\wot-design-uni\components\wd-form-item\wd-form-item.vue
* 记得注释
-->
<template>
<wd-cell
custom-class="wd-form-item"
:required="required"
:title="label"
:center="center"
:border="border"
:title-width="labelWidth"
:is-link="isLink"
>
<slot></slot>
<view v-if="errorMessage" class="wd-form-item__error-message">{{ errorMessage }}</view>
</wd-cell>
</template>
<script lang="ts">
export default {
name: 'wd-form-item',
options: {
addGlobalClass: true,
virtualHost: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import { computed, ref } from 'vue'
import { useParent } from '../composables/useParent'
import WdCell from '../wd-cell/wd-cell.vue'
import { FORM_KEY, FormItemRule } from '../wd-form/types'
interface Props {
prop: string
rules?: FormItemRule[]
required?: boolean
center?: boolean
label?: string
labelWidth?: string
isLink?: boolean
customClass?: string
customStyle?: string
}
const props = withDefaults(defineProps<Props>(), {
rules: () => [],
center: false,
labelWidth: '100px',
customClass: '',
customStyle: ''
})
const { parent: form, index } = useParent(FORM_KEY)
const errorMessage = computed(() => {
if (form && props.prop && form.errorMessages && form.errorMessages[props.prop]) {
return form.errorMessages[props.prop]
} else {
return ''
}
})
const border = computed(() => {
if (index.value > 0 && form && form.border) {
return true
} else {
return false
}
})
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

@ -0,0 +1,10 @@
@import "../common/abstracts/variable";
@import "../common/abstracts/mixin";
.wot-theme-dark {
@include b(form) {
}
}
@include b(form) {
}

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

Loading…
Cancel
Save