17 changed files with 2035 additions and 0 deletions
@ -0,0 +1,29 @@ |
|||
## 1.2.4(2023-12-19) |
|||
- 修复 uni-tr只有一列时minWidth计算错误,列变化实时计算更新 |
|||
## 1.2.3(2023-03-28) |
|||
- 修复 在vue3模式下可能会出现错误的问题 |
|||
## 1.2.2(2022-11-29) |
|||
- 优化 主题样式 |
|||
## 1.2.1(2022-06-06) |
|||
- 修复 微信小程序存在无使用组件的问题 |
|||
## 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目录规范 |
|||
@ -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> |
|||
<tr v-if="noData" class="uni-table-loading"> |
|||
<td class="uni-table-text" :class="{ 'empty-border': border }">{{ emptyText }}</td> |
|||
</tr> |
|||
<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 > 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> |
|||
@ -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> |
|||
@ -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> |
|||
@ -0,0 +1,511 @@ |
|||
<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' |
|||
} |
|||
} |
|||
}, |
|||
filterDefaultValue: { |
|||
type: [Array,String], |
|||
default () { |
|||
return "" |
|||
} |
|||
} |
|||
}, |
|||
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: { |
|||
filterData(newVal) { |
|||
this._copyFilters() |
|||
}, |
|||
indeterminate(newVal) { |
|||
this.isIndeterminate = newVal |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
resource, |
|||
enabled: true, |
|||
isOpened: false, |
|||
dataList: [], |
|||
filterValue: this.filterDefaultValue, |
|||
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"> |
|||
$uni-primary: #1890ff !default; |
|||
|
|||
.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: $uni-primary; |
|||
border-top-color: $uni-primary; |
|||
} |
|||
|
|||
.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: $uni-primary; |
|||
} |
|||
|
|||
.icon-search.active .icon-search-1 { |
|||
background-color: $uni-primary; |
|||
} |
|||
|
|||
.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: $uni-primary; |
|||
} |
|||
|
|||
.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: $uni-primary; |
|||
} |
|||
|
|||
.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: $uni-primary; |
|||
color: #ffffff; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,285 @@ |
|||
<template> |
|||
<!-- #ifdef H5 --> |
|||
<th :rowspan="rowspan" :colspan="colspan" class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + '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" :filterDefaultValue="filterDefaultValue" :filterData="filterData" :filterType="filterType" @change="ondropdown"></dropdown> |
|||
</view> |
|||
</th> |
|||
<!-- #endif --> |
|||
<!-- #ifndef H5 --> |
|||
<view class="uni-table-th" :class="{ 'table--border': border }" :style="{ width: customWidth + 'px', 'text-align': align }"><slot></slot></view> |
|||
<!-- #endif --> |
|||
</template> |
|||
|
|||
<script> |
|||
// #ifdef H5 |
|||
import dropdown from './filter-dropdown.vue' |
|||
// #endif |
|||
/** |
|||
* Th 表头 |
|||
* @description 表格内的表头单元格组件 |
|||
* @tutorial https://ext.dcloud.net.cn/plugin?id=3270 |
|||
* @property {Number | String} width 单元格宽度(支持纯数字、携带单位px或rpx) |
|||
* @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: { |
|||
// #ifdef H5 |
|||
dropdown |
|||
// #endif |
|||
}, |
|||
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 [] |
|||
} |
|||
}, |
|||
filterDefaultValue: { |
|||
type: [Array,String], |
|||
default () { |
|||
return "" |
|||
} |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
border: false, |
|||
ascending: false, |
|||
descending: false |
|||
} |
|||
}, |
|||
computed: { |
|||
// 根据props中的width属性 自动匹配当前th的宽度(px) |
|||
customWidth(){ |
|||
if(typeof this.width === 'number'){ |
|||
return this.width |
|||
} else if(typeof this.width === 'string') { |
|||
let regexHaveUnitPx = new RegExp(/^[1-9][0-9]*px$/g) |
|||
let regexHaveUnitRpx = new RegExp(/^[1-9][0-9]*rpx$/g) |
|||
let regexHaveNotUnit = new RegExp(/^[1-9][0-9]*$/g) |
|||
if (this.width.match(regexHaveUnitPx) !== null) { // 携带了 px |
|||
return this.width.replace('px', '') |
|||
} else if (this.width.match(regexHaveUnitRpx) !== null) { // 携带了 rpx |
|||
let numberRpx = Number(this.width.replace('rpx', '')) |
|||
let widthCoe = uni.getSystemInfoSync().screenWidth / 750 |
|||
return Math.round(numberRpx * widthCoe) |
|||
} else if (this.width.match(regexHaveNotUnit) !== null) { // 未携带 rpx或px 的纯数字 String |
|||
return this.width |
|||
} else { // 不符合格式 |
|||
return '' |
|||
} |
|||
} else { |
|||
return '' |
|||
} |
|||
}, |
|||
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.customWidth ? this.customWidth : 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-primary: #007aff !default; |
|||
|
|||
.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: $uni-primary; |
|||
} |
|||
} |
|||
} |
|||
.up { |
|||
::after { |
|||
content: ''; |
|||
width: 8px; |
|||
height: 8px; |
|||
position: absolute; |
|||
left: 2px; |
|||
top: 5px; |
|||
transform: rotate(45deg); |
|||
background-color: #ccc; |
|||
} |
|||
&.active { |
|||
::after { |
|||
background-color: $uni-primary; |
|||
} |
|||
} |
|||
} |
|||
</style> |
|||
@ -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> |
|||
@ -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"> |
|||
$uni-primary: #007aff !default; |
|||
$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: $uni-primary; |
|||
background-color: $uni-primary; |
|||
|
|||
.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: $uni-primary; |
|||
} |
|||
// 禁用 |
|||
&.is-disable { |
|||
/* #ifdef H5 */ |
|||
cursor: not-allowed; |
|||
/* #endif */ |
|||
background-color: #F2F6FC; |
|||
border-color: $border-color; |
|||
} |
|||
|
|||
// 选中 |
|||
&.is-checked { |
|||
border-color: $uni-primary; |
|||
background-color: $uni-primary; |
|||
|
|||
.checkbox__inner-icon { |
|||
opacity: 1; |
|||
transform: rotate(45deg); |
|||
} |
|||
|
|||
// 选中禁用 |
|||
&.is-disable { |
|||
opacity: $disable; |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
</style> |
|||
@ -0,0 +1,175 @@ |
|||
<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 = Number(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) |
|||
if (this.widthThArr.length > 0) { |
|||
const selectionWidth = this.selection === 'selection' ? 50 : 0; |
|||
this.root.minWidth = Number(this.widthThArr.reduce((a, b) => Number(a) + Number(b))) + selectionWidth; |
|||
} |
|||
}, |
|||
// 选中 |
|||
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> |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"filter-dropdown.reset": "Reset", |
|||
"filter-dropdown.search": "Search", |
|||
"filter-dropdown.submit": "Submit", |
|||
"filter-dropdown.filter": "Filter", |
|||
"filter-dropdown.gt": "Greater or equal to", |
|||
"filter-dropdown.lt": "Less than or equal to", |
|||
"filter-dropdown.date": "Date" |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"filter-dropdown.reset": "Reiniciar", |
|||
"filter-dropdown.search": "Búsqueda", |
|||
"filter-dropdown.submit": "Entregar", |
|||
"filter-dropdown.filter": "Filtrar", |
|||
"filter-dropdown.gt": "Mayor o igual a", |
|||
"filter-dropdown.lt": "Menos que o igual a", |
|||
"filter-dropdown.date": "Fecha" |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"filter-dropdown.reset": "Réinitialiser", |
|||
"filter-dropdown.search": "Chercher", |
|||
"filter-dropdown.submit": "Soumettre", |
|||
"filter-dropdown.filter": "Filtre", |
|||
"filter-dropdown.gt": "Supérieur ou égal à", |
|||
"filter-dropdown.lt": "Inférieur ou égal à", |
|||
"filter-dropdown.date": "Date" |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
import en from './en.json' |
|||
import es from './es.json' |
|||
import fr from './fr.json' |
|||
import zhHans from './zh-Hans.json' |
|||
import zhHant from './zh-Hant.json' |
|||
export default { |
|||
en, |
|||
es, |
|||
fr, |
|||
'zh-Hans': zhHans, |
|||
'zh-Hant': zhHant |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"filter-dropdown.reset": "重置", |
|||
"filter-dropdown.search": "搜索", |
|||
"filter-dropdown.submit": "确定", |
|||
"filter-dropdown.filter": "筛选", |
|||
"filter-dropdown.gt": "大于等于", |
|||
"filter-dropdown.lt": "小于等于", |
|||
"filter-dropdown.date": "日期范围" |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
{ |
|||
"filter-dropdown.reset": "重置", |
|||
"filter-dropdown.search": "搜索", |
|||
"filter-dropdown.submit": "確定", |
|||
"filter-dropdown.filter": "篩選", |
|||
"filter-dropdown.gt": "大於等於", |
|||
"filter-dropdown.lt": "小於等於", |
|||
"filter-dropdown.date": "日期範圍" |
|||
} |
|||
@ -0,0 +1,83 @@ |
|||
{ |
|||
"id": "uni-table", |
|||
"displayName": "uni-table 表格", |
|||
"version": "1.2.4", |
|||
"description": "表格组件,多用于展示多条结构类似的数据,如", |
|||
"keywords": [ |
|||
"uni-ui", |
|||
"uniui", |
|||
"table", |
|||
"表格" |
|||
], |
|||
"repository": "https://github.com/dcloudio/uni-ui", |
|||
"engines": { |
|||
"HBuilderX": "" |
|||
}, |
|||
"directories": { |
|||
"example": "../../temps/example_temps" |
|||
}, |
|||
"dcloudext": { |
|||
"sale": { |
|||
"regular": { |
|||
"price": "0.00" |
|||
}, |
|||
"sourcecode": { |
|||
"price": "0.00" |
|||
} |
|||
}, |
|||
"contact": { |
|||
"qq": "" |
|||
}, |
|||
"declaration": { |
|||
"ads": "无", |
|||
"data": "无", |
|||
"permissions": "无" |
|||
}, |
|||
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui", |
|||
"type": "component-vue" |
|||
}, |
|||
"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" |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,13 @@ |
|||
|
|||
|
|||
## Table 表单 |
|||
> 组件名:``uni-table``,代码块: `uTable`。 |
|||
|
|||
用于展示多条结构类似的数据 |
|||
|
|||
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-table) |
|||
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839 |
|||
|
|||
|
|||
|
|||
|
|||
Loading…
Reference in new issue