Initial commit

This commit is contained in:
lnljyang
2025-12-30 09:44:46 +08:00
commit 82b8d21506
147 changed files with 39113 additions and 0 deletions

View File

@@ -0,0 +1,124 @@
## 树形层级选择器
### 简介
为统一样式而生树形层级选择器picker弹窗形式的样式和比例参照uniapp的picker和uni-data-picker组件
* 支持单选、多选、父级选择,当然也支持单层选择
* 支持Object对象属性自定义映射
* 支持显示全部选中、部分选中、未选中三种状态
* 支持快速自定义简单样式分割线、按钮、标题、对齐等深入样式可复写css
### 使用方法
`script` 中引入组件
``` javascript
import baTreePicker from "@/components/ba-tree-picker/ba-tree-picker.vue"
export default {
components: {
baTreePicker
}
```
在 `template` 中使用组件
``` javascript
<ba-tree-picker ref="treePicker" :multiple='false' @select-change="selectChange" title="选择城市"
:localdata="listData" valueKey="value" textKey="label" childrenKey="children" />
```
在 `script` 中定义打开方法,和选择监听
``` javascript
methods: {
// 显示选择器
showPicker() {
this.$refs.treePicker._show();
},
//监听选择ids为数组
selectChange(ids, names) {
console.log(ids, names)
}
}
```
在 `template` 中调用打开
``` javascript
<view @click="showPicker">调用选择器</view>
```
### 属性
|属性名|类型|默认值|说明|
|:-|:-:|:--:|-:|
|localdata|Array|[]|源数据目前支持tree结构后续会考虑支持扁平化结构|
|valueKey|String|id|指定 Object 中 key 的值作为节点数据id|
|textKey|String|name|指定 Object 中 key 的值作为节点显示内容|
|childrenKey|String|children|指定 Object 中 key 的值作为节点子集|
|multiple|Boolean|false|是否多选,默认单选|
|selectParent|Boolean|true|是否可以选父级,默认可以|
|title|String| |标题|
|titleColor|String||标题颜色|
|confirmColor|String|#0055ff|确定按钮颜色|
|cancelColor|String|#757575|取消按钮颜色|
|switchColor|String|#666|节点切换图标颜色|
|border|Boolean|false|是否有分割线,默认无|
### 数据格式
注意必须有id、name(id可通过valueKey来配置为其它键值如value)字段,且唯一
``` json
[
{
id: 1,
name: '公司1',
children: [{
id: 11,
name: '研发部',
children: [{
id: 111,
name: '张三',
},{
id: 112,
name: '李四',
}]
},{
id: 12,
name: '综合部',
} ]
},
{
id: 2,
name: '公司2',
children: [{
id: 21,
name: '研发部',
},{
id: 22,
name: '综合部',
},{
id: 23,
name: '财务部',
}, ]
},
{
id: 3,
name: '公司3'
},
{
id: 4,
name: '公司4',
children: [{
id: 41,
name: '研发部',
}]
}
]
```
</details>
### 方法
|方法名|参数|默认值|说明|
|:-|:-:|:--:|-:|
|_show()| | |显示选择器|
|_hide()| | |隐藏选择器|

View File

@@ -0,0 +1,775 @@
<!-- 树形层级选择器-->
<!-- 1支持单选多选 -->
<template>
<view>
<view
class="tree-cover"
:class="{ show: showDialog }"
@tap="_cancel"
></view>
<view class="tree-dialog" :class="{ show: showDialog }">
<view class="tree-bar">
<view
class="tree-bar-cancel"
:style="{ color: cancelColor }"
hover-class="hover-c"
@tap="_cancel"
>取消
</view>
<view class="tree-bar-title" :style="{ color: titleColor }">{{
title
}}</view>
<view
class="tree-bar-confirm"
:style="{ color: confirmColor }"
hover-class="hover-c"
@tap="_confirm"
>
{{ multiple ? "确定" : "" }}
</view>
</view>
<view
class="item u-border"
style="margin: 0 16px; border-radius: 4px"
v-if="isShowOrgPicker"
>
<u-picker
:show="showOrgPicker"
:columns="[orgList]"
keyName="orgName"
@confirm="confirmOrg"
:immediateChange="true"
@cancel="showOrgPicker = false"
>
</u-picker>
<u-input
v-model="orgName"
border="surround"
:disabledColor="'#ffffff'"
disabled
placeholder="请选择车辆主体"
@tap="showOrgPicker = true"
>
<template slot="suffix">
<u-icon
v-if="orgName"
name="close"
color="#bfc7d6"
size="50"
@tap.stop="clearClick"
></u-icon>
</template>
</u-input>
</view>
<view class="tree-view">
<scroll-view class="tree-list" :scroll-y="true">
<block v-for="(item, index) in treeList" :key="index">
<view
class="tree-item"
:style="[
{
paddingLeft: item.level * 30 + 'rpx',
},
]"
:class="{
itemBorder: border === true,
show: item.isShow,
}"
>
<view class="item-label">
<view
class="item-icon uni-inline-item"
@tap.stop="_onItemSwitch(item, index)"
>
<view
v-if="!item.isLastLevel && item.isShowChild"
class="switch-on"
:style="{ 'border-left-color': switchColor }"
>
</view>
<view
v-else-if="!item.isLastLevel && !item.isShowChild"
class="switch-off"
:style="{ 'border-top-color': switchColor }"
>
</view>
<view
v-else
class="item-last-dot"
:style="{ 'border-top-color': switchColor }"
>
</view>
</view>
<view
class="uni-flex-item uni-inline-item"
style="display: flex; align-items: center; flex: 1"
@tap.stop="_onItemSelect(item, index)"
>
<view class="item-name">
{{ item.name + (item.num ? "(" + item.num + ")" : "") }}
</view>
<view
class="item-check"
v-if="selectParent ? true : item.isLastLevel"
>
<view
class="item-check-yes"
v-if="item.checkStatus == 1"
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
>
<view
class="item-check-yes-part"
:style="{ 'background-color': confirmColor }"
>
</view>
</view>
<view
class="item-check-yes"
v-else-if="item.checkStatus == 2"
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
>
<view
class="item-check-yes-all"
:style="{ 'background-color': confirmColor }"
>
</view>
</view>
<view
class="item-check-no"
v-else
:class="{ radio: !multiple }"
:style="{ 'border-color': confirmColor }"
></view>
</view>
</view>
</view>
</view>
</block>
</scroll-view>
</view>
</view>
</view>
</template>
<script>
export default {
emits: ["select-change"],
name: "ba-tree-picker",
props: {
valueKey: {
type: String,
default: "id",
},
textKey: {
type: String,
default: "name",
},
childrenKey: {
type: String,
default: "children",
},
localdata: {
type: Array,
default: function () {
return [];
},
},
localTreeList: {
//在已经格式化好的数据
type: Array,
default: function () {
return [];
},
},
selectedData: {
type: Array,
default: function () {
return [];
},
},
title: {
type: String,
default: "",
},
multiple: {
// 是否可以多选
type: Boolean,
default: true,
},
selectParent: {
//是否可以选父级
type: Boolean,
default: true,
},
confirmColor: {
// 确定按钮颜色
type: String,
default: "", // #0055ff
},
cancelColor: {
// 取消按钮颜色
type: String,
default: "", // #757575
},
titleColor: {
// 标题颜色
type: String,
default: "", //
},
switchColor: {
// 节点切换图标颜色
type: String,
default: "", // #666
},
border: {
// 是否有分割线
type: Boolean,
default: false,
},
isShowOrgPicker: {
type: Boolean,
default: false,
},
},
data() {
return {
showDialog: false,
treeList: [],
showOrgPicker: false,
orgList: [],
orgName: "",
orgId: "",
};
},
computed: {},
methods: {
clearClick() {
this.orgName = ""; // 重置组织名称
this.orgId = ""; // 重置组织id
this.$emit("emitOrgId"); //重新获取区域
},
getOrgList() {
this.orgName = ""; // 重置组织名称
this.orgId = ""; // 重置组织id
this.$api.map.getOrgsList().then((res) => {
console.log("res:\n", res);
const errList = ["羚牛总部", "嘉兴羚牛汽车服务有限公司"];
this.orgList =
res?.filter((item) => !errList.includes(item.orgName)) || [];
});
},
confirmOrg(e) {
console.log("confirmOrg:\n", e.value[0]);
this.orgName = e.value[0].orgName;
this.orgId = e.value[0].id;
this.$emit("emitOrgId", e.value[0].id);
this.showOrgPicker = false;
},
_show() {
this.showDialog = true;
},
_hide() {
this.showDialog = false;
},
_cancel() {
this._hide();
this.$emit("cancel", "");
},
_confirm() {
//多选
let selectedList = []; //如果子集全部选中,只返回父级 id
//let selectedNames;
let currentLevel = -1;
this.treeList.forEach((item, index) => {
if (currentLevel >= 0 && item.level > currentLevel) {
} else {
if (item.checkStatus === 2) {
currentLevel = item.level;
selectedList.push({ id: item.id, type: item.type });
// selectedNames = selectedNames
// ? selectedNames + " / " + item.name
// : item.name;
} else {
currentLevel = -1;
}
}
});
//console.log('_confirm', selectedList);
this._hide();
const obj = this.orgId
? { cityFilter: selectedList, orgId: this.orgId }
: { cityFilter: selectedList };
this.$emit("select-change", obj);
},
//格式化原数据原数据为tree结构
_formatTreeData(list = [], level = 0, parentItem, isShowChild = true) {
let nextIndex = 0;
let parentId = -1;
let initCheckStatus = 0;
if (parentItem) {
nextIndex =
this.treeList.findIndex((item) => item.id === parentItem.id) + 1;
parentId = parentItem.id;
if (!this.multiple) {
//单选
initCheckStatus = 0;
} else initCheckStatus = parentItem.checkStatus == 2 ? 2 : 0;
}
list.forEach((item) => {
let isLastLevel = true;
if (item && item[this.childrenKey]) {
let children = item[this.childrenKey];
if (Array.isArray(children) && children.length > 0) {
isLastLevel = false;
}
}
let itemT = {
id: item[this.valueKey],
name: item[this.textKey],
level,
isLastLevel,
isShow: isShowChild,
isShowChild: false,
checkStatus: initCheckStatus,
orCheckStatus: 0,
parentId,
children: item[this.childrenKey],
childCount: item[this.childrenKey]
? item[this.childrenKey].length
: 0,
childCheckCount: 0,
childCheckPCount: 0,
type: item.type, //新增type字段 后端接收数据需要这个type
num: item.num, //新增num字段 只显示车辆数量 不显示行政区数量 用后退返回数据
};
if (this.selectedData.indexOf(itemT.id) >= 0) {
itemT.checkStatus = 2;
itemT.orCheckStatus = 2;
itemT.childCheckCount = itemT.children ? itemT.children.length : 0;
this._onItemParentSelect(itemT, nextIndex);
}
this.treeList.splice(nextIndex, 0, itemT);
nextIndex++;
});
//console.log(this.treeList);
},
// 节点打开、关闭切换
_onItemSwitch(item, index) {
// console.log(item)
//console.log('_itemSwitch')
if (item.isLastLevel === true) {
return;
}
item.isShowChild = !item.isShowChild;
if (item.children) {
this._formatTreeData(item.children, item.level + 1, item);
item.children = undefined;
} else {
this._onItemChildSwitch(item, index);
}
},
_onItemChildSwitch(item, index) {
//console.log('_onItemChildSwitch')
const firstChildIndex = index + 1;
if (firstChildIndex > 0)
for (var i = firstChildIndex; i < this.treeList.length; i++) {
let itemChild = this.treeList[i];
if (itemChild.level > item.level) {
if (item.isShowChild) {
if (itemChild.parentId === item.id) {
itemChild.isShow = item.isShowChild;
if (!itemChild.isShow) {
itemChild.isShowChild = false;
}
}
} else {
itemChild.isShow = item.isShowChild;
itemChild.isShowChild = false;
}
} else {
return;
}
}
},
// 节点选中、取消选中
_onItemSelect(item, index) {
//console.log('_onItemSelect')
//console.log(item)
if (!this.multiple) {
//单选
item.checkStatus = item.checkStatus == 0 ? 2 : 0;
this.treeList.forEach((v, i) => {
if (i != index) {
this.treeList[i].checkStatus = 0;
} else {
this.treeList[i].checkStatus = 2;
}
});
let selectedList = [];
let selectedNames;
selectedList.push(item);
selectedNames = item.name;
this._hide();
this.$emit("select-change", selectedList, selectedNames);
return;
}
let oldCheckStatus = item.checkStatus;
switch (oldCheckStatus) {
case 0:
item.checkStatus = 2;
item.childCheckCount = item.childCount;
item.childCheckPCount = 0;
break;
case 1:
case 2:
item.checkStatus = 0;
item.childCheckCount = 0;
item.childCheckPCount = 0;
break;
default:
break;
}
//子节点 全部选中
this._onItemChildSelect(item, index);
//父节点 选中状态变化
this._onItemParentSelect(item, index, oldCheckStatus);
},
_onItemChildSelect(item, index) {
//console.log('_onItemChildSelect')
let allChildCount = 0;
if (item.childCount && item.childCount > 0) {
index++;
while (
index < this.treeList.length &&
this.treeList[index].level > item.level
) {
let itemChild = this.treeList[index];
itemChild.checkStatus = item.checkStatus;
if (itemChild.checkStatus == 2) {
itemChild.childCheckCount = itemChild.childCount;
itemChild.childCheckPCount = 0;
} else if (itemChild.checkStatus == 0) {
itemChild.childCheckCount = 0;
itemChild.childCheckPCount = 0;
}
// console.log('>>>>index', index, 'item', itemChild.name, ' status', itemChild
// .checkStatus)
index++;
}
}
},
_onItemParentSelect(item, index, oldCheckStatus) {
//console.log('_onItemParentSelect')
//console.log(item)
const parentIndex = this.treeList.findIndex(
(itemP) => itemP.id == item.parentId
);
//console.log('parentIndex' + parentIndex)
if (parentIndex >= 0) {
let itemParent = this.treeList[parentIndex];
let count = itemParent.childCheckCount;
let oldCheckStatusParent = itemParent.checkStatus;
if (oldCheckStatus == 1) {
itemParent.childCheckPCount -= 1;
} else if (oldCheckStatus == 2) {
itemParent.childCheckCount -= 1;
}
if (item.checkStatus == 1) {
itemParent.childCheckPCount += 1;
} else if (item.checkStatus == 2) {
itemParent.childCheckCount += 1;
}
if (
itemParent.childCheckCount <= 0 &&
itemParent.childCheckPCount <= 0
) {
itemParent.childCheckCount = 0;
itemParent.childCheckPCount = 0;
itemParent.checkStatus = 0;
} else if (itemParent.childCheckCount >= itemParent.childCount) {
itemParent.childCheckCount = itemParent.childCount;
itemParent.childCheckPCount = 0;
itemParent.checkStatus = 2;
} else {
itemParent.checkStatus = 1;
}
//console.log('itemParent', itemParent)
this._onItemParentSelect(itemParent, parentIndex, oldCheckStatusParent);
}
},
// 重置数据
_reTreeList() {
this.treeList.forEach((v, i) => {
this.treeList[i].checkStatus = v.orCheckStatus;
});
},
_initTree() {
this.treeList = [];
this._formatTreeData(this.localdata);
},
},
watch: {
localdata() {
this._initTree();
},
localTreeList() {
this.treeList = this.localTreeList;
},
},
mounted() {
this._initTree();
},
};
</script>
<style scoped lang="less">
/deep/ .u-border {
border-width: 1rpx !important;
border-color: #d4c7c7 !important;
border-style: solid !important;
}
.tree-cover {
position: fixed;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
z-index: 100;
background-color: rgba(0, 0, 0, 0.4);
opacity: 0;
transition: all 0.3s ease;
visibility: hidden;
}
.tree-cover.show {
visibility: visible;
opacity: 1;
}
.tree-dialog {
position: fixed;
top: 0rpx;
right: 0rpx;
bottom: 0rpx;
left: 0rpx;
background-color: #fff;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
z-index: 102;
top: 20%;
transition: all 0.3s ease;
transform: translateY(100%);
}
.tree-dialog.show {
transform: translateY(0);
padding-bottom: 65px;
}
.tree-bar {
/* background-color: #fff; */
height: 90rpx;
padding-left: 25rpx;
padding-right: 25rpx;
display: flex;
justify-content: space-between;
align-items: center;
box-sizing: border-box;
border-bottom-width: 1rpx !important;
border-bottom-style: solid;
border-bottom-color: #f5f5f5;
font-size: 32rpx;
color: #757575;
line-height: 1;
}
.tree-bar-confirm {
color: #0055ff;
padding: 15rpx;
}
.tree-bar-title {
}
.tree-bar-cancel {
color: #757575;
padding: 15rpx;
}
.tree-view {
flex: 1;
padding: 20rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
overflow: hidden;
height: 100%;
}
.tree-list {
flex: 1;
height: 100%;
overflow: hidden;
}
.tree-item {
display: flex;
justify-content: space-between;
align-items: center;
line-height: 1;
height: 0;
opacity: 0;
transition: 0.2s;
overflow: hidden;
}
.tree-item.show {
height: 90rpx;
opacity: 1;
}
.tree-item.showchild:before {
transform: rotate(90deg);
}
.tree-item.last:before {
opacity: 0;
}
.switch-on {
width: 0;
height: 0;
border-left: 10rpx solid transparent;
border-right: 10rpx solid transparent;
border-top: 15rpx solid #666;
}
.switch-off {
width: 0;
height: 0;
border-bottom: 10rpx solid transparent;
border-top: 10rpx solid transparent;
border-left: 15rpx solid #666;
}
.item-last-dot {
position: absolute;
width: 10rpx;
height: 10rpx;
border-radius: 100%;
background: #666;
}
.item-icon {
width: 26rpx;
height: 26rpx;
margin-right: 8rpx;
padding-right: 20rpx;
padding-left: 20rpx;
}
.item-label {
flex: 1;
display: flex;
align-items: center;
height: 100%;
line-height: 1.2;
}
.item-name {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 450rpx;
}
.item-check {
width: 40px;
height: 40px;
display: flex;
justify-content: center;
align-items: center;
}
.item-check-yes,
.item-check-no {
width: 20px;
height: 20px;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
border-bottom-right-radius: 20%;
border-bottom-left-radius: 20%;
border-top-width: 1rpx;
border-left-width: 1rpx;
border-bottom-width: 1rpx;
border-right-width: 1rpx;
border-style: solid;
border-color: #0055ff;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
}
.item-check-yes-part {
width: 12px;
height: 12px;
border-top-left-radius: 20%;
border-top-right-radius: 20%;
border-bottom-right-radius: 20%;
border-bottom-left-radius: 20%;
background-color: #0055ff;
}
.item-check-yes-all {
margin-bottom: 5px;
border: 2px solid #007aff;
border-left: 0;
border-top: 0;
height: 12px;
width: 6px;
transform-origin: center;
/* #ifndef APP-NVUE */
transition: all 0.3s;
/* #endif */
transform: rotate(45deg);
}
.item-check .radio {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.item-check .radio .item-check-yes-b {
border-top-left-radius: 50%;
border-top-right-radius: 50%;
border-bottom-right-radius: 50%;
border-bottom-left-radius: 50%;
}
.hover-c {
opacity: 0.6;
}
.itemBorder {
border-bottom: 1px solid #e5e5e5;
}
</style>

View File

@@ -0,0 +1,88 @@
<template>
<view>
<u--input :value="select" disabled disabledColor="#ffffff" :placeholder="placeholder" border="none"></u--input>
<u-picker :show="show" :loading="loading" ref="uPicker" :columns="columns" @confirm="confirm" keyName="dicName"
@cancel="show = false"></u-picker>
</view>
</template>
<script>
import axios from "@/utils/request";
export default {
name: "dic-select",
props: {
show: {
type: Boolean,
default: false
},
code: {
type: String,
require: true
},
placeholder: {
type: String,
default: "请选择"
}
},
computed: {
select() {
if (this.value == undefined || this.value == "") {
return '';
}
for (var i = 0; i < (this.columns[0] || []).length; i++) {
const item=this.columns[0][i]
if(item.dicCode==this.value){
return item.dicName;
}
}
}
},
mounted() {
this.getDic();
},
data() {
return {
columns: [
[
]
],
loading: false,
value:undefined,
};
},
methods: {
getDic() {
const that = this;
this.loading = true;
axios.get("/dic/queryByDicType", {
"dicType": this.code
}).then(res => {
that.columns[0]=res;
that.$refs.uPicker.setColumnValues(0, res);
that.loading = false;
}).catch(error => {
that.loading = false;
this.closeShow();
});
},
confirm(e) {
this.value = e.value[0].dicCode;
this.$emit("selectChangeValue",e.value[0].dicCode)
this.$emit("selectChange",e.value[0])
this.closeShow();
},
closeShow(){
this.$emit("closeShow",false);
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,70 @@
<template>
<view>
<u-picker :show="show" :loading="loading" ref="uPicker" :columns="columns" @confirm="confirm" keyName="dicName"
@cancel="show = false"></u-picker>
</view>
</template>
<script>
export default{
name:"select-show"
props: {
show: {
type: Boolean,
default: false
},
code: {
type: String,
require: true
},
placeholder: {
type: String,
default: "请选择"
}
},
mounted() {
this.getDic();
},
data() {
return {
columns: [
[
]
],
loading: false,
value:undefined,
};
},
methods: {
getDic() {
const that = this;
this.loading = true;
axios.get("/dic/queryByDicType", {
"dicType": this.code
}).then(res => {
that.columns[0]=res;
that.$refs.uPicker.setColumnValues(0, res);
that.loading = false;
}).catch(error => {
that.loading = false;
this.closeShow();
});
},
confirm(e) {
this.value = e.value[0].dicCode;
this.$emit("selectChangeValue",e.value[0].dicCode)
this.$emit("selectChange",e.value[0])
this.closeShow();
},
closeShow(){
this.$emit("closeShow",false);
}
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,262 @@
<template>
<view class="popup" v-show="show">
<view class="bg" @tap="cancelMultiple"></view>
<view class="selectMultiple" :animation="animationData">
<view class="multipleBody">
<view class="title">
<view class="close" @tap="cancelMultiple">
取消
</view>
<view class="name">
{{title}}
</view>
<view class="confirm" @tap="confirmMultiple">
确认
</view>
</view>
<view class="list">
<view class="mask mask-top"></view>
<view class="mask mask-bottom"></view>
<scroll-view class="diet-list" scroll-y="true">
<view v-for="(item, index) in list" :class="['item', item.selected ? 'checked' : '']" @tap="onChange(index, item)">
<span>{{item.label}}</span>
<view class="icon" v-show="item.selected">
<icon type="success_no_circle" size="16" color="#2D8DFF"/>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
name:"multiple-picker",
data() {
return {
// 选中值
value: [],
// 选中列表
selected: [],
// 列表数据
list: [],
// 出场动画
animationData: {},
};
},
props: {
// 是否显示
show: {
type: Boolean,
default: false
},
// 标题
title: {
type: String,
default: ''
},
//数据列表
columns: {
type: Array,
default: [
{
label: '测试1',
value: '1',
}
]
},
// 默认选中
defaultIndex: {
type: Array,
default: [],
}
},
watch: {
// 监听是否显示
show(val) {
if(val) {
this.openMultiple();
}
}
},
methods: {
// 列点击事件
onChange(index, item) {
// 是否已选中
if(this.value.indexOf(item.value.toString()) >= 0) {
this.list[index].selected = false;
}
else {
this.list[index].selected = true;
}
// 筛选已勾选数据
this.value = [];
this.selected = [];
this.list.forEach((col_item, col_index) => {
if(col_item.selected) {
this.value.push(col_item.value.toString());
this.selected.push({
label: col_item.label,
value: col_item.value,
});
}
});
this.$emit("change", {selected: this.selected, value: this.value});
},
// 弹出框开启触发事件
openMultiple() {
// 初始化列表数据,默认勾选数据
this.value = this.defaultIndex;
this.columns.forEach((item, index) => {
this.$set(item, "selected", false);
if(this.value.indexOf(item.value.toString()) >= 0) {
item.selected = true;
}
});
this.list = Object.assign([], this.columns);
// 弹出动画
this.openAnimation();
},
// 确认
confirmMultiple() {
this.$emit("confirm", {selected: this.selected, value: this.value});
},
// 关闭/取消
cancelMultiple() {
this.$emit("cancel");
},
// 展开动画
openAnimation() {
var animation = uni.createAnimation()
animation.translate(0, 300).step({ duration: 0 });
this.animationData = animation.export();
this.$nextTick(() => {
animation.translate(0, 0).step({ duration: 300, timingFunction: 'ease' });
this.animationData = animation.export()
})
},
}
}
</script>
<style scoped lang="scss">
.popup {
width: 100%;
height: 100vh;
position: fixed;
z-index: 99999;
left: 0;
bottom: 0;
.bg {
width: 100%;
height: 100%;
background-color: rgba(black, .5);
}
}
.selectMultiple {
width: 100%;
position: absolute;
left: 0;
bottom: 0;
background-color: white;
.multipleBody {
width: 100%;
padding: 30rpx;
box-sizing: border-box;
padding-bottom: 80rpx;
.title {
font-size: 28rpx;
display: flex;
flex-direction: row;
.close {
width: 80rpx;
opacity: .5;
}
.name {
width: 530rpx;
text-align: center;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:1;
}
.confirm {
width: 80rpx;
text-align: right;
color: #2D8DFF;
}
}
.list {
width: 100%;
padding-top: 30rpx;
position: relative;
.mask {
width: 100%;
height: 120rpx;
position: absolute;
left: 0;
z-index: 2;
pointer-events: none;
&.mask-top {
top: 30rpx;
background-image: linear-gradient(to bottom, #fff, rgba(#fff, 0));
}
&.mask-bottom {
bottom: 0;
background-image: linear-gradient(to bottom, rgba(#fff, 0), #fff);
}
}
.diet-list {
max-height: 400rpx;
}
.item {
position: relative;
width: 100%;
line-height: 40rpx;
border-bottom: 1px solid rgba($color: #000000, $alpha: .05);
padding: 20rpx 0;
font-size: 30rpx;
box-sizing: border-box;
text-align: center;
span {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient:vertical;
-webkit-line-clamp:1;
padding: 0 40rpx;
}
.icon {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
height: 16px;
}
&.checked {
color: #2D8DFF;
}
&:last-child {
border-bottom: none;
margin-bottom: 60rpx;
}
&:first-child {
margin-top: 60rpx;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,73 @@
<template>
<view class="mask">
<view class="three-bounce">
<view class="bounce1"></view>
<view class="bounce2"></view>
<view class="bounce3"></view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
};
}
}
</script>
<style>
.mask{
position: absolute;
width: 100%;
height: 100%;
left:0;
top:0;
z-index:600;
background:rgba(0,0,0,.75);
}
.three-bounce {
min-width: 60px;
min-height: 30px;
position: absolute;
left:50%;
top:50%;
transform: translateX(-50%) translateY(-50%);
z-index:100;
}
.three-bounce view {
width: 12%;
height: 12%;
min-height: 10px;
min-width: 10px;
background-color: #ffffff;
border-radius: 100%;
display: inline-block;
animation: bouncedelay 1.4s infinite ease-in-out;
animation-fill-mode: both;
}
.three-bounce .bounce1 {
animation-delay: -0.32s;
}
.three-bounce .bounce2 {
animation-delay: -0.16s;
}
@keyframes bouncedelay {
0%,
80%,
100% {
transform: scale(0.0);
}
40% {
transform: scale(1.0);
}
}
</style>

View File

@@ -0,0 +1,293 @@
$div-table-border-color: #666;
$div-table-border-width: 1upx;
@mixin fixBorderWidth($dir:none) {
@if $dir==none{
@media screen and (-webkit-device-pixel-ratio: 1) {
border-width: 1px;
}
@media screen and (-webkit-device-pixel-ratio: 2) {
border-width: .5px;
}
@media screen and (-webkit-device-pixel-ratio: 3) {
border-width: .5px;
}
}
@else{
@media screen and (-webkit-device-pixel-ratio: 1) {
border-#{$dir}-width: 1px;
}
@media screen and (-webkit-device-pixel-ratio: 2) {
border-#{$dir}-width: .5px;
}
@media screen and (-webkit-device-pixel-ratio: 3) {
border-#{$dir}-width: .5px;
}
}
}
.no-bad-table-wrap {
position: relative;
overflow: hidden;
//外层容器
.table_box_big {
overflow: auto;
width: 100%;
position: relative;
// height: 350px;
}
//工具类
.verticalV{
position: absolute;
width: 100%;
text-align: center;
left:50%;
top:50%;
transform: translateY(-50%);
}
.table_box {
overflow: auto;
// position: absolute;
}
.table_tbody_box {
// height: 300px;
// overflow: scroll;
}
//没有任何数据
.empty-data-body-box {
min-height: 100px;
line-height: 100px;
text-align: center;
color: #666;
border: 1px solid $div-table-border-color;
@include fixBorderWidth;
border-top: 0;
}
&.fix-height {
.table_box_big {
overflow-x: scroll;
overflow-y: hidden;
position: relative;
// height: 350px;
}
.table_box {
overflow: hidden;
position: absolute;
}
.table_tbody_box {
// height: 300px;
overflow: scroll;
border-bottom: 1px solid $div-table-border-color;
@include fixBorderWidth(bottom);
}
//固定高低里面的内容表格底部不要边框
.div-table-body {
border-top: 0;
border-bottom:0;
//固定高度,数据为空
&.empty-data-body-box{
border-bottom: $div-table-border-width solid $div-table-border-color;
@include fixBorderWidth(bottom);
}
}
}
//头部表格
.th,
.thead .tr,
.fixed-thead-tr {
.td {
background-color: #e0e0ea;
.td_wrap {
background-color: #e0e0ea;
}
}
}
//固定右边一列
.fixed-right {
position: absolute;
top: 0upx;
right: 0px;
z-index: 100;
// border-right: $div-table-border-width solid $div-table-border-color;
// border-left: $div-table-border-width solid $div-table-border-color;
box-shadow: -2px 0 5px rgba(0, 0, 0, .5);
}
//固定左边一列
.fixed-left {
position: absolute;
top: 0upx;
left: -1px;
z-index: 100;
border-right: $div-table-border-width solid $div-table-border-color;
border-left: $div-table-border-width solid $div-table-border-color;
@include fixBorderWidth(right);
@include fixBorderWidth(left);
box-shadow: 2px 0 5px rgba(0, 0, 0, .5);
}
.tr,
.th {
display: table-row;
&+.tr,
&+.th {
.td,
.th {
border-top: $div-table-border-width solid $div-table-border-color;
@include fixBorderWidth(top);
word-break: break-word;
}
}
}
//外层容器控制td左右居中
&.td-center{
.td {
.td_wrap{
text-align: center;
}
}
}
.td {
display: table-cell;
vertical-align: middle;
text-align: center;
box-sizing: border-box;
z-index: 1;
position: relative;
overflow: hidden;
.td_wrap {
position: relative;
padding: 10upx;
box-sizing: border-box;
overflow: hidden;
line-height: 20px;
font-size: 12px;
background: #fff;
text-align: left;
}
&.colspan {
z-index: 10;
position: absolute;
.td_wrap {
width: 100%;
height: 100%;
left: 0;
top: 0;
position: absolute;
}
}
&.rowspan {
position: absolute;
z-index: 10;
}
&.empty-cells-for-celspan {
border-left: none !important;
.td_wrap {
opacity: 0;
}
}
&.empty-cells-for-rowspan {
border-top: none !important;
.td_wrap {
opacity: 0;
}
}
&.noPadding {
padding: 0;
}
&+.td {
border-left: $div-table-border-width solid $div-table-border-color;
@include fixBorderWidth(left);
}
}
.th .td {
font-weight: bold;
}
//单选样式========》选中
.selected {
.td {
background-color: #d3e3ef;
.td_wrap {
background-color: #d3e3ef;
}
}
}
//单选样式===》禁用
.disabled {
.td {
background-color: #f8f8f9;
opacity: .6;
}
}
.div-table {
display: table;
border: $div-table-border-width solid $div-table-border-color;
@include fixBorderWidth;
box-sizing: border-box;
table-layout: fixed;
position: relative;
&.div-table-body {
border-top: 0;
}
.tbody {
display: table-row-group;
}
.thead {
display: table-header-group;
// .tr,
// .th {
//
// .td,
// .th {
// width: 120upx;
// height: 75px;
// }
// }
}
.colgroup {
display: table-column-group;
}
.col {
display: table-column;
}
.caption {
display: table-caption;
}
}
}

View File

@@ -0,0 +1,841 @@
<template>
<view
class="no-bad-table-wrap"
:class="[
tableHeight != 'auto' ? 'fix-height' : '',
celCenter ? 'td-center' : '',
]"
>
<view class="table_box_big" :style="{ height: tableHeight }">
<view class="table_box">
<!-- 头部内容 -->
<view class="div-table div-table-head">
<view class="thead">
<view class="tr">
<view
class="td selection"
v-if="selection == 'mulit'"
:style="{ width: selectionTdWidth, height: thTdHeight + 'px' }"
>
<view
:class="['td_wrap']"
:style="{
width: selectionTdWidth,
height: thTdHeight + 'px',
}"
>
<checkbox-group @change="checkboxChangeAll">
<checkbox
value="all"
color="#999"
:checked="switchAllCheckBox"
style="transform: scale(0.7)"
/>
</checkbox-group>
</view>
</view>
<view
class="td"
:style="{
width: countHeadColspanWidth(item, index),
height: thTdHeight + 'px',
}"
v-for="(item, index) in columns"
:key="item.key"
>
<view
class="td_wrap"
:style="{
width: countHeadColspanWidth(item, index, true),
height: thTdHeight + 'px',
}"
>{{ item.title }}</view
>
</view>
</view>
</view>
</view>
<!-- 头部内容 -->
<!-- 有数据情况 -->
<template v-if="list.length">
<view class="table_tbody_box" :style="{ height: talbeBodyHeight }">
<view class="div-table div-table-body">
<checkbox-group @change="checkboxChange">
<template v-for="(item, index) in list">
<view
:class="[
'tr',
rowClassNamePlus(item, index),
selection == 'single' && checkBoxList[index].$checked
? 'selected'
: '',
selection == 'single' && checkBoxList[index].$disabled
? 'disabled'
: '',
]"
@click="selectRow(item, index)"
:key="item.id"
>
<!-- 多选操作 -->
<view
class="td selection"
v-if="selection == 'mulit'"
:style="{
width: selectionTdWidth,
height: tdHeight + 'px',
}"
>
<view
:class="['td_wrap']"
:style="{
width: selectionTdWidth,
height: tdHeight + 'px',
}"
>
<checkbox
:value="checkBoxList[index].id"
color="#999"
:disabled="checkBoxList[index].$disabled"
:checked="checkBoxList[index].$checked"
style="transform: scale(0.7)"
/>
</view>
</view>
<template v-for="(tdItem, tdItemIndex) in columns">
<view
class="td"
:class="[
item.cellClassName && item.cellClassName[tdItem.key]
? item.cellClassName[tdItem.key]
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'rowspan'
] == 0
? 'empty-cells-for-rowspan'
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'colspan'
] == 0
? 'empty-cells-for-celspan'
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'rowspan'
] > 1
? 'rowspan'
: '',
spanMethod(item, tdItem, index, tdItemIndex)[
'colspan'
] > 1
? 'colspan'
: '',
]"
:style="{
height: countRowspanHeight(
item,
tdItem,
index,
tdItemIndex
),
width: countColspanWidth(
item,
tdItem,
index,
tdItemIndex
),
}"
:key="tdItem.key"
>
<view
:class="['td_wrap']"
:style="{
height: countRowspanHeight(
item,
tdItem,
index,
tdItemIndex
),
width: countColspanWidth(
item,
tdItem,
index,
tdItemIndex,
true
),
}"
>
<slot
:row="item"
:index="index"
:col="tdItem"
:isRead="isRead"
v-if="slotCols.indexOf(tdItem.key) > -1"
></slot>
<template v-else-if="tdItem.$operateList">
<template v-for="btn in tdItem.$operateList">
<button
:class="[btn.styles ? btn.styles : '']"
v-bind:style="{
padding: '2px 5px',
fontSize: '12px',
lineHeight: '1.2',
display: 'inline-block',
}"
@click="
pullEvent(btn.event, {
row: item,
index: index,
})
"
type="primary"
size="min"
:key="btn.id"
>
{{ btn.label }}
</button>
</template>
</template>
<template v-else>{{ item[tdItem.key] }} </template>
</view>
</view>
</template>
</view>
</template>
</checkbox-group>
</view>
</view>
</template>
<template v-else>
<view
class="table_tbody_box empty-data-body-box div-table-body"
:style="{ height: emptyColHeight, width: emptyColWidth }"
>
<view
class="tr"
:style="{ height: emptyColHeight, width: emptyColWidth }"
>
<view
class="td"
:style="{ height: emptyColHeight, width: emptyColWidth }"
>
<view
:class="['td_wrap']"
:style="{
height: parseInt(emptyColHeight - 2) + 'px',
width: emptyColWidth,
lineHeight: parseInt(emptyColHeight - 2) + 'px',
}"
>
<text @click="emptyClickCallBack">{{ emptyText }}</text>
</view>
</view>
</view>
</view>
</template>
<template v-if="list.length">
<!-- 固定左边一列 -->
<view
class="fixed-left"
v-if="
columnsFixedLeft[0] || (selection == 'mulit' && fixedCheckbox)
"
>
<view class="tr fixed-thead-tr">
<!-- 多选且固定左边 【-->
<view
class="td selection"
v-if="selection == 'mulit' && fixedCheckbox"
:style="{ width: selectionTdWidth, height: thTdHeight + 'px' }"
>
<view
:class="['td_wrap']"
:style="{
width: selectionTdWidth,
height: thTdHeight + 'px',
}"
>
<checkbox-group @change="checkboxChangeAll">
<checkbox
value="all"
color="#999"
:checked="switchAllCheckBox"
style="transform: scale(0.7)"
/>
</checkbox-group>
</view>
</view>
<!-- 多选且固定左边 】-->
<!-- 普通固定左边 【-->
<view
class="td"
v-if="columnsFixedLeft[0]"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
<view
class="td_wrap"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
{{ columnsFixedLeft[0].title }}
</view>
</view>
<!-- 普通固定左边 】-->
</view>
<view
v-for="(item, index) in list"
:key="item.id"
:class="[
'tr',
selection == 'single' && checkBoxList[index].$checked
? 'selected'
: '',
selection == 'single' && checkBoxList[index].$disabled
? 'disabled'
: '',
]"
@click="selectRow(item, index)"
>
<!-- 多选且固定左边 】-->
<view
class="td selection fixed-td"
v-if="selection == 'mulit' && fixedCheckbox"
:style="{ width: selectionTdWidth, height: tdHeight + 'px' }"
>
<view
:class="['td_wrap']"
:style="{ width: selectionTdWidth, height: tdHeight + 'px' }"
>
<checkbox
:value="checkBoxList[index].id"
color="#999"
:disabled="checkBoxList[index].$disabled"
:checked="checkBoxList[index].$checked"
style="transform: scale(0.7)"
/>
</view>
</view>
<!-- 普通固定左边 【-->
<view
class="td fixed-td"
v-if="columnsFixedLeft[0]"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
<view
class="td_wrap fixed-wrap"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft[0]),
}"
>
<!-- td内容 【-->
<slot
:row="item"
v-if="
slotCols.indexOf(
columnsFixedLeft[0] && columnsFixedLeft[0].key
) > -1
"
></slot>
<template v-if="columnsFixedLeft[0].$operateList">
<template v-for="btn in columnsFixedLeft[0].$operateList">
<button
:class="[btn.styles ? btn.styles : '']"
v-bind:style="{
padding: '2px 5px',
fontSize: '12px',
lineHeight: '1.2',
display: 'inline-block',
}"
@click="
pullEvent(btn.event, { row: item, index: index })
"
type="primary"
size="min"
:key="btn.id"
>
{{ btn.label }}
</button>
</template>
</template>
<template v-else
>{{ item[columnsFixedLeft[0].key] }}
</template>
<!-- td内容 】-->
</view>
</view>
<!-- 普通固定左边 】-->
</view>
</view>
<!-- 固定左边一列 】-->
<!-- 固定右边一列 【-->
<view class="fixed-right" v-if="columnsFixedRight[0]">
<view class="tr fixed-thead-tr">
<view
class="td"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
<view
class="td_wrap"
:style="{
height: fixedHeight(columnsFixedLeft[0]),
width: fixedWidth(columnsFixedLeft),
}"
>
{{ columnsFixedRight[0].title }}
</view>
</view>
</view>
<view
v-for="(item, index) in list"
:key="item.id"
:class="[
'tr',
selection == 'single' && checkBoxList[index].$checked
? 'selected'
: '',
selection == 'single' && checkBoxList[index].$disabled
? 'disabled'
: '',
]"
@click="selectRow(item, index)"
>
<view
class="td fixed-td"
:style="{
height: fixedHeight(columnsFixedRight[0]),
width: fixedWidth(columnsFixedRight[0]),
}"
>
<view
class="td_wrap fixed-wrap"
:style="{
height: fixedHeight(columnsFixedRight[0]),
width: fixedWidth(columnsFixedRight[0]),
}"
>
<!-- td内容 【-->
<slot
:row="item"
v-if="
slotCols.indexOf(
columnsFixedRight[0] && columnsFixedRight[0].key
) > -1
"
></slot>
<template
v-if="
columnsFixedRight[0] && columnsFixedRight[0].$operateList
"
>
<template v-for="btn in columnsFixedRight[0].$operateList">
<button
:class="[btn.styles ? btn.styles : '']"
v-bind:style="{
padding: '2px 5px',
fontSize: '12px',
lineHeight: '1.2',
display: 'inline-block',
}"
@click="
pullEvent(btn.event, { row: item, index: index })
"
type="primary"
size="min"
:key="btn.id"
>
{{ btn.label }}
</button>
</template>
</template>
<template v-else
>{{ item[columnsFixedRight[0].key] }}
</template>
<!-- td内容 】-->
</view>
</view>
</view>
</view>
<!-- 固定右边一列 】-->
</template>
</view>
</view>
<loading-component v-if="loading" />
</view>
</template>
<script>
import loadingComponent from "./loading.vue";
export default {
components: { loadingComponent },
props: {
//显示列
columns: {
type: Array,
required: true,
},
//数据
list: {
type: Array,
required: true,
},
//单元格样式居中
"cel-center": {
type: Boolean,
default: true,
},
//自定义行和列样式
rowClassName: {
type: [String, Function],
default: "",
},
//自定义列元素
"slot-cols": {
type: Array,
default: () => {
return [];
},
},
//行列合并函数
"span-method": {
type: Function,
default: () => {
return () => {
return {
rowspan: 1,
colspan: 1,
};
};
},
},
spanArr: {
type: Array,
default: [],
},
isRead: {
type: Number,
default: 0,
},
//是否可选 mulit=>多选 single=》单选
selection: {
type: String,
default: "none",
},
"fixed-checkbox": {
type: Boolean,
default: false,
},
loading: {
type: Boolean,
default: false,
},
height: {
type: Number,
default: undefined,
},
"td-width": {
type: Number,
default: 110,
},
"td-height": {
type: Number,
default: 50,
},
"th-td-height": {
type: Number,
default: 50,
},
"td-padding": {
type: Number,
default: 10,
},
"border-color": {
type: String,
default: "#666",
},
emptyText: {
type: String,
default: "数据为空",
},
//空提示点击事件
emptyClickFn: {
type: Function,
default() {
return () => {};
},
},
},
computed: {
columnsFixedRight() {
let t = this.columns.filter((item) => item.$fixed == "right");
return t.length ? [t[0]] : [];
},
columnsFixedLeft() {
let t = this.columns.filter((item) => item.$fixed == "left");
return t.length ? [t[0]] : [];
},
//表格高度
tableHeight() {
return Number(this.height) && Number(this.height) > this.tdHeight * 3
? this.height + "px"
: "auto";
},
//表格主体高度
talbeBodyHeight() {
let t =
this.tableHeight !== "auto"
? parseInt(this.tableHeight) - this.tdHeight - 3 + "px"
: "auto";
return t;
},
//可选的列表长度
allCheckBoxAbledLen() {
return this.checkBoxList.filter((item) => !item.$disabled).length;
},
//没数据时候主体高度
emptyColHeight() {
let t = this.height ? this.height - this.thTdHeight - 30 + "px" : "100px";
// console.log("emptyColHeight",this.height, this.thTdHeight,t)
return t;
},
//没数据时候,主体的宽度
emptyColWidth() {
let t = this.tdWidth * this.columns.length;
if (this.selection == "mulit") {
t = parseInt(this.selectionTdWidth) + t;
}
return t + "px";
},
},
data() {
return {
checkBoxList: [], //多选=》选中列表
switchAllCheckBox: false, //多选=》全选
selectionTdWidth: "750rpx", //多选列宽
singleSelect: {}, //单选,选中行
};
},
watch: {
list() {
this.asyncCheckBoxList();
},
},
created() {
this.asyncCheckBoxList();
},
methods: {
//获取数据副本,轻拷贝
asyncCheckBoxList() {
this.checkBoxList = this.list.map((item) => {
return { ...item };
});
},
//自定义行样式
rowClassNamePlus(row, index) {
if (typeof this.rowClassName === "string") {
return this.rowClassName;
} else if (typeof this.rowClassName === "function") {
return this.rowClassName(row, index);
}
},
//事件触发
pullEvent(event, data) {
this.$emit(event, data);
},
/**
* 计算单列宽
* iswrap 是否是内容容器
*/
countColspanWidth(item, tdItem, index, tdItemIndex, iswrap = false) {
let borderLeft = iswrap && tdItemIndex > 0 ? 1 : 0;
//是否跨列,返回跨列个数1为不跨列
let moreThanOne =
this.spanMethod(item, tdItem, index, tdItemIndex) &&
this.spanMethod(item, tdItem, index, tdItemIndex)["colspan"];
//console.log(`第${index}行,第${tdItemIndex}列 是跨列么?${moreThanOne}`);
let t =
moreThanOne > 1
? moreThanOne * this.tdWidth - borderLeft + "px"
: this.tdWidth - borderLeft + "px";
//跨列
if (moreThanOne > 1) {
let countWidth = 0;
for (let i = tdItemIndex; i < tdItemIndex + (moreThanOne - 1); i++) {
countWidth +=
this.columns[i].$width && parseInt(this.columns[i].$width)
? parseInt(moreThanOne * this.columns[i].$width) - borderLeft
: this.tdWidth * moreThanOne;
}
return countWidth + "px";
} else {
//不跨列
let tmp =
this.columns[tdItemIndex].width &&
parseInt(this.columns[tdItemIndex].width)
? parseInt(this.columns[tdItemIndex].width)
: this.tdWidth;
return tmp + "rpx";
}
return t;
},
/**
* 固定列宽
*/
fixedWidth(fixedWidth) {
return fixedWidth && fixedWidth.$width
? fixedWidth + "px"
: this.tdWidth + "px";
},
/*
* 固定列-》列高
*/
fixedHeight(fixedHeight) {
return fixedHeight && fixedHeight.$height
? fixedHeight + "px"
: this.tdHeight + "px";
},
/**
* 计算头部td的宽度
* */
countHeadColspanWidth(item, index, iswrap = false) {
let borderLeft = iswrap && index > 0 ? 1 : 0;
let tmp = item.width ? parseInt(item.width) : this.tdWidth;
return tmp + "rpx";
},
/**
* 计算单列高
* */
countRowspanHeight(item, tdItem, index, tdItemIndex) {
//是否跨行
let moreThanOne =
this.spanMethod(item, tdItem, index, tdItemIndex) &&
this.spanMethod(item, tdItem, index, tdItemIndex)["rowspan"] > 1;
let t = moreThanOne
? this.spanMethod(item, tdItem, index, tdItemIndex)["rowspan"] *
this.tdHeight +
"px"
: this.tdHeight + "px";
return t;
},
/*
* 单选行
* */
selectRow(item, index) {
if (item.$disabled) {
return;
}
//非单选方式
if (this.selection != "single") {
return;
}
this.checkBoxList = this.checkBoxList.map((sitem, sindex) => {
if (index === sindex) {
sitem.$checked = true;
} else {
sitem.$checked = false;
}
return sitem;
});
if (this.selection) {
this.$emit("on-selection-change", {
old: this.singleSelect,
new: {
index,
item,
},
});
}
this.singleSelect = {
index,
item,
};
},
/*
* 多选
* */
checkboxChange(e) {
let val = e.detail.value;
let before = [];
for (let v = 0; v < this.allCheckBoxAbledLen; v++) {
if (this.checkBoxList[v].$checked === true) {
before.push({ ...this.checkBoxList[v] });
}
}
if (val.length == this.allCheckBoxAbledLen) {
this.switchAllCheckBox = true;
this.checkBoxList = this.checkBoxList.map((item) => {
if (!item.$disabled) {
item.$checked = true;
}
return item;
});
} else {
this.switchAllCheckBox = false;
this.checkBoxList = this.checkBoxList.map((item) => {
if (val.indexOf(item.id) > -1) {
item.$checked = true;
} else {
item.$checked = false;
}
return item;
});
}
this.$emit("on-selection-change", {
old: before,
new: this.checkBoxList.filter((item) => item.$checked === true),
});
},
/*
* 全选
* */
checkboxChangeAll(e) {
let val = e.detail.value;
let before = [];
for (let v = 0; v < this.allCheckBoxAbledLen; v++) {
if (this.checkBoxList[v].$checked === true) {
before.push({ ...this.checkBoxList[v] });
}
}
if (val && val[0] == "all") {
this.switchAllCheckBox = true;
this.checkBoxList = this.checkBoxList.map((item) => {
if (!item.$disabled) {
item.$checked = true;
}
return item;
});
} else {
this.switchAllCheckBox = false;
this.checkBoxList = this.checkBoxList.map((item) => {
item.$checked = false;
return item;
});
}
this.$emit("on-selection-change", {
old: before,
new: this.checkBoxList.filter((item) => item.$checked === true),
});
},
/*
* 空提示点击事件
* */
emptyClickCallBack() {
typeof this.emptyClickFn == "function" ? this.emptyClickFn() : "";
},
},
};
</script>
<style lang="scss">
@import "components/no-bad-table/table.scss";
</style>

View File

@@ -0,0 +1,163 @@
<template>
<view>
<u-tabbar
:value="current"
@change="tabbarChange"
z-index="999"
activeColor="#2F6D47"
inactiveColor="#687379"
:fixed="true"
:placeholder="true"
:safeAreaInsetBottom="true"
>
<u-tabbar-item name="index" text="首页" v-if="!_isGjt">
<u-icon
name="home-fill"
size="45"
slot="active-icon"
color="#2F6D47"
></u-icon>
<u-icon
name="home-fill"
size="45"
slot="inactive-icon"
color="#687379"
></u-icon>
</u-tabbar-item>
<u-tabbar-item name="map" text="地图" v-if="_isShowMap">
<u-icon
name="map-fill"
size="45"
slot="active-icon"
color="#2F6D47"
></u-icon>
<u-icon
name="map-fill"
size="45"
slot="inactive-icon"
color="#687379"
></u-icon>
</u-tabbar-item>
<u-tabbar-item name="my" text="我的">
<u-icon
name="account-fill"
size="45"
slot="active-icon"
color="#2F6D47"
></u-icon>
<u-icon
name="account-fill"
size="45"
slot="inactive-icon"
color="#687379"
></u-icon>
</u-tabbar-item>
</u-tabbar>
</view>
</template>
<script>
import { getUser } from "@/utils/auth.js";
import { checkButtonPermission } from "@/utils/permission.js";
export default {
options: {
styleIsolation: "shared", // 解除样式隔离
},
data() {
return {
// userInfo: {}, // 用户信息
// list: [],
};
},
computed: {
current() {
return this.$store.state.current || "index";
},
_userInfo() {
console.log("---------------------");
console.log(getUser());
return getUser() || {};
},
_isGjt() {
console.log(123123123);
console.log(this._userInfo.id);
return this._userInfo.id == "1131365259079106560";
},
_isShowMap() {
return checkButtonPermission(
"weappMap",
"vehicle" //按钮在基础信息菜单的车辆信息下面
);
// const ids = this._userInfo?.roles?.map((item) => item.roleName) || [];
// if (ids.includes("地图查看")) {
// // if (ids.includes("1120244873740435456")) ID切库会变 改成中文吧
// return true; //统一改成地图查看权限配置
// } else {
// return false; //其他
// }
},
// _list() {
// return this._isShowMap
// ? [
// {
// path: "pages/index/index",
// },
// {
// path: "pages/map/index",
// },
// {
// path: "pages/my/my",
// },
// ]
// : [
// {
// path: "pages/index/index",
// },
// {
// path: "pages/my/my",
// },
// ];
// },
},
// watch: {
// _userInfo: {
// handler(newVal) {
// this.userInfo = newVal;
// },
// immediate: true,
// deep: true,
// },
// },
mounted() {
console.log("tabBar mounted");
console.log(this._userInfo);
},
methods: {
tabbarChange(e) {
console.log(e);
this.$store.state.current = e;
let arr = [
{
name: "index",
path: "pages/index/index",
},
{
name: "map",
path: "pages/map/index",
},
{
name: "my",
path: "pages/my/my",
},
];
let path =
arr.find((item) => item.name == e)?.path || "pages/index/index";
uni.switchTab({
url: "/" + path,
});
},
},
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,153 @@
<template>
<view>
<u-upload :accept="accept" :fileList="imgList" @afterRead="afterRead" @delete="deletePic" :name="paramsName"
:multiple="isMultiple" :maxCount="limit" ref="upload">
<u-button v-if="isBtn" plain type="primary" text="上传" size="mini" icon="arrow-upward" class="btn-icon"
style="border: 1px solid #c2d10a;" iconColor="#c2d10a" color="#c2d10a"></u-button>
<view v-else class="u-upload__button" :hover-class="!disabled ? 'u-upload__button--hover' : ''"
hover-stay-time="150" :class="[disabled && 'u-upload__button--disabled']" :style="[{
width: $u.addUnit(80),
height: $u.addUnit(80)
}]" style="background-color: aliceblue;display: flex;align-items: center;justify-content: center;">
<u-icon name="camera-fill" size="26" color="#D3D4D6"></u-icon>
</view>
</u-upload>
</view>
</template>
<script>
export default {
options: {
styleIsolation: 'shared', // 解除样式隔离
},
name: "upload_img_video",
props: {
limit: {
type: Number,
default: 10
},
isMultiple: {
type: Boolean,
default: true
},
isBtn: {
type: Boolean,
default: false
},
accept: {
type: String,
default: "image"
},
disabled: {
type: Boolean,
default: false
},
paramsName: {
type: String,
default: "file"
}
},
data() {
return {
imgList: [],
};
},
methods: {
// 删除图片
deletePic(event) {
this.imgList.splice(event.index, 1)
this.$emit('successFile', this.imgList);
},
// 新增图片
async afterRead(event) {
const that = this;
// 当设置 multiple 为 true 时, file 为数组格式,否则为对象格式
let lists = [].concat(event.file)
let fileListLen = this.imgList.length
lists.map((item) => {
that.imgList.push({
...item,
status: 'uploading',
message: '上传中'
})
})
for (let i = 0; i < lists.length; i++) {
const result = await this.uploadFilePromise(lists[i].url)
let item = this.imgList[fileListLen]
that.imgList.splice(fileListLen, 1, Object.assign(item, {
status: 'success',
message: '',
url: result.data.url,
name: result.data.fileName
}))
fileListLen++
}
this.$emit('successFile', this.imgList);
},
uploadFilePromise(url) {
return new Promise((resolve, reject) => {
let a = uni.uploadFile({
url: '/attachment/upload', // 仅为示例,非真实的接口地址
filePath: url,
name: this.paramsName,
success: (res) => {
resolve(JSON.parse(res.data || {}))
},
fail: (e) => {
reject(e);
}
});
})
},
}
}
</script>
<style lang="less" scoped>
/deep/.btn-icon {
font-size: 10rpx !important;
height: 58rpx !important;
/deep/.u-icon {
/deep/.u-icon__icon--primary {
font-size: 20rpx !important;
}
}
}
/deep/.u-button--mini {
width: 120rpx !important;
.u-icon {
font-size: 20rpx !important;
.u-icon__icon {
font-size: 20rpx !important;
}
}
height: 58rpx !important;
}
/deep/.u-upload__deletable {
width: 20px !important;
height: 20px !important;
.uicon-close {
font-size: 14px !important;
line-height: 14px !important;
}
}
/deep/.u-upload__success {
border-bottom-color: #c2d10a !important;
border-right-color: #c2d10a !important;
.uicon-checkmark {
font-size: 14px !important;
line-height: 14px !important;
}
}
</style>