buffeyu 4 years ago
parent
commit
e2e60b8a4f
29 changed files with 5959 additions and 81 deletions
  1. 48
      apis/add-paper.js
  2. 76
      pages/add-paper/index.vue
  3. 66
      pages/mall/index.vue
  4. 41
      pages/message/index.vue
  5. 175
      pages/paper-detail/index.vue
  6. 85
      uni_modules/uni-datetime-picker/changelog.md
  7. 185
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue
  8. 898
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue
  9. 19
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json
  10. 8
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js
  11. 19
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json
  12. 19
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json
  13. 45
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js
  14. 927
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue
  15. 981
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue
  16. 410
      uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js
  17. 90
      uni_modules/uni-datetime-picker/package.json
  18. 21
      uni_modules/uni-datetime-picker/readme.md
  19. 21
      uni_modules/uni-table/changelog.md
  20. 455
      uni_modules/uni-table/components/uni-table/uni-table.vue
  21. 29
      uni_modules/uni-table/components/uni-tbody/uni-tbody.vue
  22. 90
      uni_modules/uni-table/components/uni-td/uni-td.vue
  23. 503
      uni_modules/uni-table/components/uni-th/filter-dropdown.vue
  24. 251
      uni_modules/uni-table/components/uni-th/uni-th.vue
  25. 129
      uni_modules/uni-table/components/uni-thead/uni-thead.vue
  26. 179
      uni_modules/uni-table/components/uni-tr/table-checkbox.vue
  27. 171
      uni_modules/uni-table/components/uni-tr/uni-tr.vue
  28. 86
      uni_modules/uni-table/package.json
  29. 13
      uni_modules/uni-table/readme.md

48
apis/add-paper.js

@ -2,31 +2,45 @@ import http from '../utils/http/index.js'
// 添加纸品
export function createProduct(data) {
return http.post({
url: '/base-paper-trading/create/product',
data
})
return http.post({
url: '/base-paper-trading/create/product',
data
})
}
// 更新纸品
export function updateProduct(data) {
return http.post({
url: '/base-paper-trading/update/product',
data
})
}
// 纸品种类列表
export function getCategoryList(data) {
return http.get({
url: '/base-paper-trading/get/product/category-list',
data
})
return http.get({
url: '/base-paper-trading/get/product/category-list',
data
})
}
// 纸品详情
export function getSupplierDteail(data) {
return http.get({
url: '/base-paper-trading/get/product-detail/for/supplier',
data
})
return http.get({
url: '/base-paper-trading/get/product-detail/for/supplier',
data
})
}
// 店铺纸品列表(供应商端)
export function SupplierList(data) {
return http.get({
url: '/base-paper-trading/get/store/product-list/for/supplier',
data
})
return http.get({
url: '/base-paper-trading/get/store/product-list/for/supplier',
data
})
}
// 上下架纸品
export function productStatus(data) {
return http.post({
url: '/base-paper-trading/update/product/status',
data
})
}

76
pages/add-paper/index.vue

@ -118,7 +118,6 @@
<view class="label_value label-select" @click="popueShow(index)">
<text class="paper-price-title text-right">{{ item.stockUnit === '1' ? '张' : '吨' }}</text>
<uni-icons type="bottom" color="#000000" size="18"></uni-icons>
</view>
</view>
<view class="paper-price-item">
@ -192,7 +191,7 @@
@select="select"
@delete="deleteImage"
>
<image class="paper-upload-image" src="../../static/imgs/trade/uploadImage.png" mode=""></image>
<image class="paper-upload-image" src="../../static/imgs/trade/uploadImage.png" mode=""></image>
</uni-file-picker>
</view>
</view>
@ -285,7 +284,7 @@
import { back, go2 } from '@/utils/hook.js'
import qnEasyinput from '@/components/qn-easyinput/qn-easyinput.vue'
import uGap from '@/components/u-gap/u-gap.vue'
import { getCategoryList, createProduct,getSupplierDteail } from '@/apis/add-paper.js'
import { getCategoryList, createProduct, getSupplierDteail, updateProduct } from '@/apis/add-paper.js'
const validateFields = ['name', 'isMainProduct', 'manufacturerName', 'brandName', 'categoryId']
const validatePriceFields = ['isPromoting', 'listPrice', 'minimum', 'stock', 'stockUnit', 'weight']
export default {
@ -306,7 +305,6 @@ export default {
imageStyles: {
width: 100,
height: 100,
background:'#000',
border: {
color: '#f1f1f1',
width: 1,
@ -338,15 +336,18 @@ export default {
}
],
imgList: [],
priceIndex: 0
priceIndex: 0,
eidtFor: 0
}
},
onLoad(option) {
if (option) {
this.title = option.title
}
if(this.title === '编辑纸品'){
this.eidtFor = 0
if (this.title === '编辑纸品') {
this.getDteailList()
this.eidtFor = 1
}
this.getCategoryListQuery()
},
@ -375,13 +376,16 @@ export default {
this.$refs.paperpopup.open('bottom')
},
//
getDteailList(res){
this.getSupplierDteail(this.form).then(res => {
getDteailList(res) {
var params = {
mallSupplierIds : null,
productId : null
}
this.getSupplierDteail(params).then(res => {
if (res) {
this.form = res
this.skuList = res.skuList
this.imgList =res.imgList
this.form = res
this.skuList = res.skuList
this.imgList = res.imgList
}
})
},
@ -450,20 +454,34 @@ export default {
}
var params = {
...this.form,
skuList:this.skuList,
imgList:this.imgList
skuList: this.skuList,
imgList: this.imgList
}
if (this.eidtFor === 0) {
createProduct(params).then(res => {
if (res) {
uni.showToast({
title: '添加成功',
icon: 'success'
})
setTimeout(() => {
back()
}, 1000)
}
})
} else {
updateProduct(params).then(res => {
if (res) {
uni.showToast({
title: '更新成功',
icon: 'success'
})
setTimeout(() => {
back()
}, 1000)
}
})
}
createProduct(this.form).then(res => {
if (res) {
uni.showToast({
title: '添加成功',
icon: 'success'
})
setTimeout(() => {
back()
}, 1000)
}
})
},
//
getCategoryListQuery() {
@ -568,10 +586,10 @@ export default {
.paper-price-image {
padding: 22rpx 32rpx;
}
.paper-upload-image{
.paper-upload-image {
width: 184rpx;
height: 184rpx;
background: rgba(220,222,224,0.50);
background: rgba(220, 222, 224, 0.5);
border-radius: 10rpx;
}
.paper-price-textArea {
@ -673,8 +691,8 @@ export default {
padding: 10rpx 32rpx;
background-color: #ffffff;
}
.paper-backColor{
background-color: rgba(220,222,224,1);
.paper-backColor {
background-color: rgba(220, 222, 224, 1);
}
}
</style>

66
pages/mall/index.vue

@ -39,7 +39,7 @@
<view class="btn">
<view class="" @click="editPaper()">编辑</view>
<view class="divide"></view>
<view class="">下架</view>
<view class="" @click="productStatusChange(item)">{{item.status=== '30111'?'下架':'上架'}}</view>
</view>
</view>
<uGap></uGap>
@ -56,7 +56,7 @@
<script>
import uGap from '@/components/u-gap/u-gap.vue'
import { back, go2 } from '@/utils/hook.js'
import { SupplierList } from '@/apis/add-paper.js'
import { SupplierList,productStatus } from '@/apis/add-paper.js'
export default {
components: { uGap },
data() {
@ -109,26 +109,28 @@ export default {
})
})
},
// downCallback() {
// this.orderPagination.pageNum = 1
// this.quertData()
// .then(({ list, total }) => {
// this.$refs.list.refreshSuccess({ list, total })
// })
// .catch(() => {
// this.$refs.list.refreshFail()
// })
// },
// upCallback() {
// this.orderPagination.pageNum++
// this.quertData()
// .then(({ list, total }) => {
// this.$refs.list.loadSuccess({ list, total })
// })
// .catch(() => {
// this.$refs.list.loadFail()
// })
// },
downCallback() {
return
this.orderPagination.pageNum = 1
this.quertData()
.then(({ list, total }) => {
this.$refs.list.refreshSuccess({ list, total })
})
.catch(() => {
this.$refs.list.refreshFail()
})
},
upCallback() {
return
this.orderPagination.pageNum++
this.quertData()
.then(({ list, total }) => {
this.$refs.list.loadSuccess({ list, total })
})
.catch(() => {
this.$refs.list.loadFail()
})
},
//
checkMall() {
go2('switching-mall')
@ -154,7 +156,25 @@ export default {
//
detailInfo(){
go2('paper-detail')
}
},
//
productStatusChange(item){
var params = {
id:item.id,
status:item.status === '30111'?'30112':'30111'
}
productStatus(params).then(res => {
if (res) {
uni.showToast({
title: '添加成功',
icon: 'success'
})
setTimeout(() => {
back()
}, 1000)
}
})
},
}
}
</script>

41
pages/message/index.vue

@ -1,9 +1,44 @@
<template>
<view>消息中心</view>
<view class="message-info">
<view>
<uni-nav-bar left-icon="back" @clickLeft="back" statusBar fixed title="">
<view class="message-info-title">消息通知</view>
<view slot="left"></view>
<view slot="right" class="message-info-right-title">一键全读</view>
</uni-nav-bar>
</view>
</view>
</template>
<script>
export default {}
import { back, go2 } from '@/utils/hook.js'
export default {
data() {
return {
}
},
methods:{
back
}
}
</script>
<style></style>
<style lang="scss">
.message-info{
.message-info-title {
width: 100%;
font-size: 36rpx;
color: #000000;
letter-spacing: 0;
text-align: center;
font-weight: 500;
}
.message-info-right-title{
font-size: 28rpx;
color: #007AFF;
text-align: right;
line-height: 40rpx;
font-weight: 500;
}
}
</style>

175
pages/paper-detail/index.vue

@ -4,27 +4,118 @@
<uni-nav-bar left-icon="back" @clickLeft="back" statusBar fixed title="">
<view class="paper-detail-title">纸品详情</view>
<view slot="left"></view>
<view slot="right"></view>
<view slot="right" class="paper-detail-right-title">分享</view>
</uni-nav-bar>
</view>
<view class="paper-detail-info">
<view class="">
<text class="paper-detail-info-text">纸品名称</text>
<text class="paper-detail-info-subtext">博汇朱雀白卡纸</text>
</view>
<view class="">
<text class="paper-detail-info-text">纸厂信息</text>
<text class="paper-detail-info-subtext">博汇</text>
</view>
<view class="">
<text class="paper-detail-info-text">品牌信息</text>
<text class="paper-detail-info-subtext">朱雀</text>
</view>
<view class="">
<text class="paper-detail-info-text">纸种信息</text>
<text class="paper-detail-info-subtext">白卡纸</text>
</view>
</view>
<uGap></uGap>
<view class="paper-datail-list">
<view class="paper-datail-list-title paper-bottom-border">纸品售价</view>
<view class="">
<uni-table emptyText="暂无更多数据">
<!-- 表头行 -->
<uni-tr class="paper-table-title">
<uni-th align="left" width="65">克重</uni-th>
<uni-th align="left" width="65">规格</uni-th>
<uni-th align="left" width="65">售价</uni-th>
<uni-th align="left" width="65">库存</uni-th>
<uni-th align="left" width="65">是否特价</uni-th>
</uni-tr>
<!-- 表格数据行 -->
<uni-tr>
<uni-td>2500g</uni-td>
<uni-td>779*1092</uni-td>
<uni-td>4400.00</uni-td>
<uni-td>8900</uni-td>
<uni-td></uni-td>
</uni-tr>
</uni-table>
</view>
</view>
<uGap></uGap>
<view class="paper-datail-list ">
<view class="paper-datail-list-title">纸品图片</view>
<view class="paper-list-view">
<image class="paper-datail-list-image" src="../../static/imgs/client-credit-list/client-default.png" mode=""></image>
<image class="paper-datail-list-image" src="../../static/imgs/client-credit-list/client-default.png" mode=""></image>
<image class="paper-datail-list-image" src="../../static/imgs/client-credit-list/client-default.png" mode=""></image>
<image class="paper-datail-list-image" src="../../static/imgs/client-credit-list/client-default.png" mode=""></image>
</view>
</view>
<uGap></uGap>
<view class="paper-datail-list paper-bottom-border">
<view class="paper-datail-list-title ">商品买点</view>
<view class="paper-datail-list-text"><text>月结30天价格不加服务费</text></view>
</view>
<view class="paper-datail-list paper-bottom-border">
<view class="paper-datail-list-title ">商品描述</view>
<view class="paper-datail-list-text"><text>售价全部为含税价支持先用后付欢迎来电查询近期价格波动较大请下单前先咨询价格</text></view>
</view>
<view class="paper-datail-list paper-bottom-border">
<view class="paper-datail-list-title ">送货说明</view>
<view class="paper-datail-list-text"><text>分切纸不足重量的另加调机费低于送货重量或超出送货范围的运费另计</text></view>
</view>
<view class="paper-datail-list paper-bottom-border">
<view class="paper-datail-list-title ">其他说明</view>
<view class="paper-datail-list-text"><text>无其他说明</text></view>
</view>
</view>
</template>
<script>
import { back, go2 } from '@/utils/hook.js'
export default {
data() {
return {
import { back, go2 } from '@/utils/hook.js'
import uGap from '@/components/u-gap/u-gap.vue'
import { getSupplierDteail } from '@/apis/add-paper.js'
export default {
components: {
uGap
},
data() {
return {
form:{},
skuList:[],
imgList:[]
}
},
methods: {
back,
//
getDteailList(res) {
var params = {
mallSupplierIds : null,
productId : null
}
this.getSupplierDteail(params).then(res => {
if (res) {
this.form = res
this.skuList = res.skuList
this.imgList = res.imgList
}
})
},
methods: {
back,
}
}
}
</script>
<style lang="scss">
.paper-detail{
.paper-detail {
.paper-detail-title {
width: 100%;
font-size: 36rpx;
@ -33,5 +124,71 @@
text-align: center;
font-weight: 500;
}
.paper-detail-right-title {
font-size: 28rpx;
color: #007aff;
text-align: right;
line-height: 40rpx;
font-weight: 500;
}
.paper-detail-info {
height: 260rpx;
background: #ffffff;
padding: 24rpx 32rpx;
line-height: 56rpx;
}
.paper-detail-info-text {
font-size: 28rrpx;
color: #888888;
letter-spacing: 0;
font-weight: 400;
}
.paper-detail-info-subtext {
font-size: 28rpx;
color: #333333;
letter-spacing: 0;
font-weight: 400;
}
.paper-datail-list {
background: #ffffff;
.paper-datail-list-title {
font-size: 28rpx;
color: #000000;
letter-spacing: 0;
font-weight: 600;
height: 80rpx;
align-items: center;
display: flex;
padding-left: 32rpx;
}
.paper-datail-list-text {
font-size: 28rpx;
color: #333333;
letter-spacing: 0;
font-weight: 400;
align-items: center;
line-height: 50rpx;
padding: 0rpx 32rpx 20rpx 32rpx;
}
.paper-table-title {
font-size: 24rpx;
color: rgba(0, 0, 0, 0.5);
letter-spacing: 0;
font-weight: 400;
background: #f7f8fa;
}
.paper-list-view {
line-height: 40rpx;
}
.paper-datail-list-image {
width: 120rpx;
height: 120rpx;
padding: 32rpx 32rpx;
}
}
.paper-bottom-border {
border-bottom: 2rpx solid #d8d8d8;
}
}
</style>

85
uni_modules/uni-datetime-picker/changelog.md

@ -0,0 +1,85 @@
## 2.2.2(2021-12-10)
- 修复 clear-icon 属性在小程序平台不生效的 bug
## 2.2.1(2021-12-10)
- 修复 日期范围选在小程序平台,必须多点击一次才能取消选中状态的 bug
## 2.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-datetime-picker](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
## 2.1.5(2021-11-09)
- 新增 提供组件设计资源,组件样式调整
## 2.1.4(2021-09-10)
- 修复 hide-second 在移动端的 bug
- 修复 单选赋默认值时,赋值日期未高亮的 bug
- 修复 赋默认值时,移动端未正确显示时间的 bug
## 2.1.3(2021-09-09)
- 新增 hide-second 属性,支持只使用时分,隐藏秒
## 2.1.2(2021-09-03)
- 优化 取消选中时(范围选)直接开始下一次选择, 避免多点一次
- 优化 移动端支持清除按钮,同时支持通过 ref 调用组件的 clear 方法
- 优化 调整字号大小,美化日历界面
- 修复 因国际化导致的 placeholder 失效的 bug
## 2.1.1(2021-08-24)
- 新增 支持国际化
- 优化 范围选择器在 pc 端过宽的问题
## 2.1.0(2021-08-09)
- 新增 适配 vue3
## 2.0.19(2021-08-09)
- 新增 支持作为 uni-forms 子组件相关功能
- 修复 在 uni-forms 中使用时,选择时间报 NAN 错误的 bug
## 2.0.18(2021-08-05)
- 修复 type 属性动态赋值无效的 bug
- 修复 ‘确认’按钮被 tabbar 遮盖 bug
- 修复 组件未赋值时范围选左、右日历相同的 bug
## 2.0.17(2021-08-04)
- 修复 范围选未正确显示当前值的 bug
- 修复 h5 平台(移动端)报错 'cale' of undefined 的 bug
## 2.0.16(2021-07-21)
- 新增 return-type 属性支持返回 date 日期对象
## 2.0.15(2021-07-14)
- 修复 单选日期类型,初始赋值后不在当前日历的 bug
- 新增 clearIcon 属性,显示框的清空按钮可配置显示隐藏(仅 pc 有效)
- 优化 移动端移除显示框的清空按钮,无实际用途
## 2.0.14(2021-07-14)
- 修复 组件赋值为空,界面未更新的 bug
- 修复 start 和 end 不能动态赋值的 bug
- 修复 范围选类型,用户选择后再次选择右侧日历(结束日期)显示不正确的 bug
## 2.0.13(2021-07-08)
- 修复 范围选择不能动态赋值的 bug
## 2.0.12(2021-07-08)
- 修复 范围选择的初始时间在一个月内时,造成无法选择的bug
## 2.0.11(2021-07-08)
- 优化 弹出层在超出视窗边缘定位不准确的问题
## 2.0.10(2021-07-08)
- 修复 范围起始点样式的背景色与今日样式的字体前景色融合,导致日期字体看不清的 bug
- 优化 弹出层在超出视窗边缘被遮盖的问题
## 2.0.9(2021-07-07)
- 新增 maskClick 事件
- 修复 特殊情况日历 rpx 布局错误的 bug,rpx -> px
- 修复 范围选择时清空返回值不合理的bug,['', ''] -> []
## 2.0.8(2021-07-07)
- 新增 日期时间显示框支持插槽
## 2.0.7(2021-07-01)
- 优化 添加 uni-icons 依赖
## 2.0.6(2021-05-22)
- 修复 图标在小程序上不显示的 bug
- 优化 重命名引用组件,避免潜在组件命名冲突
## 2.0.5(2021-05-20)
- 优化 代码目录扁平化
## 2.0.4(2021-05-12)
- 新增 组件示例地址
## 2.0.3(2021-05-10)
- 修复 ios 下不识别 '-' 日期格式的 bug
- 优化 pc 下弹出层添加边框和阴影
## 2.0.2(2021-05-08)
- 修复 在 admin 中获取弹出层定位错误的bug
## 2.0.1(2021-05-08)
- 修复 type 属性向下兼容,默认值从 date 变更为 datetime
## 2.0.0(2021-04-30)
- 支持日历形式的日期+时间的范围选择
> 注意:此版本不向后兼容,不再支持单独时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)
## 1.0.6(2021-03-18)
- 新增 hide-second 属性,时间支持仅选择时、分
- 修复 选择跟显示的日期不一样的 bug
- 修复 chang事件触发2次的 bug
- 修复 分、秒 end 范围错误的 bug
- 优化 更好的 nvue 适配

185
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar-item.vue

@ -0,0 +1,185 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--before-checked-x':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked-x':weeks.afterMultiple,
}" @click="choiceDate(weeks)" @mouseenter="handleMousemove(weeks)">
<view class="uni-calendar-item__weeks-box-item" :class="{
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && (calendar.userChecked || !checkHover),
'uni-calendar-item--checked-range-text': checkHover,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text uni-calendar-item__weeks-box-text-disable uni-calendar-item--checked-text">{{weeks.date}}</text>
</view>
<view :class="{'uni-calendar-item--isDay': weeks.isDay}"></view>
</view>
</template>
<script>
export default {
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
},
checkHover: {
type: Boolean,
default: false
}
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
},
handleMousemove(weeks) {
this.$emit('handleMouse', weeks)
}
}
}
</script>
<style lang="scss" scoped>
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
margin: 1px 0;
position: relative;
}
.uni-calendar-item__weeks-box-text {
font-size: 14px;
// font-family: Lato-Bold, Lato;
font-weight: bold;
color: #455997;
}
.uni-calendar-item__weeks-lunar-text {
font-size: 12px;
color: #333;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
top: 5px;
right: 5px;
width: 8px;
height: 8px;
border-radius: 8px;
background-color: #dd524d;
}
.uni-calendar-item__weeks-box .uni-calendar-item--disable {
// background-color: rgba(249, 249, 249, $uni-opacity-disabled);
cursor: default;
}
.uni-calendar-item--disable .uni-calendar-item__weeks-box-text-disable {
color: #D1D1D1;
}
.uni-calendar-item--isDay {
position: absolute;
top: 10px;
right: 17%;
background-color: #dd524d;
width:6px;
height: 6px;
border-radius: 50%;
}
.uni-calendar-item--extra {
color: #dd524d;
opacity: 0.8;
}
.uni-calendar-item__weeks-box .uni-calendar-item--checked {
background-color: #007aff;
border-radius: 50%;
box-sizing: border-box;
border: 3px solid #fff;
}
.uni-calendar-item--checked .uni-calendar-item--checked-text {
color: #fff;
}
.uni-calendar-item--multiple .uni-calendar-item--checked-range-text {
color: #333;
}
.uni-calendar-item--multiple {
background-color: #F6F7FC;
// color: #fff;
}
.uni-calendar-item--multiple .uni-calendar-item--before-checked,
.uni-calendar-item--multiple .uni-calendar-item--after-checked {
background-color: #409eff;
border-radius: 50%;
box-sizing: border-box;
border: 3px solid #F6F7FC;
}
.uni-calendar-item--before-checked .uni-calendar-item--checked-text,
.uni-calendar-item--after-checked .uni-calendar-item--checked-text {
color: #fff;
}
.uni-calendar-item--before-checked-x {
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
box-sizing: border-box;
background-color: #F6F7FC;
}
.uni-calendar-item--after-checked-x {
border-top-right-radius: 50px;
border-bottom-right-radius: 50px;
background-color: #F6F7FC;
}
</style>

898
uni_modules/uni-datetime-picker/components/uni-datetime-picker/calendar.vue

@ -0,0 +1,898 @@
<template>
<view class="uni-calendar" @mouseleave="leaveCale">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
@click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content"
:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow, 'uni-calendar__content-mobile': aniMaskShow}">
<view class="uni-calendar__header" :class="{'uni-calendar__header-mobile' :!insert}">
<view v-if="left" class="uni-calendar__header-btn-box" @click.stop="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
<text
class="uni-calendar__header-text">{{ (nowDate.year||'') + ' 年 ' + ( nowDate.month||'') +' 月'}}</text>
</picker>
<view v-if="right" class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<view v-if="!insert" class="dialog-close" @click="clean">
<view class="dialog-close-plus" data-id="close"></view>
<view class="dialog-close-plus dialog-close-rotate" data-id="close"></view>
</view>
<!-- <text class="uni-calendar__backtoday" @click="backtoday">回到今天</text> -->
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks" style="padding-bottom: 7px;">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{monText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar"
:selected="selected" :lunar="lunar" :checkHover="range" @change="choiceDate"
@handleMouse="handleMouse">
</calendar-item>
</view>
</view>
</view>
<view v-if="!insert && !range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top"
style="padding: 0 80px;">
<view class="uni-date-changed--time-date">{{tempSingleDate ? tempSingleDate : selectDateText}}</view>
<time-picker type="time" :start="reactStartTime" :end="reactEndTime" v-model="time"
:disabled="!tempSingleDate" :border="false" :hide-second="hideSecond" class="time-picker-style">
</time-picker>
</view>
<view v-if="!insert && range && typeHasTime" class="uni-date-changed uni-calendar--fixed-top">
<view class="uni-date-changed--time-start">
<view class="uni-date-changed--time-date">{{tempRange.before ? tempRange.before : startDateText}}
</view>
<time-picker type="time" :start="reactStartTime" v-model="timeRange.startTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.before" class="time-picker-style">
</time-picker>
</view>
<uni-icons type="arrowthinright" color="#999" style="line-height: 50px;"></uni-icons>
<view class="uni-date-changed--time-end">
<view class="uni-date-changed--time-date">{{tempRange.after ? tempRange.after : endDateText}}</view>
<time-picker type="time" :end="reactEndTime" v-model="timeRange.endTime" :border="false"
:hide-second="hideSecond" :disabled="!tempRange.after" class="time-picker-style">
</time-picker>
</view>
</view>
<view v-if="!insert" class="uni-date-changed uni-date-btn--ok">
<!-- <view class="uni-calendar__header-btn-box">
<text class="uni-calendar__button-text uni-calendar--fixed-width">{{okText}}</text>
</view> -->
<view class="uni-datetime-picker--btn" @click="confirm">确认</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import calendarItem from './calendar-item.vue'
import timePicker from './time-picker.vue'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const {
t
} = initVueI18n(messages)
/**
* Calendar 日历
* @description 日历组件可以查看日期选择任意范围内的日期打点操作常用场景如酒店日期预订火车机票选择购买日期上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
* @property {Boolean} insert = [true|false] 插入模式,默认为false
* @value true 弹窗模式
* @value false 插入模式
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array} selected 打点期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @event {Function} change 日期改变`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
calendarItem,
timePicker
},
props: {
date: {
type: String,
default: ''
},
defTime: {
type: [String, Object],
default: ''
},
selectableTimes: {
type: [Object],
default () {
return {}
}
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
typeHasTime: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
},
left: {
type: Boolean,
default: true
},
right: {
type: Boolean,
default: true
},
checkHover: {
type: Boolean,
default: true
},
hideSecond: {
type: [Boolean],
default: false
},
pleStatus: {
type: Object,
default () {
return {
before: '',
after: '',
data: [],
fulldate: ''
}
}
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false,
firstEnter: true,
time: '',
timeRange: {
startTime: '',
endTime: ''
},
tempSingleDate: '',
tempRange: {
before: '',
after: ''
}
}
},
watch: {
date: {
immediate: true,
handler(newVal, oldVal) {
if (!this.range) {
this.tempSingleDate = newVal
setTimeout(() => {
this.init(newVal)
}, 100)
}
}
},
defTime: {
immediate: true,
handler(newVal, oldVal) {
if (!this.range) {
this.time = newVal
} else {
// console.log('-----', newVal);
this.timeRange.startTime = newVal.start
this.timeRange.endTime = newVal.end
}
}
},
startDate(val) {
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val) {
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
},
pleStatus: {
immediate: true,
handler(newVal, oldVal) {
const {
before,
after,
fulldate,
which
} = newVal
this.tempRange.before = before
this.tempRange.after = after
setTimeout(() => {
if (fulldate) {
this.cale.setHoverMultiple(fulldate)
if (before && after) {
this.cale.lastHover = true
if (this.rangeWithinMonth(after, before)) return
this.setDate(before)
} else {
this.cale.setMultiple(fulldate)
this.setDate(this.nowDate.fullDate)
this.calendar.fullDate = ''
this.cale.lastHover = false
}
} else {
this.cale.setDefaultMultiple(before, after)
if (which === 'left') {
this.setDate(before)
this.weeks = this.cale.weeks
} else {
this.setDate(after)
this.weeks = this.cale.weeks
}
this.cale.lastHover = true
}
}, 16)
}
}
},
computed: {
reactStartTime() {
const activeDate = this.range ? this.tempRange.before : this.calendar.fullDate
const res = activeDate === this.startDate ? this.selectableTimes.start : ''
return res
},
reactEndTime() {
const activeDate = this.range ? this.tempRange.after : this.calendar.fullDate
const res = activeDate === this.endDate ? this.selectableTimes.end : ''
return res
},
/**
* for i18n
*/
selectDateText() {
return t("uni-datetime-picker.selectDate")
},
startDateText() {
return this.startPlaceholder || t("uni-datetime-picker.startDate")
},
endDateText() {
return this.endPlaceholder || t("uni-datetime-picker.endDate")
},
okText() {
return t("uni-datetime-picker.ok")
},
monText() {
return t("uni-calender.MON")
},
TUEText() {
return t("uni-calender.TUE")
},
WEDText() {
return t("uni-calender.WED")
},
THUText() {
return t("uni-calender.THU")
},
FRIText() {
return t("uni-calender.FRI")
},
SATText() {
return t("uni-calender.SAT")
},
SUNText() {
return t("uni-calender.SUN")
},
},
created() {
//
this.cale = new Calendar({
// date: new Date(),
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
// multipleStatus: this.pleStatus
})
//
// this.cale.setDate(this.date)
this.init(this.date)
// this.setDay
},
methods: {
leaveCale() {
this.firstEnter = true
},
handleMouse(weeks) {
if (weeks.disable) return
if (this.cale.lastHover) return
let {
before,
after
} = this.cale.multipleStatus
if (!before) return
this.calendar = weeks
//
this.cale.setHoverMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
// hover
if (this.firstEnter) {
this.$emit('firstEnterCale', this.cale.multipleStatus)
this.firstEnter = false
}
},
rangeWithinMonth(A, B) {
const [yearA, monthA] = A.split('-')
const [yearB, monthB] = B.split('-')
return yearA === yearB && monthA === monthB
},
// 穿
clean() {
this.close()
},
clearCalender() {
if (this.range) {
this.timeRange.startTime = ''
this.timeRange.endTime = ''
this.tempRange.before = ''
this.tempRange.after = ''
this.cale.multipleStatus.before = ''
this.cale.multipleStatus.after = ''
this.cale.multipleStatus.data = []
this.cale.lastHover = false
} else {
this.time = ''
this.tempSingleDate = ''
}
this.calendar.fullDate = ''
this.setDate()
},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.init(value)
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
},
// choiceDate(weeks) {
// if (weeks.disable) return
// this.calendar = weeks
// //
// this.cale.setMultiple(this.calendar.fullDate, true)
// this.weeks = this.cale.weeks
// this.tempSingleDate = this.calendar.fullDate
// this.tempRange.before = this.cale.multipleStatus.before
// this.tempRange.after = this.cale.multipleStatus.after
// this.change()
// },
/**
* 打开日历弹窗
*/
open() {
//
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
// this.cale.setDate(this.date)
this.init(this.date)
}
this.show = true
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
/**
* 关闭日历弹窗
*/
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
this.$emit('close')
}, 300)
})
},
/**
* 确认按钮
*/
confirm() {
this.setEmit('confirm')
this.close()
},
/**
* 变化触发
*/
change() {
if (!this.insert) return
this.setEmit('change')
},
/**
* 选择月份触发
*/
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
},
/**
* 派发事件
* @param {Object} name
*/
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
time: this.time,
timeRange: this.timeRange,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {}
})
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
this.calendar.userChecked = true
//
this.cale.setMultiple(this.calendar.fullDate, true)
this.weeks = this.cale.weeks
this.tempSingleDate = this.calendar.fullDate
this.tempRange.before = this.cale.multipleStatus.before
this.tempRange.after = this.cale.multipleStatus.after
this.change()
},
/**
* 回到今天
*/
backtoday() {
let date = this.cale.getDate(new Date()).fullDate
// this.cale.setDate(date)
this.init(date)
this.change()
},
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
//
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
//
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
},
/**
* 上个月
*/
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
/**
* 下个月
*/
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style lang="scss" scoped>
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, 0.4);
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
bottom: calc(var(--window-bottom));
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__content-mobile {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.1);
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
}
.uni-calendar__header-mobile {
padding: 10px;
padding-bottom: 0;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: rgba(0, 0, 0, 0.4);
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
padding-left: 10px;
height: 25px;
line-height: 25px;
font-size: 12px;
border-top-left-radius: 25px;
border-bottom-left-radius: 25px;
color: #fff;
background-color: #f1f1f1;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: 15px;
color: #666;
}
.uni-calendar__button-text {
text-align: center;
width: 100px;
font-size: 14px;
color: #007aff;
/* #ifndef APP-NVUE */
letter-spacing: 3px;
/* #endif */
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 9px;
height: 9px;
border-left-color: #808080;
border-left-style: solid;
border-left-width: 1px;
border-top-color: #555555;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 40px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 12px;
color: #B2B2B2;
}
.uni-calendar__box {
position: relative;
// padding: 0 10px;
padding-bottom: 7px;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: #999;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
.uni-date-changed {
padding: 0 10px;
// line-height: 50px;
text-align: center;
color: #333;
border-top-color: #DCDCDC;
;
border-top-style: solid;
border-top-width: 1px;
flex: 1;
}
.uni-date-btn--ok {
padding: 20px 15px;
}
.uni-date-changed--time-start {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
}
.uni-date-changed--time-end {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
}
.uni-date-changed--time-date {
color: #999;
line-height: 50px;
margin-right: 5px;
// opacity: 0.6;
}
.time-picker-style {
// width: 62px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center
}
.mr-10 {
margin-right: 10px;
}
.dialog-close {
position: absolute;
top: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 0 25px;
margin-top: 10px;
}
.dialog-close-plus {
width: 16px;
height: 2px;
background-color: #737987;
border-radius: 2px;
transform: rotate(45deg);
}
.dialog-close-rotate {
position: absolute;
transform: rotate(-45deg);
}
.uni-datetime-picker--btn {
border-radius: 100px;
height: 40px;
line-height: 40px;
background-color: #007aff;
color: #fff;
font-size: 16px;
letter-spacing: 5px;
}
/* #ifndef APP-NVUE */
.uni-datetime-picker--btn:active {
opacity: 0.7;
}
/* #endif */
</style>

19
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/en.json

@ -0,0 +1,19 @@
{
"uni-datetime-picker.selectDate": "select date",
"uni-datetime-picker.selectTime": "select time",
"uni-datetime-picker.selectDateTime": "select datetime",
"uni-datetime-picker.startDate": "start date",
"uni-datetime-picker.endDate": "end date",
"uni-datetime-picker.startTime": "start time",
"uni-datetime-picker.endTime": "end time",
"uni-datetime-picker.ok": "ok",
"uni-datetime-picker.clear": "clear",
"uni-datetime-picker.cancel": "cancel",
"uni-calender.MON": "MON",
"uni-calender.TUE": "TUE",
"uni-calender.WED": "WED",
"uni-calender.THU": "THU",
"uni-calender.FRI": "FRI",
"uni-calender.SAT": "SAT",
"uni-calender.SUN": "SUN"
}

8
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/index.js

@ -0,0 +1,8 @@
import en from './en.json'
import zhHans from './zh-Hans.json'
import zhHant from './zh-Hant.json'
export default {
en,
'zh-Hans': zhHans,
'zh-Hant': zhHant
}

19
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hans.json

@ -0,0 +1,19 @@
{
"uni-datetime-picker.selectDate": "选择日期",
"uni-datetime-picker.selectTime": "选择时间",
"uni-datetime-picker.selectDateTime": "选择日期时间",
"uni-datetime-picker.startDate": "开始日期",
"uni-datetime-picker.endDate": "结束日期",
"uni-datetime-picker.startTime": "开始时间",
"uni-datetime-picker.endTime": "结束时间",
"uni-datetime-picker.ok": "确定",
"uni-datetime-picker.clear": "清除",
"uni-datetime-picker.cancel": "取消",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

19
uni_modules/uni-datetime-picker/components/uni-datetime-picker/i18n/zh-Hant.json

@ -0,0 +1,19 @@
{
"uni-datetime-picker.selectDate": "選擇日期",
"uni-datetime-picker.selectTime": "選擇時間",
"uni-datetime-picker.selectDateTime": "選擇日期時間",
"uni-datetime-picker.startDate": "開始日期",
"uni-datetime-picker.endDate": "結束日期",
"uni-datetime-picker.startTime": "開始时间",
"uni-datetime-picker.endTime": "結束时间",
"uni-datetime-picker.ok": "確定",
"uni-datetime-picker.clear": "清除",
"uni-datetime-picker.cancel": "取消",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

45
uni_modules/uni-datetime-picker/components/uni-datetime-picker/keypress.js

@ -0,0 +1,45 @@
// #ifdef H5
export default {
name: 'Keypress',
props: {
disable: {
type: Boolean,
default: false
}
},
mounted () {
const keyNames = {
esc: ['Esc', 'Escape'],
tab: 'Tab',
enter: 'Enter',
space: [' ', 'Spacebar'],
up: ['Up', 'ArrowUp'],
left: ['Left', 'ArrowLeft'],
right: ['Right', 'ArrowRight'],
down: ['Down', 'ArrowDown'],
delete: ['Backspace', 'Delete', 'Del']
}
const listener = ($event) => {
if (this.disable) {
return
}
const keyName = Object.keys(keyNames).find(key => {
const keyName = $event.key
const value = keyNames[key]
return value === keyName || (Array.isArray(value) && value.includes(keyName))
})
if (keyName) {
// 避免和其他按键事件冲突
setTimeout(() => {
this.$emit(keyName, {})
}, 0)
}
}
document.addEventListener('keyup', listener)
this.$once('hook:beforeDestroy', () => {
document.removeEventListener('keyup', listener)
})
},
render: () => {}
}
// #endif

927
uni_modules/uni-datetime-picker/components/uni-datetime-picker/time-picker.vue

@ -0,0 +1,927 @@
<template>
<view class="uni-datetime-picker">
<view @click="initTimePicker">
<slot>
<view class="uni-datetime-picker-timebox-pointer"
:class="{'uni-datetime-picker-disabled': disabled, 'uni-datetime-picker-timebox': border}">
<text class="uni-datetime-picker-text">{{time}}</text>
<view v-if="!time" class="uni-datetime-picker-time">
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
</view>
</view>
</slot>
</view>
<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
:style="fixNvueBug">
<view class="uni-title">
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
</view>
<view v-if="dateShow" class="uni-datetime-picker__container-box">
<picker-view class="uni-datetime-picker-view" :indicator-style="indicatorStyle" :value="ymd"
@change="bindDateChange">
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in years" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in months" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in days" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
</picker-view>
<!-- 兼容 nvue 不支持伪类 -->
<text class="uni-datetime-picker-sign sign-left">-</text>
<text class="uni-datetime-picker-sign sign-right">-</text>
</view>
<view v-if="timeShow" class="uni-datetime-picker__container-box">
<picker-view class="uni-datetime-picker-view" :class="[hideSecond ? 'time-hide-second' : '']"
:indicator-style="indicatorStyle" :value="hms" @change="bindTimeChange">
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in hours" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column>
<view class="uni-datetime-picker-item" v-for="(item,index) in minutes" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
<picker-view-column v-if="!hideSecond">
<view class="uni-datetime-picker-item" v-for="(item,index) in seconds" :key="index">
<text class="uni-datetime-picker-item">{{lessThanTen(item)}}</text>
</view>
</picker-view-column>
</picker-view>
<!-- 兼容 nvue 不支持伪类 -->
<text class="uni-datetime-picker-sign" :class="[hideSecond ? 'sign-center' : 'sign-left']">:</text>
<text v-if="!hideSecond" class="uni-datetime-picker-sign sign-right">:</text>
</view>
<view class="uni-datetime-picker-btn">
<view @click="clearTime">
<text class="uni-datetime-picker-btn-text">{{clearText}}</text>
</view>
<view class="uni-datetime-picker-btn-group">
<view class="uni-datetime-picker-cancel" @click="tiggerTimePicker">
<text class="uni-datetime-picker-btn-text">{{cancelText}}</text>
</view>
<view @click="setTime">
<text class="uni-datetime-picker-btn-text">{{okText}}</text>
</view>
</view>
</view>
</view>
<!-- #ifdef H5 -->
<!-- <keypress v-if="visible" @esc="tiggerTimePicker" @enter="setTime" /> -->
<!-- #endif -->
</view>
</template>
<script>
// #ifdef H5
import keypress from './keypress'
// #endif
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const { t } = initVueI18n(messages)
/**
* DatetimePicker 时间选择器
* @description 可以同时选择日期和时间的选择器
* @tutorial https://ext.dcloud.net.cn/plugin?id=xxx
* @property {String} type = [datetime | date | time] 显示模式
* @property {Boolean} multiple = [true|false] 是否多选
* @property {String|Number} value 默认值
* @property {String|Number} start 起始日期或时间
* @property {String|Number} end 起始日期或时间
* @property {String} return-type = [timestamp | string]
* @event {Function} change 选中发生变化触发
*/
export default {
name: 'UniDatetimePicker',
components: {
// #ifdef H5
keypress
// #endif
},
data() {
return {
indicatorStyle: `height: 50px;`,
visible: false,
fixNvueBug: {},
dateShow: true,
timeShow: true,
title: '日期和时间',
//
time: '',
//
year: 1920,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
//
startYear: 1920,
startMonth: 1,
startDay: 1,
startHour: 0,
startMinute: 0,
startSecond: 0,
//
endYear: 2120,
endMonth: 12,
endDay: 31,
endHour: 23,
endMinute: 59,
endSecond: 59,
}
},
props: {
type: {
type: String,
default: 'datetime'
},
value: {
type: [String, Number],
default: ''
},
modelValue: {
type: [String, Number],
default: ''
},
start: {
type: [Number, String],
default: ''
},
end: {
type: [Number, String],
default: ''
},
returnType: {
type: String,
default: 'string'
},
disabled: {
type: [Boolean, String],
default: false
},
border: {
type: [Boolean, String],
default: true
},
hideSecond: {
type: [Boolean, String],
default: false
}
},
watch: {
value: {
handler(newVal, oldVal) {
if (newVal) {
this.parseValue(this.fixIosDateFormat(newVal)) // iOSsafari
this.initTime(false)
} else {
this.time = ''
this.parseValue(Date.now())
}
},
immediate: true
},
type: {
handler(newValue) {
if (newValue === 'date') {
this.dateShow = true
this.timeShow = false
this.title = '日期'
} else if (newValue === 'time') {
this.dateShow = false
this.timeShow = true
this.title = '时间'
} else {
this.dateShow = true
this.timeShow = true
this.title = '日期和时间'
}
},
immediate: true
},
start: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'start') // iOSsafari
},
immediate: true
},
end: {
handler(newVal) {
this.parseDatetimeRange(this.fixIosDateFormat(newVal), 'end') // iOSsafari
},
immediate: true
},
//
months(newVal) {
this.checkValue('month', this.month, newVal)
},
days(newVal) {
this.checkValue('day', this.day, newVal)
},
hours(newVal) {
this.checkValue('hour', this.hour, newVal)
},
minutes(newVal) {
this.checkValue('minute', this.minute, newVal)
},
seconds(newVal) {
this.checkValue('second', this.second, newVal)
}
},
computed: {
//
years() {
return this.getCurrentRange('year')
},
months() {
return this.getCurrentRange('month')
},
days() {
return this.getCurrentRange('day')
},
hours() {
return this.getCurrentRange('hour')
},
minutes() {
return this.getCurrentRange('minute')
},
seconds() {
return this.getCurrentRange('second')
},
// picker
ymd() {
return [this.year - this.minYear, this.month - this.minMonth, this.day - this.minDay]
},
hms() {
return [this.hour - this.minHour, this.minute - this.minMinute, this.second - this.minSecond]
},
// date start
currentDateIsStart() {
return this.year === this.startYear && this.month === this.startMonth && this.day === this.startDay
},
// date end
currentDateIsEnd() {
return this.year === this.endYear && this.month === this.endMonth && this.day === this.endDay
},
//
minYear() {
return this.startYear
},
maxYear() {
return this.endYear
},
minMonth() {
if (this.year === this.startYear) {
return this.startMonth
} else {
return 1
}
},
maxMonth() {
if (this.year === this.endYear) {
return this.endMonth
} else {
return 12
}
},
minDay() {
if (this.year === this.startYear && this.month === this.startMonth) {
return this.startDay
} else {
return 1
}
},
maxDay() {
if (this.year === this.endYear && this.month === this.endMonth) {
return this.endDay
} else {
return this.daysInMonth(this.year, this.month)
}
},
minHour() {
if (this.type === 'datetime') {
if (this.currentDateIsStart) {
return this.startHour
} else {
return 0
}
}
if (this.type === 'time') {
return this.startHour
}
},
maxHour() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd) {
return this.endHour
} else {
return 23
}
}
if (this.type === 'time') {
return this.endHour
}
},
minMinute() {
if (this.type === 'datetime') {
if (this.currentDateIsStart && this.hour === this.startHour) {
return this.startMinute
} else {
return 0
}
}
if (this.type === 'time') {
if (this.hour === this.startHour) {
return this.startMinute
} else {
return 0
}
}
},
maxMinute() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd && this.hour === this.endHour) {
return this.endMinute
} else {
return 59
}
}
if (this.type === 'time') {
if (this.hour === this.endHour) {
return this.endMinute
} else {
return 59
}
}
},
minSecond() {
if (this.type === 'datetime') {
if (this.currentDateIsStart && this.hour === this.startHour && this.minute === this.startMinute) {
return this.startSecond
} else {
return 0
}
}
if (this.type === 'time') {
if (this.hour === this.startHour && this.minute === this.startMinute) {
return this.startSecond
} else {
return 0
}
}
},
maxSecond() {
if (this.type === 'datetime') {
if (this.currentDateIsEnd && this.hour === this.endHour && this.minute === this.endMinute) {
return this.endSecond
} else {
return 59
}
}
if (this.type === 'time') {
if (this.hour === this.endHour && this.minute === this.endMinute) {
return this.endSecond
} else {
return 59
}
}
},
/**
* for i18n
*/
selectTimeText() {
return t("uni-datetime-picker.selectTime")
},
okText() {
return t("uni-datetime-picker.ok")
},
clearText() {
return t("uni-datetime-picker.clear")
},
cancelText() {
return t("uni-datetime-picker.cancel")
}
},
mounted() {
// #ifdef APP-NVUE
const res = uni.getSystemInfoSync();
this.fixNvueBug = {
top: res.windowHeight / 2,
left: res.windowWidth / 2
}
// #endif
},
methods: {
/**
* @param {Object} item
* 小于 10 在前面加个 0
*/
lessThanTen(item) {
return item < 10 ? '0' + item : item
},
/**
* 解析时分秒字符串例如00:00:00
* @param {String} timeString
*/
parseTimeType(timeString) {
if (timeString) {
let timeArr = timeString.split(':')
this.hour = Number(timeArr[0])
this.minute = Number(timeArr[1])
this.second = Number(timeArr[2])
}
},
/**
* 解析选择器初始值类型可以是字符串时间戳例如2000-10-02'08:30:00' 1610695109000
* @param {String | Number} datetime
*/
initPickerValue(datetime) {
let defaultValue = null
if (datetime) {
defaultValue = this.compareValueWithStartAndEnd(datetime, this.start, this.end)
} else {
defaultValue = Date.now()
defaultValue = this.compareValueWithStartAndEnd(defaultValue, this.start, this.end)
}
this.parseValue(defaultValue)
},
/**
* 初始值规则
* - 用户设置初始值 value
* - 设置了起始时间 start终止时间 end start < value < end初始值为 value 否则初始值为 start
* - 只设置了起始时间 start start < value初始值为 value否则初始值为 start
* - 只设置了终止时间 end value < end初始值为 value否则初始值为 end
* - 无起始终止时间则初始值为 value
* - 无初始值 value则初始值为当前本地时间 Date.now()
* @param {Object} value
* @param {Object} dateBase
*/
compareValueWithStartAndEnd(value, start, end) {
let winner = null
value = this.superTimeStamp(value)
start = this.superTimeStamp(start)
end = this.superTimeStamp(end)
if (start && end) {
if (value < start) {
winner = new Date(start)
} else if (value > end) {
winner = new Date(end)
} else {
winner = new Date(value)
}
} else if (start && !end) {
winner = start <= value ? new Date(value) : new Date(start)
} else if (!start && end) {
winner = value <= end ? new Date(value) : new Date(end)
} else {
winner = new Date(value)
}
return winner
},
/**
* 转换为可比较的时间戳接受日期时分秒时间戳
* @param {Object} value
*/
superTimeStamp(value) {
let dateBase = ''
if (this.type === 'time' && value && typeof value === 'string') {
const now = new Date()
const year = now.getFullYear()
const month = now.getMonth() + 1
const day = now.getDate()
dateBase = year + '/' + month + '/' + day + ' '
}
if (Number(value) && typeof value !== NaN) {
value = parseInt(value)
dateBase = 0
}
return this.createTimeStamp(dateBase + value)
},
/**
* 解析默认值 value字符串时间戳
* @param {Object} defaultTime
*/
parseValue(value) {
if (!value) {
return
}
if (this.type === 'time' && typeof value === "string") {
this.parseTimeType(value)
} else {
let defaultDate = null
defaultDate = new Date(value)
if (this.type !== 'time') {
this.year = defaultDate.getFullYear()
this.month = defaultDate.getMonth() + 1
this.day = defaultDate.getDate()
}
if (this.type !== 'date') {
this.hour = defaultDate.getHours()
this.minute = defaultDate.getMinutes()
this.second = defaultDate.getSeconds()
}
}
if (this.hideSecond) {
this.second = 0
}
},
/**
* 解析可选择时间范围 startend年月日字符串时间戳
* @param {Object} defaultTime
*/
parseDatetimeRange(point, pointType) {
//
if (!point) {
if (pointType === 'start') {
this.startYear = 1920
this.startMonth = 1
this.startDay = 1
this.startHour = 0
this.startMinute = 0
this.startSecond = 0
}
if (pointType === 'end') {
this.endYear = 2120
this.endMonth = 12
this.endDay = 31
this.endHour = 23
this.endMinute = 59
this.endSecond = 59
}
return
}
if (this.type === 'time') {
const pointArr = point.split(':')
this[pointType + 'Hour'] = Number(pointArr[0])
this[pointType + 'Minute'] = Number(pointArr[1])
this[pointType + 'Second'] = Number(pointArr[2])
} else {
if (!point) {
pointType === 'start' ? this.startYear = this.year - 60 : this.endYear = this.year + 60
return
}
if (Number(point) && Number(point) !== NaN) {
point = parseInt(point)
}
// datetime end ,
const hasTime = /[0-9]:[0-9]/
if (this.type === 'datetime' && pointType === 'end' && typeof point === 'string' && !hasTime.test(
point)) {
point = point + ' 23:59:59'
}
const pointDate = new Date(point)
this[pointType + 'Year'] = pointDate.getFullYear()
this[pointType + 'Month'] = pointDate.getMonth() + 1
this[pointType + 'Day'] = pointDate.getDate()
if (this.type === 'datetime') {
this[pointType + 'Hour'] = pointDate.getHours()
this[pointType + 'Minute'] = pointDate.getMinutes()
this[pointType + 'Second'] = pointDate.getSeconds()
}
}
},
//
getCurrentRange(value) {
const range = []
for (let i = this['min' + this.capitalize(value)]; i <= this['max' + this.capitalize(value)]; i++) {
range.push(i)
}
return range
},
//
capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1)
},
//
checkValue(name, value, values) {
if (values.indexOf(value) === -1) {
this[name] = values[0]
}
},
//
daysInMonth(year, month) { // Use 1 for January, 2 for February, etc.
return new Date(year, month, 0).getDate();
},
// iOSsafari
fixIosDateFormat(value) {
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
return value
},
/**
* 生成时间戳
* @param {Object} time
*/
createTimeStamp(time) {
if (!time) return
if (typeof time === "number") {
return time
} else {
time = time.replace(/-/g, '/')
if (this.type === 'date') {
time = time + ' ' + '00:00:00'
}
return Date.parse(time)
}
},
/**
* 生成日期或时间的字符串
*/
createDomSting() {
const yymmdd = this.year +
'-' +
this.lessThanTen(this.month) +
'-' +
this.lessThanTen(this.day)
let hhmmss = this.lessThanTen(this.hour) +
':' +
this.lessThanTen(this.minute)
if (!this.hideSecond) {
hhmmss = hhmmss + ':' + this.lessThanTen(this.second)
}
if (this.type === 'date') {
return yymmdd
} else if (this.type === 'time') {
return hhmmss
} else {
return yymmdd + ' ' + hhmmss
}
},
/**
* 初始化返回值并抛出 change 事件
*/
initTime(emit = true) {
this.time = this.createDomSting()
if (!emit) return
if (this.returnType === 'timestamp' && this.type !== 'time') {
this.$emit('change', this.createTimeStamp(this.time))
this.$emit('input', this.createTimeStamp(this.time))
this.$emit('update:modelValue', this.createTimeStamp(this.time))
} else {
this.$emit('change', this.time)
this.$emit('input', this.time)
this.$emit('update:modelValue', this.time)
}
},
/**
* 用户选择日期或时间更新 data
* @param {Object} e
*/
bindDateChange(e) {
const val = e.detail.value
this.year = this.years[val[0]]
this.month = this.months[val[1]]
this.day = this.days[val[2]]
},
bindTimeChange(e) {
const val = e.detail.value
this.hour = this.hours[val[0]]
this.minute = this.minutes[val[1]]
this.second = this.seconds[val[2]]
},
/**
* 初始化弹出层
*/
initTimePicker() {
if (this.disabled) return
const value = this.fixIosDateFormat(this.value)
this.initPickerValue(value)
this.visible = !this.visible
},
/**
* 触发或关闭弹框
*/
tiggerTimePicker(e) {
this.visible = !this.visible
},
/**
* 用户点击清空按钮清空当前值
*/
clearTime() {
this.time = ''
this.$emit('change', this.time)
this.$emit('input', this.time)
this.$emit('update:modelValue', this.time)
this.tiggerTimePicker()
},
/**
* 用户点击确定按钮
*/
setTime() {
this.initTime()
this.tiggerTimePicker()
}
}
}
</script>
<style>
.uni-datetime-picker {
/* #ifndef APP-NVUE */
/* width: 100%; */
/* #endif */
}
.uni-datetime-picker-view {
height: 130px;
width: 270px;
/* #ifndef APP-NVUE */
cursor: pointer;
/* #endif */
}
.uni-datetime-picker-item {
height: 50px;
line-height: 50px;
text-align: center;
font-size: 14px;
}
.uni-datetime-picker-btn {
margin-top: 60px;
/* #ifndef APP-NVUE */
display: flex;
cursor: pointer;
/* #endif */
flex-direction: row;
justify-content: space-between;
}
.uni-datetime-picker-btn-text {
font-size: 14px;
color: #007AFF;
}
.uni-datetime-picker-btn-group {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.uni-datetime-picker-cancel {
margin-right: 30px;
}
.uni-datetime-picker-mask {
position: fixed;
bottom: 0px;
top: 0px;
left: 0px;
right: 0px;
background-color: rgba(0, 0, 0, 0.4);
transition-duration: 0.3s;
z-index: 998;
}
.uni-datetime-picker-popup {
border-radius: 8px;
padding: 30px;
width: 270px;
/* #ifdef APP-NVUE */
height: 500px;
/* #endif */
/* #ifdef APP-NVUE */
width: 330px;
/* #endif */
background-color: #fff;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
transition-duration: 0.3s;
z-index: 999;
}
.fix-nvue-height {
/* #ifdef APP-NVUE */
height: 330px;
/* #endif */
}
.uni-datetime-picker-time {
color: grey;
}
.uni-datetime-picker-column {
height: 50px;
}
.uni-datetime-picker-timebox {
border: 1px solid #E5E5E5;
border-radius: 5px;
padding: 7px 10px;
/* #ifndef APP-NVUE */
box-sizing: border-box;
cursor: pointer;
/* #endif */
}
.uni-datetime-picker-timebox-pointer {
/* #ifndef APP-NVUE */
cursor: pointer;
/* #endif */
}
.uni-datetime-picker-disabled {
opacity: 0.4;
/* #ifdef H5 */
cursor: not-allowed !important;
/* #endif */
}
.uni-datetime-picker-text {
font-size: 14px;
}
.uni-datetime-picker-sign {
position: absolute;
top: 53px;
/* 减掉 10px 的元素高度,兼容nvue */
color: #999;
/* #ifdef APP-NVUE */
font-size: 16px;
/* #endif */
}
.sign-left {
left: 86px;
}
.sign-right {
right: 86px;
}
.sign-center {
left: 135px;
}
.uni-datetime-picker__container-box {
position: relative;
display: flex;
align-items: center;
justify-content: center;
margin-top: 40px;
}
.time-hide-second {
width: 180px;
}
</style>

981
uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue

@ -0,0 +1,981 @@
<template>
<view class="uni-date">
<view class="uni-date-editor" @click="show">
<slot>
<view class="uni-date-editor--x" :class="{'uni-date-editor--x__disabled': disabled,
'uni-date-x--border': border}">
<view v-if="!isRange" class="uni-date-x uni-date-single">
<uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons>
<input class="uni-date__x-input" type="text" v-model="singleVal"
:placeholder="singlePlaceholderText" :disabled="true" />
</view>
<view v-else class="uni-date-x uni-date-range">
<uni-icons type="calendar" color="#e1e1e1" size="22"></uni-icons>
<input class="uni-date__x-input t-c" type="text" v-model="range.startDate"
:placeholder="startPlaceholderText" :disabled="true" />
<slot>
<view class="">{{rangeSeparator}}</view>
</slot>
<input class="uni-date__x-input t-c" type="text" v-model="range.endDate"
:placeholder="endPlaceholderText" :disabled="true" />
</view>
<view v-if="showClearIcon" class="uni-date__icon-clear" @click.stop="clear">
<uni-icons type="clear" color="#e1e1e1" size="18"></uni-icons>
</view>
</view>
</slot>
</view>
<view v-show="popup" class="uni-date-mask" @click="close"></view>
<view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
<view v-if="!isRange" class="uni-date-single--x" :style="popover">
<view class="uni-popper__arrow"></view>
<view v-if="hasTime" class="uni-date-changed popup-x-header">
<input class="uni-date__input t-c" type="text" v-model="tempSingleDate"
:placeholder="selectDateText" />
<time-picker type="time" v-model="time" :border="false" :disabled="!tempSingleDate"
:start="reactStartTime" :end="reactEndTime" :hideSecond="hideSecond" style="width: 100%;">
<input class="uni-date__input t-c" type="text" v-model="time" :placeholder="selectTimeText"
:disabled="!tempSingleDate" />
</time-picker>
</view>
<calendar ref="pcSingle" :showMonth="false"
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :date="defSingleDate"
@change="singleChange" style="padding: 0 8px;" />
<view v-if="hasTime" class="popup-x-footer">
<!-- <text class="">此刻</text> -->
<text class="confirm" @click="confirmSingleChange">{{okText}}</text>
</view>
<view class="uni-date-popper__arrow"></view>
</view>
<view v-else class="uni-date-range--x" :style="popover">
<view class="uni-popper__arrow"></view>
<view v-if="hasTime" class="popup-x-header uni-date-changed">
<view class="popup-x-header--datetime">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.startDate"
:placeholder="startDateText" />
<time-picker type="time" v-model="tempRange.startTime" :start="reactStartTime" :border="false"
:disabled="!tempRange.startDate" :hideSecond="hideSecond">
<input class="uni-date__input uni-date-range__input" type="text"
v-model="tempRange.startTime" :placeholder="startTimeText"
:disabled="!tempRange.startDate" />
</time-picker>
</view>
<uni-icons type="arrowthinright" color="#999" style="line-height: 40px;"></uni-icons>
<view class="popup-x-header--datetime">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endDate"
:placeholder="endDateText" />
<time-picker type="time" v-model="tempRange.endTime" :end="reactEndTime" :border="false"
:disabled="!tempRange.endDate" :hideSecond="hideSecond">
<input class="uni-date__input uni-date-range__input" type="text" v-model="tempRange.endTime"
:placeholder="endTimeText" :disabled="!tempRange.endDate" />
</time-picker>
</view>
</view>
<view class="popup-x-body">
<calendar ref="left" :showMonth="false"
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true"
@change="leftChange" :pleStatus="endMultipleStatus" @firstEnterCale="updateRightCale"
@monthSwitch="leftMonthSwitch" style="padding: 0 8px;" />
<calendar ref="right" :showMonth="false"
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :range="true"
@change="rightChange" :pleStatus="startMultipleStatus" @firstEnterCale="updateLeftCale"
@monthSwitch="rightMonthSwitch" style="padding: 0 8px;border-left: 1px solid #F1F1F1;" />
</view>
<view v-if="hasTime" class="popup-x-footer">
<text class="" @click="clear">{{clearText}}</text>
<text class="confirm" @click="confirmRangeChange">{{okText}}</text>
</view>
</view>
</view>
<calendar v-show="isPhone" ref="mobile" :clearDate="false" :date="defSingleDate" :defTime="reactMobDefTime"
:start-date="caleRange.startDate" :end-date="caleRange.endDate" :selectableTimes="mobSelectableTime"
:pleStatus="endMultipleStatus" :showMonth="false" :range="isRange" :typeHasTime="hasTime" :insert="false"
:hideSecond="hideSecond" @confirm="mobileChange" />
</view>
</template>
<script>
/**
* DatetimePicker 时间选择器
* @description 同时支持 PC 和移动端使用日历选择日期和日期范围
* @tutorial https://ext.dcloud.net.cn/plugin?id=3962
* @property {String} type 选择器类型
* @property {String|Number|Array|Date} value 绑定值
* @property {String} placeholder 单选择时的占位内容
* @property {String} start 起始时间
* @property {String} end 终止时间
* @property {String} start-placeholder 范围选择时开始日期的占位内容
* @property {String} end-placeholder 范围选择时结束日期的占位内容
* @property {String} range-separator 选择范围时的分隔符
* @property {Boolean} border = [true|false] 是否有边框
* @property {Boolean} disabled = [true|false] 是否禁用
* @property {Boolean} clearIcon = [true|false] 是否显示清除按钮仅PC端适用
* @event {Function} change 确定日期时触发的事件
* @event {Function} show 打开弹出层
* @event {Function} close 关闭弹出层
* @event {Function} clear 清除上次选中的状态和值
**/
import calendar from './calendar.vue'
import timePicker from './time-picker.vue'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import messages from './i18n/index.js'
const {
t
} = initVueI18n(messages)
export default {
name: 'UniDatetimePicker',
components: {
calendar,
timePicker
},
data() {
return {
isRange: false,
hasTime: false,
mobileRange: false,
//
singleVal: '',
tempSingleDate: '',
defSingleDate: '',
time: '',
//
caleRange: {
startDate: '',
startTime: '',
endDate: '',
endTime: ''
},
range: {
startDate: '',
// startTime: '',
endDate: '',
// endTime: ''
},
tempRange: {
startDate: '',
startTime: '',
endDate: '',
endTime: ''
},
//
startMultipleStatus: {
before: '',
after: '',
data: [],
fulldate: ''
},
endMultipleStatus: {
before: '',
after: '',
data: [],
fulldate: ''
},
visible: false,
popup: false,
popover: null,
isEmitValue: false,
isPhone: false,
isFirstShow: true,
}
},
props: {
type: {
type: String,
default: 'datetime'
},
value: {
type: [String, Number, Array, Date],
default: ''
},
modelValue: {
type: [String, Number, Array, Date],
default: ''
},
start: {
type: [Number, String],
default: ''
},
end: {
type: [Number, String],
default: ''
},
returnType: {
type: String,
default: 'string'
},
placeholder: {
type: String,
default: ''
},
startPlaceholder: {
type: String,
default: ''
},
endPlaceholder: {
type: String,
default: ''
},
rangeSeparator: {
type: String,
default: '-'
},
border: {
type: [Boolean],
default: true
},
disabled: {
type: [Boolean],
default: false
},
clearIcon: {
type: [Boolean],
default: true
},
hideSecond: {
type: [Boolean],
default: false
}
},
watch: {
type: {
immediate: true,
handler(newVal, oldVal) {
if (newVal.indexOf('time') !== -1) {
this.hasTime = true
} else {
this.hasTime = false
}
if (newVal.indexOf('range') !== -1) {
this.isRange = true
} else {
this.isRange = false
}
}
},
value: {
immediate: true,
handler(newVal, oldVal) {
if (this.isEmitValue) {
this.isEmitValue = false
return
}
this.initPicker(newVal)
}
},
start: {
immediate: true,
handler(newVal, oldVal) {
if (!newVal) return
const {
defDate,
defTime
} = this.parseDate(newVal)
this.caleRange.startDate = defDate
if (this.hasTime) {
this.caleRange.startTime = defTime
}
}
},
end: {
immediate: true,
handler(newVal, oldVal) {
if (!newVal) return
const {
defDate,
defTime
} = this.parseDate(newVal)
this.caleRange.endDate = defDate
if (this.hasTime) {
this.caleRange.endTime = defTime
}
}
},
},
computed: {
reactStartTime() {
const activeDate = this.isRange ? this.tempRange.startDate : this.tempSingleDate
const res = activeDate === this.caleRange.startDate ? this.caleRange.startTime : ''
return res
},
reactEndTime() {
const activeDate = this.isRange ? this.tempRange.endDate : this.tempSingleDate
const res = activeDate === this.caleRange.endDate ? this.caleRange.endTime : ''
return res
},
reactMobDefTime() {
const times = {
start: this.tempRange.startTime,
end: this.tempRange.endTime
}
return this.isRange ? times : this.time
},
mobSelectableTime() {
return {
start: this.caleRange.startTime,
end: this.caleRange.endTime
}
},
datePopupWidth() {
// todo
return this.isRange ? 653 : 301
},
/**
* for i18n
*/
singlePlaceholderText() {
return this.placeholder || (this.type === 'date' ? this.selectDateText : t(
"uni-datetime-picker.selectDateTime"))
},
startPlaceholderText() {
return this.startPlaceholder || this.startDateText
},
endPlaceholderText() {
return this.endPlaceholder || this.endDateText
},
selectDateText() {
return t("uni-datetime-picker.selectDate")
},
selectTimeText() {
return t("uni-datetime-picker.selectTime")
},
startDateText() {
return this.startPlaceholder || t("uni-datetime-picker.startDate")
},
startTimeText() {
return t("uni-datetime-picker.startTime")
},
endDateText() {
return this.endPlaceholder || t("uni-datetime-picker.endDate")
},
endTimeText() {
return t("uni-datetime-picker.endTime")
},
okText() {
return t("uni-datetime-picker.ok")
},
clearText() {
return t("uni-datetime-picker.clear")
},
showClearIcon() {
const { clearIcon, disabled, singleVal, range } = this
const bool = clearIcon && !disabled && (singleVal || (range.startDate && range.endDate))
return bool
}
},
created() {
this.form = this.getForm('uniForms')
this.formItem = this.getForm('uniFormsItem')
// if (this.formItem) {
// if (this.formItem.name) {
// this.rename = this.formItem.name
// this.form.inputChildrens.push(this)
// }
// }
},
mounted() {
this.platform()
},
methods: {
/**
* 获取父元素实例
*/
getForm(name = 'uniForms') {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== name) {
parent = parent.$parent;
if (!parent) return false
parentName = parent.$options.name;
}
return parent;
},
initPicker(newVal) {
if (!newVal || Array.isArray(newVal) && !newVal.length) {
this.$nextTick(() => {
this.clear(false)
})
return
}
if (!Array.isArray(newVal) && !this.isRange) {
const {
defDate,
defTime
} = this.parseDate(newVal)
this.singleVal = defDate
this.tempSingleDate = defDate
this.defSingleDate = defDate
if (this.hasTime) {
this.singleVal = defDate + ' ' + defTime
this.time = defTime
}
} else {
const [before, after] = newVal
if (!before && !after) return
const defBefore = this.parseDate(before)
const defAfter = this.parseDate(after)
const startDate = defBefore.defDate
const endDate = defAfter.defDate
this.range.startDate = this.tempRange.startDate = startDate
this.range.endDate = this.tempRange.endDate = endDate
if (this.hasTime) {
this.range.startDate = defBefore.defDate + ' ' + defBefore.defTime
this.range.endDate = defAfter.defDate + ' ' + defAfter.defTime
this.tempRange.startTime = defBefore.defTime
this.tempRange.endTime = defAfter.defTime
}
const defaultRange = {
before: defBefore.defDate,
after: defAfter.defDate
}
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, defaultRange, {
which: 'right'
})
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, defaultRange, {
which: 'left'
})
}
},
updateLeftCale(e) {
const left = this.$refs.left
//
left.cale.setHoverMultiple(e.after)
left.setDate(this.$refs.left.nowDate.fullDate)
},
updateRightCale(e) {
const right = this.$refs.right
//
right.cale.setHoverMultiple(e.after)
right.setDate(this.$refs.right.nowDate.fullDate)
},
platform() {
const systemInfo = uni.getSystemInfoSync()
this.isPhone = systemInfo.windowWidth <= 500
this.windowWidth = systemInfo.windowWidth
},
show(event) {
if (this.disabled) {
return
}
this.platform()
if (this.isPhone) {
this.$refs.mobile.open()
return
}
this.popover = {
top: '10px'
}
const dateEditor = uni.createSelectorQuery().in(this).select(".uni-date-editor")
dateEditor.boundingClientRect(rect => {
if (this.windowWidth - rect.left < this.datePopupWidth) {
this.popover.right = 0
}
}).exec()
setTimeout(() => {
this.popup = !this.popup
if (!this.isPhone && this.isRange && this.isFirstShow) {
this.isFirstShow = false
const {
startDate,
endDate
} = this.range
if (startDate && endDate) {
if (this.diffDate(startDate, endDate) < 30) {
this.$refs.right.next()
}
} else {
this.$refs.right.next()
this.$refs.right.cale.lastHover = false
}
}
}, 50)
},
close() {
setTimeout(() => {
this.popup = false
this.$emit('maskClick', this.value)
}, 20)
},
setEmit(value) {
if (this.returnType === "timestamp" || this.returnType === "date") {
if (!Array.isArray(value)) {
if (!this.hasTime) {
value = value + ' ' + '00:00:00'
}
value = this.createTimestamp(value)
if (this.returnType === "date") {
value = new Date(value)
}
} else {
if (!this.hasTime) {
value[0] = value[0] + ' ' + '00:00:00'
value[1] = value[1] + ' ' + '00:00:00'
}
value[0] = this.createTimestamp(value[0])
value[1] = this.createTimestamp(value[1])
if (this.returnType === "date") {
value[0] = new Date(value[0])
value[1] = new Date(value[1])
}
}
}
this.formItem && this.formItem.setValue(value)
this.$emit('change', value)
this.$emit('input', value)
this.$emit('update:modelValue', value)
this.isEmitValue = true
},
createTimestamp(date) {
date = this.fixIosDateFormat(date)
return Date.parse(new Date(date))
},
singleChange(e) {
this.tempSingleDate = e.fulldate
if (this.hasTime) return
this.confirmSingleChange()
},
confirmSingleChange() {
if (!this.tempSingleDate) {
this.popup = false
return
}
if (this.hasTime) {
this.singleVal = this.tempSingleDate + ' ' + (this.time ? this.time : '00:00:00')
} else {
this.singleVal = this.tempSingleDate
}
this.setEmit(this.singleVal)
this.popup = false
},
leftChange(e) {
const {
before,
after
} = e.range
this.rangeChange(before, after)
const obj = {
before: e.range.before,
after: e.range.after,
data: e.range.data,
fulldate: e.fulldate
}
this.startMultipleStatus = Object.assign({}, this.startMultipleStatus, obj)
},
rightChange(e) {
const {
before,
after
} = e.range
this.rangeChange(before, after)
const obj = {
before: e.range.before,
after: e.range.after,
data: e.range.data,
fulldate: e.fulldate
}
this.endMultipleStatus = Object.assign({}, this.endMultipleStatus, obj)
},
mobileChange(e) {
if (this.isRange) {
const {
before,
after
} = e.range
this.handleStartAndEnd(before, after, true)
if (this.hasTime) {
const {
startTime,
endTime
} = e.timeRange
this.tempRange.startTime = startTime
this.tempRange.endTime = endTime
}
this.confirmRangeChange()
} else {
if (this.hasTime) {
this.singleVal = e.fulldate + ' ' + e.time
} else {
this.singleVal = e.fulldate
}
this.setEmit(this.singleVal)
}
this.$refs.mobile.close()
},
rangeChange(before, after) {
if (!(before && after)) return
this.handleStartAndEnd(before, after, true)
if (this.hasTime) return
this.confirmRangeChange()
},
confirmRangeChange() {
if (!this.tempRange.startDate && !this.tempRange.endDate) {
this.popup = false
return
}
let start, end
if (!this.hasTime) {
start = this.range.startDate = this.tempRange.startDate
end = this.range.endDate = this.tempRange.endDate
} else {
start = this.range.startDate = this.tempRange.startDate + ' ' +
(this.tempRange.startTime ? this.tempRange.startTime : '00:00:00')
end = this.range.endDate = this.tempRange.endDate + ' ' +
(this.tempRange.endTime ? this.tempRange.endTime : '00:00:00')
}
const displayRange = [start, end]
this.setEmit(displayRange)
this.popup = false
},
handleStartAndEnd(before, after, temp = false) {
if (!(before && after)) return
const type = temp ? 'tempRange' : 'range'
if (this.dateCompare(before, after)) {
this[type].startDate = before
this[type].endDate = after
} else {
this[type].startDate = after
this[type].endDate = before
}
},
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
//
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
//
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
},
/**
* 比较时间差
*/
diffDate(startDate, endDate) {
//
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
//
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
const diff = (endDate - startDate) / (24 * 60 * 60 * 1000)
return Math.abs(diff)
},
clear(needEmit = true) {
if (!this.isRange) {
this.singleVal = ''
this.tempSingleDate = ''
this.time = ''
if (this.isPhone) {
this.$refs.mobile && this.$refs.mobile.clearCalender()
} else {
this.$refs.pcSingle && this.$refs.pcSingle.clearCalender()
}
if (needEmit) {
this.formItem && this.formItem.setValue('')
this.$emit('change', '')
this.$emit('input', '')
this.$emit('update:modelValue', '')
}
} else {
this.range.startDate = ''
this.range.endDate = ''
this.tempRange.startDate = ''
this.tempRange.startTime = ''
this.tempRange.endDate = ''
this.tempRange.endTime = ''
if (this.isPhone) {
this.$refs.mobile && this.$refs.mobile.clearCalender()
} else {
this.$refs.left && this.$refs.left.clearCalender()
this.$refs.right && this.$refs.right.clearCalender()
this.$refs.right && this.$refs.right.next()
}
if (needEmit) {
this.formItem && this.formItem.setValue([])
this.$emit('change', [])
this.$emit('input', [])
this.$emit('update:modelValue', [])
}
}
},
parseDate(date) {
date = this.fixIosDateFormat(date)
const defVal = new Date(date)
const year = defVal.getFullYear()
const month = defVal.getMonth() + 1
const day = defVal.getDate()
const hour = defVal.getHours()
const minute = defVal.getMinutes()
const second = defVal.getSeconds()
const defDate = year + '-' + this.lessTen(month) + '-' + this.lessTen(day)
const defTime = this.lessTen(hour) + ':' + this.lessTen(minute) + (this.hideSecond ? '' : (':' + this
.lessTen(second)))
return {
defDate,
defTime
}
},
lessTen(item) {
return item < 10 ? '0' + item : item
},
// iOSsafari
fixIosDateFormat(value) {
if (typeof value === 'string') {
value = value.replace(/-/g, '/')
}
return value
},
leftMonthSwitch(e) {
// console.log('leftMonthSwitch :', e)
},
rightMonthSwitch(e) {
// console.log('rightMonthSwitch :', e)
}
}
}
</script>
<style>
.uni-date-x {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 0 10px;
border-radius: 4px;
background-color: #fff;
color: #666;
font-size: 14px;
}
.uni-date-x--border {
box-sizing: border-box;
border-radius: 4px;
border: 1px solid #dcdfe6;
}
.uni-date-editor--x {
position: relative;
}
.uni-date-editor--x .uni-date__icon-clear {
position: absolute;
top: 0;
right: 0;
display: inline-block;
box-sizing: border-box;
border: 9px solid transparent;
/* #ifdef H5 */
cursor: pointer;
/* #endif */
}
.uni-date__x-input {
padding: 0 8px;
height: 40px;
width: 100%;
line-height: 40px;
font-size: 14px;
}
.t-c {
text-align: center;
}
.uni-date__input {
height: 40px;
width: 100%;
line-height: 40px;
font-size: 14px;
}
.uni-date-range__input {
text-align: center;
max-width: 142px;
}
.uni-date-picker__container {
position: relative;
/* position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
box-sizing: border-box;
z-index: 996;
font-size: 14px; */
}
.uni-date-mask {
position: fixed;
bottom: 0px;
top: 0px;
left: 0px;
right: 0px;
background-color: rgba(0, 0, 0, 0);
transition-duration: 0.3s;
z-index: 996;
}
.uni-date-single--x {
/* padding: 0 8px; */
background-color: #fff;
position: absolute;
top: 0;
z-index: 999;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.uni-date-range--x {
/* padding: 0 8px; */
background-color: #fff;
position: absolute;
top: 0;
z-index: 999;
border: 1px solid #EBEEF5;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.uni-date-editor--x__disabled {
opacity: 0.4;
cursor: default;
}
.uni-date-editor--logo {
width: 16px;
height: 16px;
vertical-align: middle;
}
/* 添加时间 */
.popup-x-header {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
/* justify-content: space-between; */
}
.popup-x-header--datetime {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex: 1;
}
.popup-x-body {
display: flex;
}
.popup-x-footer {
padding: 0 15px;
border-top-color: #F1F1F1;
border-top-style: solid;
border-top-width: 1px;
/* background-color: #fff; */
line-height: 40px;
text-align: right;
color: #666;
}
.popup-x-footer text:hover {
color: #007aff;
cursor: pointer;
opacity: 0.8;
}
.popup-x-footer .confirm {
margin-left: 20px;
color: #007aff;
}
.uni-date-changed {
/* background-color: #fff; */
text-align: center;
color: #333;
border-bottom-color: #F1F1F1;
border-bottom-style: solid;
border-bottom-width: 1px;
/* padding: 0 50px; */
}
.uni-date-changed--time text {
/* padding: 0 20px; */
height: 50px;
line-height: 50px;
}
.uni-date-changed .uni-date-changed--time {
/* display: flex; */
flex: 1;
}
.uni-date-changed--time-date {
color: #333;
opacity: 0.6;
}
.mr-50 {
margin-right: 50px;
}
/* picker 弹出层通用的指示小三角, todo:扩展至上下左右方向定位 */
.uni-popper__arrow,
.uni-popper__arrow::after {
position: absolute;
display: block;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
}
.uni-popper__arrow {
filter: drop-shadow(0 2px 12px rgba(0, 0, 0, 0.03));
top: -6px;
left: 10%;
margin-right: 3px;
border-top-width: 0;
border-bottom-color: #EBEEF5;
}
.uni-popper__arrow::after {
content: " ";
top: 1px;
margin-left: -6px;
border-top-width: 0;
border-bottom-color: #fff;
}
</style>

410
uni_modules/uni-datetime-picker/components/uni-datetime-picker/util.js

@ -0,0 +1,410 @@
class Calendar {
constructor({
date,
selected,
startDate,
endDate,
range,
// multipleStatus
} = {}) {
// 当前日期
this.date = this.getDate(new Date()) // 当前初入日期
// 打点信息
this.selected = selected || [];
// 范围开始
this.startDate = startDate
// 范围结束
this.endDate = endDate
this.range = range
// 多选状态
this.cleanMultipleStatus()
// 每周日期
this.weeks = {}
// this._getWeek(this.date.fullDate)
// this.multipleStatus = multipleStatus
this.lastHover = false
}
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.selectDate = this.getDate(date)
this._getWeek(this.selectDate.fullDate)
}
/**
* 清理多选状态
*/
cleanMultipleStatus() {
this.multipleStatus = {
before: '',
after: '',
data: []
}
}
/**
* 重置开始日期
*/
resetSatrtDate(startDate) {
// 范围开始
this.startDate = startDate
}
/**
* 重置结束日期
*/
resetEndDate(endDate) {
// 范围结束
this.endDate = endDate
}
/**
* 获取任意时间
*/
getDate(date, AddDayCount = 0, str = 'day') {
if (!date) {
date = new Date()
}
if (typeof date !== 'object') {
date = date.replace(/-/g, '/')
}
const dd = new Date(date)
switch (str) {
case 'day':
dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
break
case 'month':
if (dd.getDate() === 31) {
dd.setDate(dd.getDate() + AddDayCount)
} else {
dd.setMonth(dd.getMonth() + AddDayCount) // 获取AddDayCount天后的日期
}
break
case 'year':
dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
break
}
const y = dd.getFullYear()
const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
return {
fullDate: y + '-' + m + '-' + d,
year: y,
month: m,
date: d,
day: dd.getDay()
}
}
/**
* 获取上月剩余天数
*/
_getLastMonthDays(firstDay, full) {
let dateArr = []
for (let i = firstDay; i > 0; i--) {
const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
dateArr.push({
date: beforeDate,
month: full.month - 1,
disable: true
})
}
return dateArr
}
/**
* 获取本月天数
*/
_currentMonthDys(dateData, full) {
let dateArr = []
let fullDate = this.date.fullDate
for (let i = 1; i <= dateData; i++) {
let isinfo = false
let nowDate = full.year + '-' + (full.month < 10 ?
full.month : full.month) + '-' + (i < 10 ?
'0' + i : i)
// 是否今天
let isDay = fullDate === nowDate
// 获取打点信息
let info = this.selected && this.selected.find((item) => {
if (this.dateEqual(nowDate, item.date)) {
return item
}
})
// 日期禁用
let disableBefore = true
let disableAfter = true
if (this.startDate) {
// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
disableBefore = this.dateCompare(this.startDate, nowDate)
}
if (this.endDate) {
// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
disableAfter = this.dateCompare(nowDate, this.endDate)
}
let multiples = this.multipleStatus.data
let checked = false
let multiplesStatus = -1
if (this.range) {
if (multiples) {
multiplesStatus = multiples.findIndex((item) => {
return this.dateEqual(item, nowDate)
})
}
if (multiplesStatus !== -1) {
checked = true
}
}
let data = {
fullDate: nowDate,
year: full.year,
date: i,
multiple: this.range ? checked : false,
beforeMultiple: this.isLogicBefore(nowDate, this.multipleStatus.before, this.multipleStatus.after),
afterMultiple: this.isLogicAfter(nowDate, this.multipleStatus.before, this.multipleStatus.after),
month: full.month,
disable: !(disableBefore && disableAfter),
isDay,
userChecked: false
}
if (info) {
data.extraInfo = info
}
dateArr.push(data)
}
return dateArr
}
/**
* 获取下月天数
*/
_getNextMonthDays(surplus, full) {
let dateArr = []
for (let i = 1; i < surplus + 1; i++) {
dateArr.push({
date: i,
month: Number(full.month) + 1,
disable: true
})
}
return dateArr
}
/**
* 获取当前日期详情
* @param {Object} date
*/
getInfo(date) {
if (!date) {
date = new Date()
}
const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
return dateInfo
}
/**
* 比较时间大小
*/
dateCompare(startDate, endDate) {
// 计算截止时间
startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
if (startDate <= endDate) {
return true
} else {
return false
}
}
/**
* 比较时间是否相等
*/
dateEqual(before, after) {
// 计算截止时间
before = new Date(before.replace('-', '/').replace('-', '/'))
// 计算详细项的截止时间
after = new Date(after.replace('-', '/').replace('-', '/'))
if (before.getTime() - after.getTime() === 0) {
return true
} else {
return false
}
}
/**
* 比较真实起始日期
*/
isLogicBefore(currentDay, before, after) {
let logicBefore = before
if (before && after) {
logicBefore = this.dateCompare(before, after) ? before : after
}
return this.dateEqual(logicBefore, currentDay)
}
isLogicAfter(currentDay, before, after) {
let logicAfter = after
if (before && after) {
logicAfter = this.dateCompare(before, after) ? after : before
}
return this.dateEqual(logicAfter, currentDay)
}
/**
* 获取日期范围内所有日期
* @param {Object} begin
* @param {Object} end
*/
geDateAll(begin, end) {
var arr = []
var ab = begin.split('-')
var ae = end.split('-')
var db = new Date()
db.setFullYear(ab[0], ab[1] - 1, ab[2])
var de = new Date()
de.setFullYear(ae[0], ae[1] - 1, ae[2])
var unixDb = db.getTime() - 24 * 60 * 60 * 1000
var unixDe = de.getTime() - 24 * 60 * 60 * 1000
for (var k = unixDb; k <= unixDe;) {
k = k + 24 * 60 * 60 * 1000
arr.push(this.getDate(new Date(parseInt(k))).fullDate)
}
return arr
}
/**
* 获取多选状态
*/
setMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (before && after) {
if (!this.lastHover) {
this.lastHover = true
return
}
this.multipleStatus.before = fullDate
this.multipleStatus.after = ''
this.multipleStatus.data = []
this.multipleStatus.fulldate = ''
this.lastHover = false
} else {
if (!before) {
this.multipleStatus.before = fullDate
this.lastHover = false
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus
.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus
.before);
}
this.lastHover = true
}
}
this._getWeek(fullDate)
}
/**
* 鼠标 hover 更新多选状态
*/
setHoverMultiple(fullDate) {
let {
before,
after
} = this.multipleStatus
if (!this.range) return
if (this.lastHover) return
if (!before) {
this.multipleStatus.before = fullDate
} else {
this.multipleStatus.after = fullDate
if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
} else {
this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
}
}
this._getWeek(fullDate)
}
/**
* 更新默认值多选状态
*/
setDefaultMultiple(before, after) {
this.multipleStatus.before = before
this.multipleStatus.after = after
if (before && after) {
if (this.dateCompare(before, after)) {
this.multipleStatus.data = this.geDateAll(before, after);
this._getWeek(after)
} else {
this.multipleStatus.data = this.geDateAll(after, before);
this._getWeek(before)
}
}
}
/**
* 获取每周数据
* @param {Object} dateData
*/
_getWeek(dateData) {
const {
fullDate,
year,
month,
date,
day
} = this.getDate(dateData)
let firstDay = new Date(year, month - 1, 1).getDay()
let currentDay = new Date(year, month, 0).getDate()
let dates = {
lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
nextMonthDays: [], // 下个月开始几天
weeks: []
}
let canlender = []
const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
let weeks = {}
// 拼接数组 上个月开始几天 + 本月天数+ 下个月开始几天
for (let i = 0; i < canlender.length; i++) {
if (i % 7 === 0) {
weeks[parseInt(i / 7)] = new Array(7)
}
weeks[parseInt(i / 7)][i % 7] = canlender[i]
}
this.canlender = canlender
this.weeks = weeks
}
//静态方法
// static init(date) {
// if (!this.instance) {
// this.instance = new Calendar(date);
// }
// return this.instance;
// }
}
export default Calendar

90
uni_modules/uni-datetime-picker/package.json

@ -0,0 +1,90 @@
{
"id": "uni-datetime-picker",
"displayName": "uni-datetime-picker 日期选择器",
"version": "2.2.2",
"description": "uni-datetime-picker 日期时间选择器,支持日历,支持范围选择",
"keywords": [
"uni-datetime-picker",
"uni-ui",
"uniui",
"日期时间选择器",
"日期时间"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss",
"uni-icons"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

21
uni_modules/uni-datetime-picker/readme.md

@ -0,0 +1,21 @@
> `重要通知:组件升级更新 2.0.0 后,支持日期+时间范围选择,组件 ui 将使用日历选择日期,ui 变化较大,同时支持 PC 和 移动端。此版本不向后兼容,不再支持单独的时间选择(type=time)及相关的 hide-second 属性(时间选可使用内置组件 picker)。若仍需使用旧版本,可在插件市场下载*非uni_modules版本*,旧版本将不再维护`
## DatetimePicker 时间选择器
> **组件名:uni-datetime-picker**
> 代码块: `uDatetimePicker`
该组件的优势是,支持**时间戳**输入和输出(起始时间、终止时间也支持时间戳),可**同时选择**日期和时间。
若只是需要单独选择日期和时间,不需要时间戳输入和输出,可使用原生的 picker 组件。
**_点击 picker 默认值规则:_**
- 若设置初始值 value, 会显示在 picker 显示框中
- 若无初始值 value,则初始值 value 为当前本地时间 Date.now(), 但不会显示在 picker 显示框中
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-datetime-picker)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839

21
uni_modules/uni-table/changelog.md

@ -0,0 +1,21 @@
## 1.2.0(2021-11-19)
- 优化 组件UI,并提供设计资源,详见:[https://uniapp.dcloud.io/component/uniui/resource](https://uniapp.dcloud.io/component/uniui/resource)
- 文档迁移,详见:[https://uniapp.dcloud.io/component/uniui/uni-table](https://uniapp.dcloud.io/component/uniui/uni-table)
## 1.1.0(2021-07-30)
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
## 1.0.7(2021-07-08)
- 新增 uni-th 支持 date 日期筛选范围
## 1.0.6(2021-07-05)
- 新增 uni-th 支持 range 筛选范围
## 1.0.5(2021-06-28)
- 新增 uni-th 筛选功能
## 1.0.4(2021-05-12)
- 新增 示例地址
- 修复 示例项目缺少组件的Bug
## 1.0.3(2021-04-16)
- 新增 sortable 属性,是否开启单列排序
- 优化 表格多选逻辑
## 1.0.2(2021-03-22)
- uni-tr 添加 disabled 属性,用于 type=selection 时,设置某行是否可由全选按钮控制
## 1.0.1(2021-02-05)
- 调整为uni_modules目录规范

455
uni_modules/uni-table/components/uni-table/uni-table.vue

@ -0,0 +1,455 @@
<template>
<view class="uni-table-scroll" :class="{ 'table--border': border, 'border-none': !noData }">
<!-- #ifdef H5 -->
<table class="uni-table" border="0" cellpadding="0" cellspacing="0" :class="{ 'table--stripe': stripe }" :style="{ 'min-width': minWidth + 'px' }">
<slot></slot>
<view v-if="noData" class="uni-table-loading">
<view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view>
</view>
<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
</table>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table" :style="{ 'min-width': minWidth + 'px' }" :class="{ 'table--stripe': stripe }">
<slot></slot>
<view v-if="noData" class="uni-table-loading">
<view class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</view>
</view>
<view v-if="loading" class="uni-table-mask" :class="{ 'empty-border': border }"><div class="uni-table--loader"></div></view>
</view>
<!-- #endif -->
</view>
</template>
<script>
/**
* Table 表格
* @description 用于展示多条结构类似的数据
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270
* @property {Boolean} border 是否带有纵向边框
* @property {Boolean} stripe 是否显示斑马线
* @property {Boolean} type 是否开启多选
* @property {String} emptyText 空数据时显示的文本内容
* @property {Boolean} loading 显示加载中
* @event {Function} selection-change 开启多选时当选择项发生变化时会触发该事件
*/
export default {
name: 'uniTable',
options: {
virtualHost: true
},
emits:['selection-change'],
props: {
data: {
type: Array,
default() {
return []
}
},
// 线
border: {
type: Boolean,
default: false
},
// 线
stripe: {
type: Boolean,
default: false
},
//
type: {
type: String,
default: ''
},
//
emptyText: {
type: String,
default: '没有更多数据'
},
loading: {
type: Boolean,
default: false
},
rowKey: {
type: String,
default: ''
}
},
data() {
return {
noData: true,
minWidth: 0,
multiTableHeads: []
}
},
watch: {
loading(val) {},
data(newVal) {
let theadChildren = this.theadChildren
let rowspan = 1
if (this.theadChildren) {
rowspan = this.theadChildren.rowspan
}
// this.trChildren.length - rowspan
this.noData = false
// this.noData = newVal.length === 0
}
},
created() {
// tr
this.trChildren = []
this.thChildren = []
this.theadChildren = null
this.backData = []
this.backIndexData = []
},
methods: {
isNodata() {
let theadChildren = this.theadChildren
let rowspan = 1
if (this.theadChildren) {
rowspan = this.theadChildren.rowspan
}
this.noData = this.trChildren.length - rowspan <= 0
},
/**
* 选中所有
*/
selectionAll() {
let startIndex = 1
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
} else {
startIndex = theadChildren.rowspan - 1
}
let isHaveData = this.data && this.data.length.length > 0
theadChildren.checked = true
theadChildren.indeterminate = false
this.trChildren.forEach((item, index) => {
if (!item.disabled) {
item.checked = true
if (isHaveData && item.keyValue) {
const row = this.data.find(v => v[this.rowKey] === item.keyValue)
if (!this.backData.find(v => v[this.rowKey] === row[this.rowKey])) {
this.backData.push(row)
}
}
if (index > (startIndex - 1) && this.backIndexData.indexOf(index - startIndex) === -1) {
this.backIndexData.push(index - startIndex)
}
}
})
// this.backData = JSON.parse(JSON.stringify(this.data))
this.$emit('selection-change', {
detail: {
value: this.backData,
index: this.backIndexData
}
})
},
/**
* 用于多选表格切换某一行的选中状态如果使用了第二个参数则是设置这一行选中与否selected true 则选中
*/
toggleRowSelection(row, selected) {
// if (!this.theadChildren) return
row = [].concat(row)
this.trChildren.forEach((item, index) => {
// if (item.keyValue) {
const select = row.findIndex(v => {
//
if (typeof v === 'number') {
return v === index - 1
} else {
return v[this.rowKey] === item.keyValue
}
})
let ischeck = item.checked
if (select !== -1) {
if (typeof selected === 'boolean') {
item.checked = selected
} else {
item.checked = !item.checked
}
if (ischeck !== item.checked) {
this.check(item.rowData||item, item.checked, item.rowData?item.keyValue:null, true)
}
}
// }
})
this.$emit('selection-change', {
detail: {
value: this.backData,
index:this.backIndexData
}
})
},
/**
* 用于多选表格清空用户的选择
*/
clearSelection() {
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
}
// if (!this.theadChildren) return
theadChildren.checked = false
theadChildren.indeterminate = false
this.trChildren.forEach(item => {
// if (item.keyValue) {
item.checked = false
// }
})
this.backData = []
this.backIndexData = []
this.$emit('selection-change', {
detail: {
value: [],
index: []
}
})
},
/**
* 用于多选表格切换所有行的选中状态
*/
toggleAllSelection() {
let list = []
let startIndex = 1
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
} else {
startIndex = theadChildren.rowspan - 1
}
this.trChildren.forEach((item, index) => {
if (!item.disabled) {
if (index > (startIndex - 1) ) {
list.push(index-startIndex)
}
}
})
this.toggleRowSelection(list)
},
/**
* 选中\取消选中
* @param {Object} child
* @param {Object} check
* @param {Object} rowValue
*/
check(child, check, keyValue, emit) {
let theadChildren = this.theadChildren
if (!this.theadChildren) {
theadChildren = this.trChildren[0]
}
let childDomIndex = this.trChildren.findIndex((item, index) => child === item)
if(childDomIndex < 0){
childDomIndex = this.data.findIndex(v=>v[this.rowKey] === keyValue) + 1
}
const dataLen = this.trChildren.filter(v => !v.disabled && v.keyValue).length
if (childDomIndex === 0) {
check ? this.selectionAll() : this.clearSelection()
return
}
if (check) {
if (keyValue) {
this.backData.push(child)
}
this.backIndexData.push(childDomIndex - 1)
} else {
const index = this.backData.findIndex(v => v[this.rowKey] === keyValue)
const idx = this.backIndexData.findIndex(item => item === childDomIndex - 1)
if (keyValue) {
this.backData.splice(index, 1)
}
this.backIndexData.splice(idx, 1)
}
const domCheckAll = this.trChildren.find((item, index) => index > 0 && !item.checked && !item.disabled)
if (!domCheckAll) {
theadChildren.indeterminate = false
theadChildren.checked = true
} else {
theadChildren.indeterminate = true
theadChildren.checked = false
}
if (this.backIndexData.length === 0) {
theadChildren.indeterminate = false
}
if (!emit) {
this.$emit('selection-change', {
detail: {
value: this.backData,
index: this.backIndexData
}
})
}
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-scroll {
width: 100%;
/* #ifndef APP-NVUE */
overflow-x: auto;
/* #endif */
}
.uni-table {
position: relative;
width: 100%;
border-radius: 5px;
// box-shadow: 0px 0px 3px 1px rgba(0, 0, 0, 0.1);
background-color: #fff;
/* #ifndef APP-NVUE */
box-sizing: border-box;
display: table;
overflow-x: auto;
::v-deep .uni-table-tr:nth-child(n + 2) {
&:hover {
background-color: #f5f7fa;
}
}
::v-deep .uni-table-thead {
.uni-table-tr {
// background-color: #f5f7fa;
&:hover {
background-color:#fafafa;
}
}
}
/* #endif */
}
.table--border {
border: 1px $border-color solid;
border-right: none;
}
.border-none {
/* #ifndef APP-NVUE */
border-bottom: none;
/* #endif */
}
.table--stripe {
/* #ifndef APP-NVUE */
::v-deep .uni-table-tr:nth-child(2n + 3) {
background-color: #fafafa;
}
/* #endif */
}
/* 表格加载、无数据样式 */
.uni-table-loading {
position: relative;
/* #ifndef APP-NVUE */
display: table-row;
/* #endif */
height: 50px;
line-height: 50px;
overflow: hidden;
box-sizing: border-box;
}
.empty-border {
border-right: 1px $border-color solid;
}
.uni-table-text {
position: absolute;
right: 0;
left: 0;
text-align: center;
font-size: 14px;
color: #999;
}
.uni-table-mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(255, 255, 255, 0.8);
z-index: 99;
/* #ifndef APP-NVUE */
display: flex;
margin: auto;
transition: all 0.5s;
/* #endif */
justify-content: center;
align-items: center;
}
.uni-table--loader {
width: 30px;
height: 30px;
border: 2px solid #aaa;
// border-bottom-color: transparent;
border-radius: 50%;
/* #ifndef APP-NVUE */
animation: 2s uni-table--loader linear infinite;
/* #endif */
position: relative;
}
@keyframes uni-table--loader {
0% {
transform: rotate(360deg);
}
10% {
border-left-color: transparent;
}
20% {
border-bottom-color: transparent;
}
30% {
border-right-color: transparent;
}
40% {
border-top-color: transparent;
}
50% {
transform: rotate(0deg);
}
60% {
border-top-color: transparent;
}
70% {
border-left-color: transparent;
}
80% {
border-bottom-color: transparent;
}
90% {
border-right-color: transparent;
}
100% {
transform: rotate(-360deg);
}
}
</style>

29
uni_modules/uni-table/components/uni-tbody/uni-tbody.vue

@ -0,0 +1,29 @@
<template>
<!-- #ifdef H5 -->
<tbody>
<slot></slot>
</tbody>
<!-- #endif -->
<!-- #ifndef H5 -->
<view><slot></slot></view>
<!-- #endif -->
</template>
<script>
export default {
name: 'uniBody',
options: {
virtualHost: true
},
data() {
return {
}
},
created() {},
methods: {}
}
</script>
<style>
</style>

90
uni_modules/uni-table/components/uni-td/uni-td.vue

@ -0,0 +1,90 @@
<template>
<!-- #ifdef H5 -->
<td class="uni-table-td" :rowspan="rowspan" :colspan="colspan" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
<slot></slot>
</td>
<!-- #endif -->
<!-- #ifndef H5 -->
<!-- :class="{'table--border':border}" -->
<view class="uni-table-td" :class="{'table--border':border}" :style="{width:width + 'px','text-align':align}">
<slot></slot>
</view>
<!-- #endif -->
</template>
<script>
/**
* Td 单元格
* @description 表格中的标准单元格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270
* @property {Number} align = [left|center|right] 单元格对齐方式
*/
export default {
name: 'uniTd',
options: {
virtualHost: true
},
props: {
width: {
type: [String, Number],
default: ''
},
align: {
type: String,
default: 'left'
},
rowspan: {
type: [Number,String],
default: 1
},
colspan: {
type: [Number,String],
default: 1
}
},
data() {
return {
border: false
};
},
created() {
this.root = this.getTable()
this.border = this.root.border
},
methods: {
/**
* 获取父元素实例
*/
getTable() {
let parent = this.$parent;
let parentName = parent.$options.name;
while (parentName !== 'uniTable') {
parent = parent.$parent;
if (!parent) return false;
parentName = parent.$options.name;
}
return parent;
},
}
}
</script>
<style lang="scss">
$border-color:#EBEEF5;
.uni-table-td {
display: table-cell;
padding: 8px 10px;
font-size: 14px;
border-bottom: 1px $border-color solid;
font-weight: 400;
color: #606266;
line-height: 23px;
box-sizing: border-box;
}
.table--border {
border-right: 1px $border-color solid;
}
</style>

503
uni_modules/uni-table/components/uni-th/filter-dropdown.vue

@ -0,0 +1,503 @@
<template>
<view class="uni-filter-dropdown">
<view class="dropdown-btn" @click="onDropdown">
<view class="icon-select" :class="{active: canReset}" v-if="isSelect || isRange"></view>
<view class="icon-search" :class="{active: canReset}" v-if="isSearch">
<view class="icon-search-0"></view>
<view class="icon-search-1"></view>
</view>
<view class="icon-calendar" :class="{active: canReset}" v-if="isDate">
<view class="icon-calendar-0"></view>
<view class="icon-calendar-1"></view>
</view>
</view>
<view class="uni-dropdown-cover" v-if="isOpened" @click="handleClose"></view>
<view class="dropdown-popup dropdown-popup-right" v-if="isOpened" @click.stop>
<!-- select-->
<view v-if="isSelect" class="list">
<label class="flex-r a-i-c list-item" v-for="(item,index) in dataList" :key="index"
@click="onItemClick($event, index)">
<check-box class="check" :checked="item.checked" />
<view class="checklist-content">
<text class="checklist-text" :style="item.styleIconText">{{item[map.text]}}</text>
</view>
</label>
</view>
<view v-if="isSelect" class="flex-r opera-area">
<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSelectReset">
{{resource.reset}}</view>
<view class="flex-f btn btn-submit" @click="handleSelectSubmit">{{resource.submit}}</view>
</view>
<!-- search -->
<view v-if="isSearch" class="search-area">
<input class="search-input" v-model="filterValue" />
</view>
<view v-if="isSearch" class="flex-r opera-area">
<view class="flex-f btn btn-submit" @click="handleSearchSubmit">{{resource.search}}</view>
<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleSearchReset">
{{resource.reset}}</view>
</view>
<!-- range -->
<view v-if="isRange">
<view class="input-label">{{resource.gt}}</view>
<input class="input" v-model="gtValue" />
<view class="input-label">{{resource.lt}}</view>
<input class="input" v-model="ltValue" />
</view>
<view v-if="isRange" class="flex-r opera-area">
<view class="flex-f btn btn-default" :class="{disable: !canReset}" @click="handleRangeReset">
{{resource.reset}}</view>
<view class="flex-f btn btn-submit" @click="handleRangeSubmit">{{resource.submit}}</view>
</view>
<!-- date -->
<view v-if="isDate">
<uni-datetime-picker ref="datetimepicker" :value="dateRange" type="datetimerange" return-type="timestamp" @change="datetimechange" @maskClick="timepickerclose">
<view></view>
</uni-datetime-picker>
</view>
</view>
</view>
</template>
<script>
import checkBox from '../uni-tr/table-checkbox.vue'
const resource = {
"reset": "重置",
"search": "搜索",
"submit": "确定",
"filter": "筛选",
"gt": "大于等于",
"lt": "小于等于",
"date": "日期范围"
}
const DropdownType = {
Select: "select",
Search: "search",
Range: "range",
Date: "date",
Timestamp: "timestamp"
}
export default {
name: 'FilterDropdown',
emits:['change'],
components: {
checkBox
},
options: {
virtualHost: true
},
props: {
filterType: {
type: String,
default: DropdownType.Select
},
filterData: {
type: Array,
default () {
return []
}
},
mode: {
type: String,
default: 'default'
},
map: {
type: Object,
default () {
return {
text: 'text',
value: 'value'
}
}
}
},
computed: {
canReset() {
if (this.isSearch) {
return this.filterValue.length > 0
}
if (this.isSelect) {
return this.checkedValues.length > 0
}
if (this.isRange) {
return (this.gtValue.length > 0 && this.ltValue.length > 0)
}
if (this.isDate) {
return this.dateSelect.length > 0
}
return false
},
isSelect() {
return this.filterType === DropdownType.Select
},
isSearch() {
return this.filterType === DropdownType.Search
},
isRange() {
return this.filterType === DropdownType.Range
},
isDate() {
return (this.filterType === DropdownType.Date || this.filterType === DropdownType.Timestamp)
}
},
watch: {
filters(newVal) {
this._copyFilters()
},
indeterminate(newVal) {
this.isIndeterminate = newVal
}
},
data() {
return {
resource,
enabled: true,
isOpened: false,
dataList: [],
filterValue: '',
checkedValues: [],
gtValue: '',
ltValue: '',
dateRange: [],
dateSelect: []
};
},
created() {
this._copyFilters()
},
methods: {
_copyFilters() {
let dl = JSON.parse(JSON.stringify(this.filterData))
for (let i = 0; i < dl.length; i++) {
if (dl[i].checked === undefined) {
dl[i].checked = false
}
}
this.dataList = dl
},
openPopup() {
this.isOpened = true
if (this.isDate) {
this.$nextTick(() => {
if (!this.dateRange.length) {
this.resetDate()
}
this.$refs.datetimepicker.show()
})
}
},
closePopup() {
this.isOpened = false
},
handleClose(e) {
this.closePopup()
},
resetDate() {
let date = new Date()
let dateText = date.toISOString().split('T')[0]
this.dateRange = [dateText + ' 0:00:00', dateText + ' 23:59:59']
},
onDropdown(e) {
this.openPopup()
},
onItemClick(e, index) {
let items = this.dataList
let listItem = items[index]
if (listItem.checked === undefined) {
items[index].checked = true
} else {
items[index].checked = !listItem.checked
}
let checkvalues = []
for (let i = 0; i < items.length; i++) {
const item = items[i]
if (item.checked) {
checkvalues.push(item.value)
}
}
this.checkedValues = checkvalues
},
datetimechange(e) {
this.closePopup()
this.dateRange = e
this.dateSelect = e
this.$emit('change', {
filterType: this.filterType,
filter: e
})
},
timepickerclose(e) {
this.closePopup()
},
handleSelectSubmit() {
this.closePopup()
this.$emit('change', {
filterType: this.filterType,
filter: this.checkedValues
})
},
handleSelectReset() {
if (!this.canReset) {
return;
}
var items = this.dataList
for (let i = 0; i < items.length; i++) {
let item = items[i]
this.$set(item, 'checked', false)
}
this.checkedValues = []
this.handleSelectSubmit()
},
handleSearchSubmit() {
this.closePopup()
this.$emit('change', {
filterType: this.filterType,
filter: this.filterValue
})
},
handleSearchReset() {
if (!this.canReset) {
return;
}
this.filterValue = ''
this.handleSearchSubmit()
},
handleRangeSubmit(isReset) {
this.closePopup()
this.$emit('change', {
filterType: this.filterType,
filter: isReset === true ? [] : [parseInt(this.gtValue), parseInt(this.ltValue)]
})
},
handleRangeReset() {
if (!this.canReset) {
return;
}
this.gtValue = ''
this.ltValue = ''
this.handleRangeSubmit(true)
}
}
}
</script>
<style lang="scss">
.flex-r {
display: flex;
flex-direction: row;
}
.flex-f {
flex: 1;
}
.a-i-c {
align-items: center;
}
.j-c-c {
justify-content: center;
}
.icon-select {
width: 14px;
height: 16px;
border: solid 6px transparent;
border-top: solid 6px #ddd;
border-bottom: none;
background-color: #ddd;
background-clip: content-box;
box-sizing: border-box;
}
.icon-select.active {
background-color: #1890ff;
border-top-color: #1890ff;
}
.icon-search {
width: 12px;
height: 16px;
position: relative;
}
.icon-search-0 {
border: 2px solid #ddd;
border-radius: 8px;
width: 7px;
height: 7px;
}
.icon-search-1 {
position: absolute;
top: 8px;
right: 0;
width: 1px;
height: 7px;
background-color: #ddd;
transform: rotate(-45deg);
}
.icon-search.active .icon-search-0 {
border-color: #1890ff;
}
.icon-search.active .icon-search-1 {
background-color: #1890ff;
}
.icon-calendar {
color: #ddd;
width: 14px;
height: 16px;
}
.icon-calendar-0 {
height: 4px;
margin-top: 3px;
margin-bottom: 1px;
background-color: #ddd;
border-radius: 2px 2px 1px 1px;
position: relative;
}
.icon-calendar-0:before, .icon-calendar-0:after {
content: '';
position: absolute;
top: -3px;
width: 4px;
height: 3px;
border-radius: 1px;
background-color: #ddd;
}
.icon-calendar-0:before {
left: 2px;
}
.icon-calendar-0:after {
right: 2px;
}
.icon-calendar-1 {
height: 9px;
background-color: #ddd;
border-radius: 1px 1px 2px 2px;
}
.icon-calendar.active {
color: #1890ff;
}
.icon-calendar.active .icon-calendar-0,
.icon-calendar.active .icon-calendar-1,
.icon-calendar.active .icon-calendar-0:before,
.icon-calendar.active .icon-calendar-0:after {
background-color: #1890ff;
}
.uni-filter-dropdown {
position: relative;
font-weight: normal;
}
.dropdown-popup {
position: absolute;
top: 100%;
background-color: #fff;
box-shadow: 0 3px 6px -4px #0000001f, 0 6px 16px #00000014, 0 9px 28px 8px #0000000d;
min-width: 150px;
z-index: 1000;
}
.dropdown-popup-left {
left: 0;
}
.dropdown-popup-right {
right: 0;
}
.uni-dropdown-cover {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: transparent;
z-index: 100;
}
.list {
margin-top: 5px;
margin-bottom: 5px;
}
.list-item {
padding: 5px 10px;
text-align: left;
}
.list-item:hover {
background-color: #f0f0f0;
}
.check {
margin-right: 5px;
}
.search-area {
padding: 10px;
}
.search-input {
font-size: 12px;
border: 1px solid #f0f0f0;
border-radius: 3px;
padding: 2px 5px;
min-width: 150px;
text-align: left;
}
.input-label {
margin: 10px 10px 5px 10px;
text-align: left;
}
.input {
font-size: 12px;
border: 1px solid #f0f0f0;
border-radius: 3px;
margin: 10px;
padding: 2px 5px;
min-width: 150px;
text-align: left;
}
.opera-area {
cursor: default;
border-top: 1px solid #ddd;
padding: 5px;
}
.opera-area .btn {
font-size: 12px;
border-radius: 3px;
margin: 5px;
padding: 4px 4px;
}
.btn-default {
border: 1px solid #ddd;
}
.btn-default.disable {
border-color: transparent;
}
.btn-submit {
background-color: #1890ff;
color: #ffffff;
}
</style>

251
uni_modules/uni-table/components/uni-th/uni-th.vue

@ -0,0 +1,251 @@
<template>
<!-- #ifdef H5 -->
<th :rowspan="rowspan" :colspan="colspan" class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: width + 'px', 'text-align': align }">
<view class="uni-table-th-row">
<view class="uni-table-th-content" :style="{ 'justify-content': contentAlign }" @click="sort">
<slot></slot>
<view v-if="sortable" class="arrow-box">
<text class="arrow up" :class="{ active: ascending }" @click.stop="ascendingFn"></text>
<text class="arrow down" :class="{ active: descending }" @click.stop="descendingFn"></text>
</view>
</view>
<dropdown v-if="filterType || filterData.length" :filterData="filterData" :filterType="filterType" @change="ondropdown"></dropdown>
</view>
</th>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: width + 'px', 'text-align': align }"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import dropdown from './filter-dropdown.vue'
/**
* Th 表头
* @description 表格内的表头单元格组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270
* @property {Number} width 单元格宽度
* @property {Boolean} sortable 是否启用排序
* @property {Number} align = [left|center|right] 单元格对齐方式
* @value left 单元格文字左侧对齐
* @value center 单元格文字居中
* @value right 单元格文字右侧对齐
* @property {Array} filterData 筛选数据
* @property {String} filterType [search|select] 筛选类型
* @value search 关键字搜素
* @value select 条件选择
* @event {Function} sort-change 排序触发事件
*/
export default {
name: 'uniTh',
options: {
virtualHost: true
},
components: {
dropdown
},
emits:['sort-change','filter-change'],
props: {
width: {
type: [String, Number],
default: ''
},
align: {
type: String,
default: 'left'
},
rowspan: {
type: [Number, String],
default: 1
},
colspan: {
type: [Number, String],
default: 1
},
sortable: {
type: Boolean,
default: false
},
filterType: {
type: String,
default: ""
},
filterData: {
type: Array,
default () {
return []
}
}
},
data() {
return {
border: false,
ascending: false,
descending: false
}
},
computed: {
contentAlign() {
let align = 'left'
switch (this.align) {
case 'left':
align = 'flex-start'
break
case 'center':
align = 'center'
break
case 'right':
align = 'flex-end'
break
}
return align
}
},
created() {
this.root = this.getTable('uniTable')
this.rootTr = this.getTable('uniTr')
this.rootTr.minWidthUpdate(this.width ? this.width : 140)
this.border = this.root.border
this.root.thChildren.push(this)
},
methods: {
sort() {
if (!this.sortable) return
this.clearOther()
if (!this.ascending && !this.descending) {
this.ascending = true
this.$emit('sort-change', { order: 'ascending' })
return
}
if (this.ascending && !this.descending) {
this.ascending = false
this.descending = true
this.$emit('sort-change', { order: 'descending' })
return
}
if (!this.ascending && this.descending) {
this.ascending = false
this.descending = false
this.$emit('sort-change', { order: null })
}
},
ascendingFn() {
this.clearOther()
this.ascending = !this.ascending
this.descending = false
this.$emit('sort-change', { order: this.ascending ? 'ascending' : null })
},
descendingFn() {
this.clearOther()
this.descending = !this.descending
this.ascending = false
this.$emit('sort-change', { order: this.descending ? 'descending' : null })
},
clearOther() {
this.root.thChildren.map(item => {
if (item !== this) {
item.ascending = false
item.descending = false
}
return item
})
},
ondropdown(e) {
this.$emit("filter-change", e)
},
/**
* 获取父元素实例
*/
getTable(name) {
let parent = this.$parent
let parentName = parent.$options.name
while (parentName !== name) {
parent = parent.$parent
if (!parent) return false
parentName = parent.$options.name
}
return parent
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-th {
padding: 12px 10px;
/* #ifndef APP-NVUE */
display: table-cell;
box-sizing: border-box;
/* #endif */
font-size: 14px;
font-weight: bold;
color: #909399;
border-bottom: 1px $border-color solid;
}
.uni-table-th-row {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
}
.table--border {
border-right: 1px $border-color solid;
}
.uni-table-th-content {
display: flex;
align-items: center;
flex: 1;
}
.arrow-box {
}
.arrow {
display: block;
position: relative;
width: 10px;
height: 8px;
// border: 1px red solid;
left: 5px;
overflow: hidden;
cursor: pointer;
}
.down {
top: 3px;
::after {
content: '';
width: 8px;
height: 8px;
position: absolute;
left: 2px;
top: -5px;
transform: rotate(45deg);
background-color: #ccc;
}
&.active {
::after {
background-color: #007aff;
}
}
}
.up {
::after {
content: '';
width: 8px;
height: 8px;
position: absolute;
left: 2px;
top: 5px;
transform: rotate(45deg);
background-color: #ccc;
}
&.active {
::after {
background-color: #007aff;
}
}
}
</style>

129
uni_modules/uni-table/components/uni-thead/uni-thead.vue

@ -0,0 +1,129 @@
<template>
<!-- #ifdef H5 -->
<thead class="uni-table-thead">
<tr class="uni-table-tr">
<th :rowspan="rowspan" colspan="1" class="checkbox" :class="{ 'tr-table--border': border }">
<table-checkbox :indeterminate="indeterminate" :checked="checked" @checkboxSelected="checkboxSelected"></table-checkbox>
</th>
</tr>
<slot></slot>
</thead>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table-thead"><slot></slot></view>
<!-- #endif -->
</template>
<script>
import tableCheckbox from '../uni-tr/table-checkbox.vue'
export default {
name: 'uniThead',
components: {
tableCheckbox
},
options: {
virtualHost: true
},
data() {
return {
border: false,
selection: false,
rowspan: 1,
indeterminate: false,
checked: false
}
},
created() {
this.root = this.getTable()
// #ifdef H5
this.root.theadChildren = this
// #endif
this.border = this.root.border
this.selection = this.root.type
},
methods: {
init(self) {
this.rowspan++
},
checkboxSelected(e) {
this.indeterminate = false
const backIndexData = this.root.backIndexData
const data = this.root.trChildren.filter(v => !v.disabled && v.keyValue)
if (backIndexData.length === data.length) {
this.checked = false
this.root.clearSelection()
} else {
this.checked = true
this.root.selectionAll()
}
},
/**
* 获取父元素实例
*/
getTable(name = 'uniTable') {
let parent = this.$parent
let parentName = parent.$options.name
while (parentName !== name) {
parent = parent.$parent
if (!parent) return false
parentName = parent.$options.name
}
return parent
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-thead {
display: table-header-group;
}
.uni-table-tr {
/* #ifndef APP-NVUE */
display: table-row;
transition: all 0.3s;
box-sizing: border-box;
/* #endif */
border: 1px red solid;
background-color: #fafafa;
}
.checkbox {
padding: 0 8px;
width: 26px;
padding-left: 12px;
/* #ifndef APP-NVUE */
display: table-cell;
vertical-align: middle;
/* #endif */
color: #333;
font-weight: 500;
border-bottom: 1px $border-color solid;
font-size: 14px;
// text-align: center;
}
.tr-table--border {
border-right: 1px $border-color solid;
}
/* #ifndef APP-NVUE */
.uni-table-tr {
::v-deep .uni-table-th {
&.table--border:last-child {
// border-right: none;
}
}
::v-deep .uni-table-td {
&.table--border:last-child {
// border-right: none;
}
}
}
/* #endif */
</style>

179
uni_modules/uni-table/components/uni-tr/table-checkbox.vue

@ -0,0 +1,179 @@
<template>
<view class="uni-table-checkbox" @click="selected">
<view v-if="!indeterminate" class="checkbox__inner" :class="{'is-checked':isChecked,'is-disable':isDisabled}">
<view class="checkbox__inner-icon"></view>
</view>
<view v-else class="checkbox__inner checkbox--indeterminate">
<view class="checkbox__inner-icon"></view>
</view>
</view>
</template>
<script>
export default {
name: 'TableCheckbox',
emits:['checkboxSelected'],
props: {
indeterminate: {
type: Boolean,
default: false
},
checked: {
type: [Boolean,String],
default: false
},
disabled: {
type: Boolean,
default: false
},
index: {
type: Number,
default: -1
},
cellData: {
type: Object,
default () {
return {}
}
}
},
watch:{
checked(newVal){
if(typeof this.checked === 'boolean'){
this.isChecked = newVal
}else{
this.isChecked = true
}
},
indeterminate(newVal){
this.isIndeterminate = newVal
}
},
data() {
return {
isChecked: false,
isDisabled: false,
isIndeterminate:false
}
},
created() {
if(typeof this.checked === 'boolean'){
this.isChecked = this.checked
}
this.isDisabled = this.disabled
},
methods: {
selected() {
if (this.isDisabled) return
this.isIndeterminate = false
this.isChecked = !this.isChecked
this.$emit('checkboxSelected', {
checked: this.isChecked,
data: this.cellData
})
}
}
}
</script>
<style lang="scss">
$checked-color: #007aff;
$border-color: #DCDFE6;
$disable:0.4;
.uni-table-checkbox {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
position: relative;
margin: 5px 0;
cursor: pointer;
//
.checkbox__inner {
/* #ifndef APP-NVUE */
flex-shrink: 0;
box-sizing: border-box;
/* #endif */
position: relative;
width: 16px;
height: 16px;
border: 1px solid $border-color;
border-radius: 2px;
background-color: #fff;
z-index: 1;
.checkbox__inner-icon {
position: absolute;
/* #ifdef APP-NVUE */
top: 2px;
/* #endif */
/* #ifndef APP-NVUE */
top: 2px;
/* #endif */
left: 5px;
height: 7px;
width: 3px;
border: 1px solid #fff;
border-left: 0;
border-top: 0;
opacity: 0;
transform-origin: center;
transform: rotate(45deg);
box-sizing: content-box;
}
&.checkbox--indeterminate {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
position: absolute;
opacity: 1;
transform: rotate(0deg);
height: 2px;
top: 0;
bottom: 0;
margin: auto;
left: 0px;
right: 0px;
bottom: 0;
width: auto;
border: none;
border-radius: 2px;
transform: scale(0.5);
background-color: #fff;
}
}
&:hover{
border-color: $checked-color;
}
//
&.is-disable {
/* #ifdef H5 */
cursor: not-allowed;
/* #endif */
background-color: #F2F6FC;
border-color: $border-color;
}
//
&.is-checked {
border-color: $checked-color;
background-color: $checked-color;
.checkbox__inner-icon {
opacity: 1;
transform: rotate(45deg);
}
//
&.is-disable {
opacity: $disable;
}
}
}
}
</style>

171
uni_modules/uni-table/components/uni-tr/uni-tr.vue

@ -0,0 +1,171 @@
<template>
<!-- #ifdef H5 -->
<tr class="uni-table-tr">
<th v-if="selection === 'selection' && ishead" class="checkbox" :class="{ 'tr-table--border': border }">
<table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" @checkboxSelected="checkboxSelected"></table-checkbox>
</th>
<slot></slot>
<!-- <uni-th class="th-fixed">123</uni-th> -->
</tr>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="uni-table-tr">
<view v-if="selection === 'selection' " class="checkbox" :class="{ 'tr-table--border': border }">
<table-checkbox :checked="checked" :indeterminate="indeterminate" :disabled="disabled" @checkboxSelected="checkboxSelected"></table-checkbox>
</view>
<slot></slot>
</view>
<!-- #endif -->
</template>
<script>
import tableCheckbox from './table-checkbox.vue'
/**
* Tr 表格行组件
* @description 表格行组件 仅包含 th,td 组件
* @tutorial https://ext.dcloud.net.cn/plugin?id=
*/
export default {
name: 'uniTr',
components: { tableCheckbox },
props: {
disabled: {
type: Boolean,
default: false
},
keyValue: {
type: [String, Number],
default: ''
}
},
options: {
virtualHost: true
},
data() {
return {
value: false,
border: false,
selection: false,
widthThArr: [],
ishead: true,
checked: false,
indeterminate:false
}
},
created() {
this.root = this.getTable()
this.head = this.getTable('uniThead')
if (this.head) {
this.ishead = false
this.head.init(this)
}
this.border = this.root.border
this.selection = this.root.type
this.root.trChildren.push(this)
const rowData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue)
if(rowData){
this.rowData = rowData
}
this.root.isNodata()
},
mounted() {
if (this.widthThArr.length > 0) {
const selectionWidth = this.selection === 'selection' ? 50 : 0
this.root.minWidth = this.widthThArr.reduce((a, b) => Number(a) + Number(b)) + selectionWidth
}
},
// #ifndef VUE3
destroyed() {
const index = this.root.trChildren.findIndex(i => i === this)
this.root.trChildren.splice(index, 1)
this.root.isNodata()
},
// #endif
// #ifdef VUE3
unmounted() {
const index = this.root.trChildren.findIndex(i => i === this)
this.root.trChildren.splice(index, 1)
this.root.isNodata()
},
// #endif
methods: {
minWidthUpdate(width) {
this.widthThArr.push(width)
},
//
checkboxSelected(e) {
let rootData = this.root.data.find(v => v[this.root.rowKey] === this.keyValue)
this.checked = e.checked
this.root.check(rootData||this, e.checked,rootData? this.keyValue:null)
},
change(e) {
this.root.trChildren.forEach(item => {
if (item === this) {
this.root.check(this, e.detail.value.length > 0 ? true : false)
}
})
},
/**
* 获取父元素实例
*/
getTable(name = 'uniTable') {
let parent = this.$parent
let parentName = parent.$options.name
while (parentName !== name) {
parent = parent.$parent
if (!parent) return false
parentName = parent.$options.name
}
return parent
}
}
}
</script>
<style lang="scss">
$border-color: #ebeef5;
.uni-table-tr {
/* #ifndef APP-NVUE */
display: table-row;
transition: all 0.3s;
box-sizing: border-box;
/* #endif */
}
.checkbox {
padding: 0 8px;
width: 26px;
padding-left: 12px;
/* #ifndef APP-NVUE */
display: table-cell;
vertical-align: middle;
/* #endif */
color: #333;
font-weight: 500;
border-bottom: 1px $border-color solid;
font-size: 14px;
// text-align: center;
}
.tr-table--border {
border-right: 1px $border-color solid;
}
/* #ifndef APP-NVUE */
.uni-table-tr {
::v-deep .uni-table-th {
&.table--border:last-child {
// border-right: none;
}
}
::v-deep .uni-table-td {
&.table--border:last-child {
// border-right: none;
}
}
}
/* #endif */
</style>

86
uni_modules/uni-table/package.json

@ -0,0 +1,86 @@
{
"id": "uni-table",
"displayName": "uni-table 表格",
"version": "1.2.0",
"description": "表格组件,多用于展示多条结构类似的数据,如",
"keywords": [
"uni-ui",
"uniui",
"table",
"表格"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": ["uni-scss","uni-datetime-picker"],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "n"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "n",
"QQ": "y"
},
"快应用": {
"华为": "n",
"联盟": "n"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}

13
uni_modules/uni-table/readme.md

@ -0,0 +1,13 @@
## Table 表单
> 组件名:``uni-table``,代码块: `uTable`
用于展示多条结构类似的数据
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
Loading…
Cancel
Save