flat:所属社区
This commit is contained in:
@@ -135,3 +135,8 @@ export const cancelUserInvite = (params) => request({
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
|
export const getDeptAllTree = (params) => request({
|
||||||
|
url: '/api/jobslink-api/system/dept/all-tree',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
@@ -1,187 +1,189 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="showPopup" class="uni-popup">
|
<view v-if="showPopup" class="uni-popup">
|
||||||
<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask" @click="close(true)" />
|
<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask"
|
||||||
<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper" @click="close(true)">
|
@click="close(true)" />
|
||||||
<view class="uni-popup__wrapper-box" @click.stop="clear">
|
<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper"
|
||||||
<slot />
|
@click="close(true)">
|
||||||
</view>
|
<view class="uni-popup__wrapper-box" @click.stop="clear">
|
||||||
</view>
|
<slot />
|
||||||
</view>
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'UniPopup',
|
name: 'UniPopup',
|
||||||
props: {
|
props: {
|
||||||
// 开启动画
|
// 开启动画
|
||||||
animation: {
|
animation: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'center'
|
default: 'center'
|
||||||
},
|
},
|
||||||
// 是否开启自定义
|
// 是否开启自定义
|
||||||
custom: {
|
custom: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
// maskClick
|
// maskClick
|
||||||
maskClick: {
|
maskClick: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
show: {
|
show: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
ani: '',
|
ani: '',
|
||||||
showPopup: false
|
showPopup: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
show(newValue) {
|
show(newValue) {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
this.open()
|
this.open()
|
||||||
} else {
|
} else {
|
||||||
this.close()
|
this.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {},
|
created() {},
|
||||||
methods: {
|
methods: {
|
||||||
clear() {},
|
clear() {},
|
||||||
open() {
|
open() {
|
||||||
this.$emit('change', {
|
this.$emit('change', {
|
||||||
show: true
|
show: true
|
||||||
})
|
})
|
||||||
this.showPopup = true
|
this.showPopup = true
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.ani = 'uni-' + this.type
|
this.ani = 'uni-' + this.type
|
||||||
}, 30)
|
}, 30)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
close(type) {
|
close(type) {
|
||||||
if (!this.maskClick && type) return
|
if (!this.maskClick && type) return
|
||||||
this.$emit('change', {
|
this.$emit('change', {
|
||||||
show: false
|
show: false
|
||||||
})
|
})
|
||||||
this.ani = ''
|
this.ani = ''
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showPopup = false
|
this.showPopup = false
|
||||||
}, 300)
|
}, 300)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
|
|
||||||
.uni-popup {
|
.uni-popup {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__mask {
|
.uni-popup__mask {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 998;
|
z-index: 998;
|
||||||
/* background: rgba(0, 0, 0, .4); */
|
background: rgba(0, 0, 0, .4);
|
||||||
opacity: 0
|
opacity: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__mask.ani {
|
.uni-popup__mask.ani {
|
||||||
transition: all .3s
|
transition: all .3s
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__mask.uni-bottom,
|
.uni-popup__mask.uni-bottom,
|
||||||
.uni-popup__mask.uni-center,
|
.uni-popup__mask.uni-center,
|
||||||
.uni-popup__mask.uni-top {
|
.uni-popup__mask.uni-top {
|
||||||
opacity: 1
|
opacity: 1
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper {
|
.uni-popup__wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.ani {
|
.uni-popup__wrapper.ani {
|
||||||
transition: all .3s
|
transition: all .3s
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.top {
|
.uni-popup__wrapper.top {
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transform: translateY(-100%)
|
transform: translateY(-100%)
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.bottom {
|
.uni-popup__wrapper.bottom {
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transform: translateY(100%)
|
transform: translateY(100%)
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.center {
|
.uni-popup__wrapper.center {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
transform: scale(1.2);
|
transform: scale(1.2);
|
||||||
opacity: 0
|
opacity: 0
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper-box {
|
.uni-popup__wrapper-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
.uni-popup__wrapper.uni-custom .uni-popup__wrapper-box {
|
||||||
padding: 30upx;
|
padding: 30upx;
|
||||||
background: #fff
|
background: #fff
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
|
.uni-popup__wrapper.uni-custom.center .uni-popup__wrapper-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
max-width: 80%;
|
max-width: 80%;
|
||||||
max-height: 80%;
|
max-height: 80%;
|
||||||
overflow-y: scroll
|
overflow-y: scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.uni-custom.bottom .uni-popup__wrapper-box,
|
.uni-popup__wrapper.uni-custom.bottom .uni-popup__wrapper-box,
|
||||||
.uni-popup__wrapper.uni-custom.top .uni-popup__wrapper-box {
|
.uni-popup__wrapper.uni-custom.top .uni-popup__wrapper-box {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
overflow-y: scroll
|
overflow-y: scroll
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.uni-bottom,
|
.uni-popup__wrapper.uni-bottom,
|
||||||
.uni-popup__wrapper.uni-top {
|
.uni-popup__wrapper.uni-top {
|
||||||
transform: translateY(0)
|
transform: translateY(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
.uni-popup__wrapper.uni-center {
|
.uni-popup__wrapper.uni-center {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
opacity: 1
|
opacity: 1
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -101,6 +101,11 @@
|
|||||||
<u--input v-model="info.jobCompanyIndustry" border="none" :disabled="true" disabledColor="#ffffff"
|
<u--input v-model="info.jobCompanyIndustry" border="none" :disabled="true" disabledColor="#ffffff"
|
||||||
placeholder="请输入所属行业"></u--input>
|
placeholder="请输入所属行业"></u--input>
|
||||||
</u-form-item>
|
</u-form-item>
|
||||||
|
<u-form-item label="所属社区" prop="createDept" borderBottom labelWidth="80" ref="item1">
|
||||||
|
<custom-tree-select style="width: 100%;" :listData="deptTreeData" v-model="info.createDept"
|
||||||
|
data-label="title" data-value="value" boxStyle="border: 0; paddingLeft: 0;" search border
|
||||||
|
placeholder="请选择所属社区" />
|
||||||
|
</u-form-item>
|
||||||
<u-form-item label="企业性质" prop="jobCompanyNature" borderBottom labelWidth="80" ref="item1">
|
<u-form-item label="企业性质" prop="jobCompanyNature" borderBottom labelWidth="80" ref="item1">
|
||||||
<u--input v-model="info.jobCompanyNature" border="none" :disabled="true" disabledColor="#ffffff"
|
<u--input v-model="info.jobCompanyNature" border="none" :disabled="true" disabledColor="#ffffff"
|
||||||
placeholder="请输入企业性质"></u--input>
|
placeholder="请输入企业性质"></u--input>
|
||||||
@@ -176,7 +181,8 @@
|
|||||||
submitInfo,
|
submitInfo,
|
||||||
getWorktypesBaseList,
|
getWorktypesBaseList,
|
||||||
findTradeList,
|
findTradeList,
|
||||||
getDictionary
|
getDictionary,
|
||||||
|
getDeptAllTree
|
||||||
} from '@/api/userrecruit.js'
|
} from '@/api/userrecruit.js'
|
||||||
import PickerTree from './enterpriceCertification/pickerTree.vue'
|
import PickerTree from './enterpriceCertification/pickerTree.vue'
|
||||||
import {
|
import {
|
||||||
@@ -191,6 +197,7 @@
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
deptTreeData: [],
|
||||||
addressOptions: [],
|
addressOptions: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
latitude: 31.05, //中心点
|
latitude: 31.05, //中心点
|
||||||
@@ -242,6 +249,7 @@
|
|||||||
callTel: '', // 手机号
|
callTel: '', // 手机号
|
||||||
callNumber: '', // 座机
|
callNumber: '', // 座机
|
||||||
address: '', // 公司详细地址
|
address: '', // 公司详细地址
|
||||||
|
createDept: []
|
||||||
// userInfo: {
|
// userInfo: {
|
||||||
// name: '楼兰',
|
// name: '楼兰',
|
||||||
// sex: '',
|
// sex: '',
|
||||||
@@ -391,6 +399,12 @@
|
|||||||
message: '请输入详细地址',
|
message: '请输入详细地址',
|
||||||
trigger: ['change']
|
trigger: ['change']
|
||||||
},
|
},
|
||||||
|
createDept: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
message: '请选择所属社区',
|
||||||
|
trigger: ['change']
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -412,6 +426,7 @@
|
|||||||
} = dic.wageUnitCategoryState[0].filter(item => item.id == 3)[0]
|
} = dic.wageUnitCategoryState[0].filter(item => item.id == 3)[0]
|
||||||
this.info.wageUnitCategory = staId
|
this.info.wageUnitCategory = staId
|
||||||
this.info.wageUnitCategoryName = staLabel
|
this.info.wageUnitCategoryName = staLabel
|
||||||
|
this.getTreeDept()
|
||||||
if (this.company) {
|
if (this.company) {
|
||||||
this.backfill(this.company)
|
this.backfill(this.company)
|
||||||
}
|
}
|
||||||
@@ -426,6 +441,13 @@
|
|||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async getTreeDept() {
|
||||||
|
let resData = await getDeptAllTree()
|
||||||
|
if (resData.data.code === 200) {
|
||||||
|
this.deptTreeData = resData.data.data
|
||||||
|
console.log('dept', resData.data.data)
|
||||||
|
}
|
||||||
|
},
|
||||||
selectInput: debounce(function(val) {
|
selectInput: debounce(function(val) {
|
||||||
// querySearch(val, '3CXBZ-SKHCL-QC6PH-MLJAE-ZYCFK-6MBR5').then(res => {
|
// querySearch(val, '3CXBZ-SKHCL-QC6PH-MLJAE-ZYCFK-6MBR5').then(res => {
|
||||||
// console.log(res)
|
// console.log(res)
|
||||||
@@ -484,6 +506,7 @@
|
|||||||
this.info.jobCompanyIndustry = info.jobCompanyIndustry
|
this.info.jobCompanyIndustry = info.jobCompanyIndustry
|
||||||
this.info.jobCompanyNature = info.jobCompanyNature
|
this.info.jobCompanyNature = info.jobCompanyNature
|
||||||
this.info.jobCompanyDescription = info.jobCompanyDescription
|
this.info.jobCompanyDescription = info.jobCompanyDescription
|
||||||
|
this.info.createDept = [String(info.createDept)]
|
||||||
this.info.callName = info.callName
|
this.info.callName = info.callName
|
||||||
this.info.callTel = info.callTel
|
this.info.callTel = info.callTel
|
||||||
this.info.callNumber = info.callNumber
|
this.info.callNumber = info.callNumber
|
||||||
@@ -658,6 +681,7 @@
|
|||||||
}
|
}
|
||||||
params.lon = that.longitude
|
params.lon = that.longitude
|
||||||
params.lat = that.latitude
|
params.lat = that.latitude
|
||||||
|
params.createDept = params.createDept[0]
|
||||||
submitInfo(params).then(res => {
|
submitInfo(params).then(res => {
|
||||||
if (res.data.code == 200) {
|
if (res.data.code == 200) {
|
||||||
that.reset()
|
that.reset()
|
||||||
@@ -700,6 +724,7 @@
|
|||||||
'jobCompanyIndustry',
|
'jobCompanyIndustry',
|
||||||
'jobCompanyNature',
|
'jobCompanyNature',
|
||||||
'jobCompanyDescription',
|
'jobCompanyDescription',
|
||||||
|
"createDept",
|
||||||
'callName',
|
'callName',
|
||||||
'callTel',
|
'callTel',
|
||||||
'callNumber',
|
'callNumber',
|
||||||
|
|||||||
@@ -105,6 +105,11 @@
|
|||||||
<u--input v-model="info.jobCompanyIndustry" border="none" placeholder="请输入所属行业" :disabled="true"
|
<u--input v-model="info.jobCompanyIndustry" border="none" placeholder="请输入所属行业" :disabled="true"
|
||||||
disabledColor="#ffffff"></u--input>
|
disabledColor="#ffffff"></u--input>
|
||||||
</u-form-item>
|
</u-form-item>
|
||||||
|
<u-form-item label="所属社区" prop="createDept" borderBottom labelWidth="80" ref="item1">
|
||||||
|
<custom-tree-select style="width: 100%;" ceilStyle="border: 0;" :listData="deptTreeData"
|
||||||
|
v-model="info.createDept" data-label="title" data-value="id"
|
||||||
|
boxStyle="border: 0; paddingLeft: 0;" search border placeholder="请选择所属社区" />
|
||||||
|
</u-form-item>
|
||||||
<u-form-item label="企业性质" prop="jobCompanyNature" borderBottom labelWidth="80" ref="item1">
|
<u-form-item label="企业性质" prop="jobCompanyNature" borderBottom labelWidth="80" ref="item1">
|
||||||
<u--input v-model="info.jobCompanyNature" border="none" placeholder="请输入企业性质" :disabled="true"
|
<u--input v-model="info.jobCompanyNature" border="none" placeholder="请输入企业性质" :disabled="true"
|
||||||
disabledColor="#ffffff"></u--input>
|
disabledColor="#ffffff"></u--input>
|
||||||
@@ -177,7 +182,8 @@
|
|||||||
submitInfo,
|
submitInfo,
|
||||||
getWorktypesBaseList,
|
getWorktypesBaseList,
|
||||||
findTradeList,
|
findTradeList,
|
||||||
getDictionary
|
getDictionary,
|
||||||
|
getDeptAllTree
|
||||||
} from '@/api/userrecruit.js'
|
} from '@/api/userrecruit.js'
|
||||||
import PickerTree from './enterpriceCertification/pickerTree.vue'
|
import PickerTree from './enterpriceCertification/pickerTree.vue'
|
||||||
import {
|
import {
|
||||||
@@ -186,6 +192,7 @@
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
deptTreeData: [],
|
||||||
latitude: 31.05, //中心点
|
latitude: 31.05, //中心点
|
||||||
longitude: 104.20,
|
longitude: 104.20,
|
||||||
covers: [{ //marker标记位置
|
covers: [{ //marker标记位置
|
||||||
@@ -384,6 +391,12 @@
|
|||||||
message: '请输入详细地址',
|
message: '请输入详细地址',
|
||||||
trigger: ['change']
|
trigger: ['change']
|
||||||
},
|
},
|
||||||
|
createDept: {
|
||||||
|
type: 'array',
|
||||||
|
required: true,
|
||||||
|
message: '请选择所属社区',
|
||||||
|
trigger: ['change']
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -412,6 +425,7 @@
|
|||||||
this.info.wageUnitCategory = staId
|
this.info.wageUnitCategory = staId
|
||||||
this.info.wageUnitCategoryName = staLabel
|
this.info.wageUnitCategoryName = staLabel
|
||||||
console.log(this.area)
|
console.log(this.area)
|
||||||
|
this.getTreeDept()
|
||||||
this.getWorkTypes()
|
this.getWorkTypes()
|
||||||
this.dictionary()
|
this.dictionary()
|
||||||
if (this.company) {
|
if (this.company) {
|
||||||
@@ -419,6 +433,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
async getTreeDept() {
|
||||||
|
let resData = await getDeptAllTree()
|
||||||
|
if (resData.data.code === 200) {
|
||||||
|
this.deptTreeData = resData.data.data
|
||||||
|
}
|
||||||
|
},
|
||||||
onBlurWage(value) {
|
onBlurWage(value) {
|
||||||
const val = this.wallMaxAndMin
|
const val = this.wallMaxAndMin
|
||||||
if (!val.length) {
|
if (!val.length) {
|
||||||
@@ -468,6 +488,7 @@
|
|||||||
this.info.jobCompanyNature = info.jobCompanyNature
|
this.info.jobCompanyNature = info.jobCompanyNature
|
||||||
this.info.jobCompanyDescription = info.jobCompanyDescription
|
this.info.jobCompanyDescription = info.jobCompanyDescription
|
||||||
this.info.callName = info.callName
|
this.info.callName = info.callName
|
||||||
|
this.info.createDept = [String(info.createDept)]
|
||||||
this.info.callTel = info.callTel
|
this.info.callTel = info.callTel
|
||||||
this.info.callNumber = info.callNumber
|
this.info.callNumber = info.callNumber
|
||||||
this.info.address = info.address
|
this.info.address = info.address
|
||||||
@@ -648,6 +669,7 @@
|
|||||||
}
|
}
|
||||||
params.lon = that.longitude
|
params.lon = that.longitude
|
||||||
params.lat = that.latitude
|
params.lat = that.latitude
|
||||||
|
params.createDept = params.createDept[0]
|
||||||
submitInfo(params).then(res => {
|
submitInfo(params).then(res => {
|
||||||
if (res.data.code == 200) {
|
if (res.data.code == 200) {
|
||||||
that.reset()
|
that.reset()
|
||||||
@@ -701,6 +723,7 @@
|
|||||||
'jobCompanyNature',
|
'jobCompanyNature',
|
||||||
'jobCompanyDescription',
|
'jobCompanyDescription',
|
||||||
'callName',
|
'callName',
|
||||||
|
'createDept',
|
||||||
'callTel',
|
'callTel',
|
||||||
'callNumber',
|
'callNumber',
|
||||||
'address'
|
'address'
|
||||||
|
|||||||
187
uni_modules/custom-tree-select/changelog.md
Normal file
187
uni_modules/custom-tree-select/changelog.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
## 4.1.0(2023-10-22)
|
||||||
|
- 修复小程序端无法移除已选项问题
|
||||||
|
- 修复小程序端异步加载节点不可用问题
|
||||||
|
- 增加点击左侧箭头同样可以展开元素
|
||||||
|
## 4.0.0(2023-09-17)
|
||||||
|
- 调整 v-model 传参类型,为适应数值和字符串同时存在的树形数据
|
||||||
|
## 3.8.4(2023-09-17)
|
||||||
|
- 支持动态加载节点
|
||||||
|
## 3.8.3(2023-09-17)
|
||||||
|
- 文档更新
|
||||||
|
## 3.8.2(2023-09-17)
|
||||||
|
- 新增路径模式吗,解决重名选项无法辨认问题
|
||||||
|
## 3.8.1(2023-08-06)
|
||||||
|
- 修复加载选择器数据后,选择框没有数据问题
|
||||||
|
## 3.8.0(2023-07-29)
|
||||||
|
- 修复父子联动状态下,数据回显父级半选状态不展示问题
|
||||||
|
- selectChange 返回数据调整
|
||||||
|
- 新增 contentHeight 、disabledList 属性
|
||||||
|
- 文档更新
|
||||||
|
## 3.7.9(2023-07-11)
|
||||||
|
- 文档更新
|
||||||
|
## 3.7.8(2023-07-10)
|
||||||
|
- 修复父元素未完全选中状态显示异常问题
|
||||||
|
## 3.7.7(2023-07-07)
|
||||||
|
- 文档更新
|
||||||
|
## 3.7.6(2023-07-07)
|
||||||
|
- 重写checkbox,解决与colorui样式冲突问题
|
||||||
|
- 父级添加子级未全选状态,更直观了解选中数据情况
|
||||||
|
- 样式优化,提取文件内样式变量
|
||||||
|
- 代码体积优化
|
||||||
|
## 3.7.5(2023-05-15)
|
||||||
|
- 修复visible在数据初始化时配置有时失效问题
|
||||||
|
## 3.7.4(2023-05-11)
|
||||||
|
- 修复父元素下某个子元素设置隐藏时,父子联动状态异常问题
|
||||||
|
- 对性能以及代码体积做了进一步优化
|
||||||
|
## 3.7.3(2023-05-08)
|
||||||
|
- 修复子元素展开懒加载功能被覆盖问题
|
||||||
|
- 优化数据初始化速度
|
||||||
|
## 3.7.2(2023-05-04)
|
||||||
|
- uni.$emit 替换 bus事件
|
||||||
|
## 3.7.1(2023-04-10)
|
||||||
|
- 解决小程序无法折叠或展开问题
|
||||||
|
## 3.7.0(2023-04-09)
|
||||||
|
- 全选默认不开启,并且只在多选模式下生效
|
||||||
|
## 3.6.91(2023-04-09)
|
||||||
|
- 文档更新
|
||||||
|
## 3.6.9(2023-04-09)
|
||||||
|
- 代码整体优化
|
||||||
|
- 新增弹窗安全区配置选项
|
||||||
|
- 新增全选功能配置选项
|
||||||
|
- 新增搜索框清除按钮是否直接重置搜索结果选项
|
||||||
|
- 遗留问题:小程序端搜索后无法折叠或打开
|
||||||
|
## 3.6.8(2023-04-07)
|
||||||
|
- 优化deepClone
|
||||||
|
## 3.6.7(2023-04-03)
|
||||||
|
- 取消对微信小程序的支持
|
||||||
|
## 3.6.6(2023-04-03)
|
||||||
|
- 修复listData刷新后,不能正确渲染勾选状态问题
|
||||||
|
## 3.6.5(2023-03-30)
|
||||||
|
- 修复小程序节点展开或折叠时指示箭头不变换状态
|
||||||
|
- 修复回显数据有时不展示问题
|
||||||
|
## 3.6.3(2023-03-27)
|
||||||
|
- [紧急] 修复单选状态仍可以多选bug
|
||||||
|
## 3.6.2(2023-03-27)
|
||||||
|
- 修复偶尔出现节点无法根据绑定数据改变选中状态问题
|
||||||
|
## 3.6.1(2023-03-24)
|
||||||
|
- 修复引入问题
|
||||||
|
## 3.6.0(2023-03-24)
|
||||||
|
- 重构去除冗余代码,功能不变
|
||||||
|
## 3.5.0(2023-03-23)
|
||||||
|
- 修复没有子元素时点击报错问题
|
||||||
|
## 3.3.9(2023-03-22)
|
||||||
|
- 解决搜索时 toLowerCase 报错问题
|
||||||
|
## 3.3.8(2023-03-22)
|
||||||
|
- 懒加载优化
|
||||||
|
## 3.3.7(2023-03-20)
|
||||||
|
- 样式更新
|
||||||
|
## 3.3.6(2023-03-20)
|
||||||
|
- 修复子元素折叠或展开不触发问题
|
||||||
|
## 3.3.5(2023-03-15)
|
||||||
|
- isArray 替换 instanceof
|
||||||
|
## 3.3.4(2023-03-11)
|
||||||
|
- 修复搜索后无法展开或折叠问题
|
||||||
|
- 搜索后直接展开搜索项
|
||||||
|
## 3.3.3(2023-03-06)
|
||||||
|
- 优化展示效果
|
||||||
|
- 添加引导线选项
|
||||||
|
## 3.3.2(2023-02-22)
|
||||||
|
- 修复搜索内容包含隐藏数据的问题
|
||||||
|
- 优化搜索触发方式
|
||||||
|
## 3.3.1(2023-02-16)
|
||||||
|
- 修复异常
|
||||||
|
## 3.2.9(2023-02-16)
|
||||||
|
- 修复编译到小程序报错问题
|
||||||
|
## 3.2.8(2023-02-16)
|
||||||
|
- 修复多个组件key重复问题
|
||||||
|
## 3.2.7(2023-02-16)
|
||||||
|
- 全新底层架构设计
|
||||||
|
- 修改默认配色,更显成熟稳重
|
||||||
|
## 3.2.6(2023-02-15)
|
||||||
|
- 修改文档
|
||||||
|
## 3.2.5(2023-02-15)
|
||||||
|
- 文档修改
|
||||||
|
## 3.2.3(2023-02-15)
|
||||||
|
- 修复小程序组件无法折叠问题
|
||||||
|
## 3.2.2(2023-02-10)
|
||||||
|
- 文档更新
|
||||||
|
## 3.2.1(2023-02-09)
|
||||||
|
- 增加节点展开和关闭全局配置
|
||||||
|
- 修复搜索后数据无法展开和关闭问题
|
||||||
|
- 修复搜索后不能自动滚动到顶部问题
|
||||||
|
## 3.2.0(2023-01-16)
|
||||||
|
- 修复事件冲突
|
||||||
|
## 3.1.5(2023-01-13)
|
||||||
|
- 样式调整
|
||||||
|
## 3.1.4(2023-01-13)
|
||||||
|
- 样式修复
|
||||||
|
## 3.1.3(2023-01-13)
|
||||||
|
- 文档更新
|
||||||
|
## 3.1.2(2023-01-13)
|
||||||
|
- 修复一些已知问题
|
||||||
|
## 3.1.1(2023-01-12)
|
||||||
|
- 解决微信小程序bus事件冲突问题
|
||||||
|
## 3.1.0(2023-01-11)
|
||||||
|
- 支持微信小程序
|
||||||
|
## 3.0.3(2023-01-10)
|
||||||
|
- 更新文档
|
||||||
|
## 3.0.2(2023-01-09)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 3.0.1(2023-01-09)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 3.0.0(2023-01-09)
|
||||||
|
- 全面支持懒加载,大量数据也能快速打开
|
||||||
|
## 2.0.01(2022-12-29)
|
||||||
|
- 文档修改
|
||||||
|
## 2.0.0(2022-12-28)
|
||||||
|
- 添加搜索功能
|
||||||
|
- 完善文档
|
||||||
|
## 1.9.2(2022-12-14)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.9.1(2022-12-14)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.9.0(2022-12-14)
|
||||||
|
- 增加选项禁用、隐藏功能
|
||||||
|
- 文档添加示例
|
||||||
|
## 1.8.4(2022-12-13)
|
||||||
|
- 文档修复
|
||||||
|
## 1.8.3(2022-12-09)
|
||||||
|
- 样式调整
|
||||||
|
## 1.8.2(2022-12-07)
|
||||||
|
- 修复弹窗不同设备显示高度问题
|
||||||
|
## 1.8.1(2022-12-06)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.8.0(2022-12-05)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
- 图标替换图片
|
||||||
|
## 1.7.0(2022-12-05)
|
||||||
|
- 增加 disabled 属性,适用于查看页面
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.6.0(2022-12-05)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.5.0(2022-12-05)
|
||||||
|
- 根据绑定数据类型自动判断返回值类型
|
||||||
|
- 完善文档
|
||||||
|
## 1.4.0(2022-12-02)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.3.1(2022-12-02)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.3.0(2022-12-02)
|
||||||
|
- 修复了一些已知问题
|
||||||
|
## 1.2.01(2022-12-02)
|
||||||
|
- 文档更新
|
||||||
|
## 1.2.0(2022-12-02)
|
||||||
|
- 添加一键清除功能
|
||||||
|
- 添加多选、单选功能
|
||||||
|
## 1.1.0(2022-12-02)
|
||||||
|
- 支持数据回显、已选数据移除
|
||||||
|
- 对标 uni-easyinput 默认样式
|
||||||
|
- 支持更多属性和事件
|
||||||
|
## 1.0.3(2022-12-01)
|
||||||
|
- 修改信息
|
||||||
|
## 1.0.2(2022-12-01)
|
||||||
|
- 更新文档
|
||||||
|
## 1.0.1(2022-12-01)
|
||||||
|
- 更新预览截图
|
||||||
|
## 1.0.0(2022-12-01)
|
||||||
|
- 插件更新
|
||||||
@@ -0,0 +1,983 @@
|
|||||||
|
<template>
|
||||||
|
<view class="custom-tree-select-content">
|
||||||
|
<view
|
||||||
|
:class="['select-list', { disabled }, { active: selectList.length }]"
|
||||||
|
:style="boxStyle"
|
||||||
|
@click.stop="open"
|
||||||
|
>
|
||||||
|
<view class="left">
|
||||||
|
<view v-if="selectList.length" class="select-items">
|
||||||
|
<view
|
||||||
|
class="select-item"
|
||||||
|
v-for="item in selectedListBaseinfo"
|
||||||
|
:key="item[dataValue]"
|
||||||
|
>
|
||||||
|
<view class="name">
|
||||||
|
<text>{{ pathMode ? item.path : item[dataLabel] }}</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="!disabled && !item.disabled"
|
||||||
|
class="close"
|
||||||
|
@click.stop="removeSelectedItem(item)"
|
||||||
|
>
|
||||||
|
<uni-icons type="closeempty" size="16" color="#999"></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else style="color: #6a6a6a" class="no-data">
|
||||||
|
<text style="#C0C4CB">{{ placeholder }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="right">
|
||||||
|
<uni-icons
|
||||||
|
v-if="!selectList.length || !clearable"
|
||||||
|
type="bottom"
|
||||||
|
size="14"
|
||||||
|
color="#999"
|
||||||
|
></uni-icons>
|
||||||
|
<uni-icons
|
||||||
|
v-if="selectList.length && clearable"
|
||||||
|
type="clear"
|
||||||
|
size="24"
|
||||||
|
color="#c0c4cc"
|
||||||
|
@click.native.stop="clear"
|
||||||
|
></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<uni-popup
|
||||||
|
v-if="showPopup"
|
||||||
|
ref="popup"
|
||||||
|
:animation="animation"
|
||||||
|
:is-mask-click="isMaskClick"
|
||||||
|
:mask-background-color="maskBackgroundColor"
|
||||||
|
:background-color="backgroundColor"
|
||||||
|
:safe-area="safeArea"
|
||||||
|
type="bottom"
|
||||||
|
@change="change"
|
||||||
|
@maskClick="maskClick"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
class="popup-content"
|
||||||
|
:style="{ height: contentHeight || defaultContentHeight }"
|
||||||
|
>
|
||||||
|
<view class="title">
|
||||||
|
<view
|
||||||
|
v-if="mutiple && canSelectAll"
|
||||||
|
class="left"
|
||||||
|
@click.stop="handleSelectAll"
|
||||||
|
>
|
||||||
|
<text>{{ isSelectedAll ? '取消全选' : '全选' }}</text>
|
||||||
|
</view>
|
||||||
|
<view class="center">
|
||||||
|
<text>{{ placeholder }}</text>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="right"
|
||||||
|
:style="{ color: confirmTextColor }"
|
||||||
|
@click.stop="close"
|
||||||
|
>
|
||||||
|
<text>{{ confirmText }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-if="search" class="search-box">
|
||||||
|
<uni-easyinput
|
||||||
|
:maxlength="-1"
|
||||||
|
prefixIcon="search"
|
||||||
|
placeholder="搜索"
|
||||||
|
v-model="searchStr"
|
||||||
|
confirm-type="search"
|
||||||
|
@confirm="handleSearch(false)"
|
||||||
|
@clear="handleSearch(true)"
|
||||||
|
>
|
||||||
|
</uni-easyinput>
|
||||||
|
<button
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
class="search-btn"
|
||||||
|
@click.stop="handleSearch(false)"
|
||||||
|
>
|
||||||
|
搜索
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
<view v-if="treeData.length" class="select-content">
|
||||||
|
<scroll-view
|
||||||
|
class="scroll-view-box"
|
||||||
|
:scroll-top="scrollTop"
|
||||||
|
scroll-y="true"
|
||||||
|
@touchmove.stop
|
||||||
|
>
|
||||||
|
<view v-if="!filterTreeData.length" class="no-data center">
|
||||||
|
<text>暂无数据</text>
|
||||||
|
</view>
|
||||||
|
<data-select-item
|
||||||
|
v-for="item in filterTreeData"
|
||||||
|
:key="item[dataValue]"
|
||||||
|
:node="item"
|
||||||
|
:dataLabel="dataLabel"
|
||||||
|
:dataValue="dataValue"
|
||||||
|
:dataChildren="dataChildren"
|
||||||
|
:choseParent="choseParent"
|
||||||
|
:border="border"
|
||||||
|
:linkage="linkage"
|
||||||
|
:load="load"
|
||||||
|
:lazyLoadChildren="lazyLoadChildren"
|
||||||
|
></data-select-item>
|
||||||
|
<view class="sentry" />
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
<view v-else class="no-data center">
|
||||||
|
<text>暂无数据</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const partCheckedSet = new Set()
|
||||||
|
import { paging } from './utils'
|
||||||
|
import dataSelectItem from './data-select-item.vue'
|
||||||
|
export default {
|
||||||
|
name: 'custom-tree-select',
|
||||||
|
components: {
|
||||||
|
dataSelectItem
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
prop: 'value',
|
||||||
|
event: 'input'
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
boxStyle: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
canSelectAll: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
safeArea: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
clearResetSearch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
animation: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
'is-mask-click': {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
'mask-background-color': {
|
||||||
|
type: String,
|
||||||
|
default: 'rgba(0,0,0,0.4)'
|
||||||
|
},
|
||||||
|
'background-color': {
|
||||||
|
type: String,
|
||||||
|
default: 'none'
|
||||||
|
},
|
||||||
|
'safe-area': {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
choseParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: '请选择'
|
||||||
|
},
|
||||||
|
confirmText: {
|
||||||
|
type: String,
|
||||||
|
default: '完成'
|
||||||
|
},
|
||||||
|
confirmTextColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#007aff'
|
||||||
|
},
|
||||||
|
contentHeight: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
disabledList: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
listData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
dataLabel: {
|
||||||
|
type: String,
|
||||||
|
default: 'name'
|
||||||
|
},
|
||||||
|
dataValue: {
|
||||||
|
type: String,
|
||||||
|
default: 'id'
|
||||||
|
},
|
||||||
|
dataChildren: {
|
||||||
|
type: String,
|
||||||
|
default: 'children'
|
||||||
|
},
|
||||||
|
linkage: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
mutiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
showChildren: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
pathMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
pathHyphen: {
|
||||||
|
type: String,
|
||||||
|
default: '/'
|
||||||
|
},
|
||||||
|
load: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
},
|
||||||
|
lazyLoadChildren: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
defaultContentHeight: '500px',
|
||||||
|
treeData: [],
|
||||||
|
filterTreeData: [],
|
||||||
|
clearTimerList: [],
|
||||||
|
selectedListBaseinfo: [],
|
||||||
|
showPopup: false,
|
||||||
|
clickOpenTimer: null,
|
||||||
|
isSelectedAll: false,
|
||||||
|
scrollTop: 0,
|
||||||
|
searchStr: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
selectList() {
|
||||||
|
return this.value || []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
listData: {
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
partCheckedSet.clear()
|
||||||
|
this.treeData = this.initData(newVal)
|
||||||
|
|
||||||
|
if (this.value) {
|
||||||
|
this.changeStatus(this.treeData, this.value, true)
|
||||||
|
this.filterTreeData.length &&
|
||||||
|
this.changeStatus(this.filterTreeData, this.value)
|
||||||
|
}
|
||||||
|
if (this.showPopup) {
|
||||||
|
this.resetClearTimerList()
|
||||||
|
this.renderTree(this.treeData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.changeStatus(this.treeData, this.value, true)
|
||||||
|
this.filterTreeData.length &&
|
||||||
|
this.changeStatus(this.filterTreeData, this.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getContentHeight(uni.getSystemInfoSync())
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 搜索完成返回顶部
|
||||||
|
goTop() {
|
||||||
|
this.scrollTop = 10
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.scrollTop = 0
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获取对应数据
|
||||||
|
getReflectNode(node, arr) {
|
||||||
|
const array = [...arr]
|
||||||
|
while (array.length) {
|
||||||
|
const item = array.shift()
|
||||||
|
if (item[this.dataValue] === node[this.dataValue]) {
|
||||||
|
return item
|
||||||
|
}
|
||||||
|
if (item[this.dataChildren]?.length) {
|
||||||
|
array.push(...item[this.dataChildren])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
getContentHeight({ screenHeight }) {
|
||||||
|
this.defaultContentHeight = `${Math.floor(screenHeight * 0.7)}px`
|
||||||
|
},
|
||||||
|
// 处理搜索
|
||||||
|
handleSearch(isClear = false) {
|
||||||
|
this.resetClearTimerList()
|
||||||
|
if (isClear) {
|
||||||
|
// 点击清空按钮并且设置清空按钮会重置搜索
|
||||||
|
if (this.clearResetSearch) {
|
||||||
|
this.renderTree(this.treeData)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.renderTree(this.searchValue(this.searchStr, this.treeData))
|
||||||
|
}
|
||||||
|
this.goTop()
|
||||||
|
uni.hideKeyboard()
|
||||||
|
},
|
||||||
|
// 具体搜索方法
|
||||||
|
searchValue(str, arr) {
|
||||||
|
const res = []
|
||||||
|
arr.forEach((item) => {
|
||||||
|
if (item.visible) {
|
||||||
|
if (
|
||||||
|
item[this.dataLabel]
|
||||||
|
.toString()
|
||||||
|
.toLowerCase()
|
||||||
|
.indexOf(str.toLowerCase()) > -1
|
||||||
|
) {
|
||||||
|
res.push(item)
|
||||||
|
} else {
|
||||||
|
if (item[this.dataChildren]?.length) {
|
||||||
|
const data = this.searchValue(str, item[this.dataChildren])
|
||||||
|
if (data?.length) {
|
||||||
|
if (
|
||||||
|
str &&
|
||||||
|
!item.showChildren &&
|
||||||
|
item[this.dataChildren]?.length
|
||||||
|
) {
|
||||||
|
item.showChildren = true
|
||||||
|
}
|
||||||
|
res.push({
|
||||||
|
...item,
|
||||||
|
[this.dataChildren]: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// 懒加载
|
||||||
|
renderTree(arr) {
|
||||||
|
const pagingArr = paging(arr)
|
||||||
|
this.filterTreeData.splice(
|
||||||
|
0,
|
||||||
|
this.filterTreeData.length,
|
||||||
|
...(pagingArr?.[0] || [])
|
||||||
|
)
|
||||||
|
this.lazyRenderList(pagingArr, 1)
|
||||||
|
},
|
||||||
|
// 懒加载具体逻辑
|
||||||
|
lazyRenderList(arr, startIndex) {
|
||||||
|
for (let i = startIndex; i < arr.length; i++) {
|
||||||
|
let timer = null
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
this.filterTreeData.push(...arr[i])
|
||||||
|
}, i * 500)
|
||||||
|
this.clearTimerList.push(() => clearTimeout(timer))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 中断懒加载
|
||||||
|
resetClearTimerList() {
|
||||||
|
const list = [...this.clearTimerList]
|
||||||
|
this.clearTimerList = []
|
||||||
|
list.forEach((fn) => fn())
|
||||||
|
},
|
||||||
|
// 打开弹窗
|
||||||
|
open() {
|
||||||
|
// disaled模式下禁止打开弹窗
|
||||||
|
if (this.disabled) return
|
||||||
|
this.showPopup = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.popup.open()
|
||||||
|
this.renderTree(this.treeData)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 关闭弹窗
|
||||||
|
close() {
|
||||||
|
this.$refs.popup.close()
|
||||||
|
},
|
||||||
|
// 弹窗状态变化 包括点击回显框和遮罩
|
||||||
|
change(data) {
|
||||||
|
if (data.show) {
|
||||||
|
uni.$on('custom-tree-select-node-click', this.handleNodeClick)
|
||||||
|
uni.$on('custom-tree-select-name-click', this.handleHideChildren)
|
||||||
|
uni.$on('custom-tree-select-load', this.handleLoadNode)
|
||||||
|
} else {
|
||||||
|
uni.$off('custom-tree-select-node-click', this.handleNodeClick)
|
||||||
|
uni.$off('custom-tree-select-name-click', this.handleHideChildren)
|
||||||
|
uni.$off('custom-tree-select-load', this.handleLoadNode)
|
||||||
|
this.resetClearTimerList()
|
||||||
|
this.searchStr = ''
|
||||||
|
if (this.animation) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showPopup = false
|
||||||
|
}, 200)
|
||||||
|
} else {
|
||||||
|
this.showPopup = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$emit('change', data)
|
||||||
|
},
|
||||||
|
// 点击遮罩
|
||||||
|
maskClick() {
|
||||||
|
this.$emit('maskClick')
|
||||||
|
},
|
||||||
|
// 初始化数据
|
||||||
|
initData(arr, parentVisible = undefined, pathArr = []) {
|
||||||
|
if (!Array.isArray(arr)) return []
|
||||||
|
const res = []
|
||||||
|
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
const curPathArr = [...pathArr, arr[i][this.dataLabel]]
|
||||||
|
const obj = {
|
||||||
|
...arr[i],
|
||||||
|
[this.dataLabel]: arr[i][this.dataLabel],
|
||||||
|
[this.dataValue]: arr[i][this.dataValue],
|
||||||
|
path: curPathArr.join(this.pathHyphen)
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.checked = this.selectList.includes(arr[i][this.dataValue])
|
||||||
|
|
||||||
|
obj.disabled = false
|
||||||
|
if (
|
||||||
|
Boolean(arr[i].disabled) ||
|
||||||
|
this.disabledList.includes(obj[this.dataValue])
|
||||||
|
) {
|
||||||
|
obj.disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
//半选
|
||||||
|
obj.partChecked = Boolean(
|
||||||
|
arr[i].partChecked === undefined ? false : arr[i].partChecked
|
||||||
|
)
|
||||||
|
obj.partChecked && obj.partCheckedSet.add(obj[this.dataValue])
|
||||||
|
!obj.partChecked && (this.isSelectedAll = false)
|
||||||
|
|
||||||
|
const parentVisibleState =
|
||||||
|
parentVisible === undefined ? true : parentVisible
|
||||||
|
const curVisibleState =
|
||||||
|
arr[i].visible === undefined ? true : Boolean(arr[i].visible)
|
||||||
|
if (parentVisibleState === curVisibleState) {
|
||||||
|
obj.visible = parentVisibleState
|
||||||
|
} else if (!parentVisibleState || !curVisibleState) {
|
||||||
|
obj.visible = false
|
||||||
|
} else {
|
||||||
|
obj.visible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
obj.showChildren =
|
||||||
|
'showChildren' in arr[i] && arr[i].showChildren != undefined
|
||||||
|
? arr[i].showChildren
|
||||||
|
: this.showChildren
|
||||||
|
|
||||||
|
if (arr[i][this.dataChildren]?.length) {
|
||||||
|
const childrenVal = this.initData(
|
||||||
|
arr[i][this.dataChildren],
|
||||||
|
obj.visible,
|
||||||
|
curPathArr
|
||||||
|
)
|
||||||
|
obj[this.dataChildren] = childrenVal
|
||||||
|
if (
|
||||||
|
!obj.checked &&
|
||||||
|
childrenVal.some((item) => item.checked || item.partChecked)
|
||||||
|
) {
|
||||||
|
obj.partChecked = true
|
||||||
|
partCheckedSet.add(obj[this.dataValue])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// 获取某个节点后面所有元素
|
||||||
|
getChildren(node) {
|
||||||
|
if (!node[this.dataChildren]?.length) return []
|
||||||
|
const res = node[this.dataChildren].reduce((pre, val) => {
|
||||||
|
if (val.visible) {
|
||||||
|
return [...pre, val]
|
||||||
|
}
|
||||||
|
return pre
|
||||||
|
}, [])
|
||||||
|
for (let i = 0; i < node[this.dataChildren].length; i++) {
|
||||||
|
res.push(...this.getChildren(node[this.dataChildren][i]))
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// 获取某个节点所有祖先元素
|
||||||
|
getParentNode(target, arr) {
|
||||||
|
let res = []
|
||||||
|
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
if (arr[i][this.dataValue] === target[this.dataValue]) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arr[i][this.dataChildren]?.length) {
|
||||||
|
const childRes = this.getParentNode(target, arr[i][this.dataChildren])
|
||||||
|
if (typeof childRes === 'boolean' && childRes) {
|
||||||
|
res = [arr[i]]
|
||||||
|
} else if (Array.isArray(childRes) && childRes.length) {
|
||||||
|
res = [...childRes, arr[i]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
// 点击checkbox
|
||||||
|
handleNodeClick(data, status) {
|
||||||
|
const node = this.getReflectNode(data, this.treeData)
|
||||||
|
node.checked = typeof status === 'boolean' ? status : !node.checked
|
||||||
|
node.partChecked = false
|
||||||
|
partCheckedSet.delete(node[this.dataValue])
|
||||||
|
// 如果是单选不考虑其他情况
|
||||||
|
if (!this.mutiple) {
|
||||||
|
let emitData = []
|
||||||
|
if (node.checked) {
|
||||||
|
emitData = [node[this.dataValue]]
|
||||||
|
}
|
||||||
|
this.$emit('input', emitData)
|
||||||
|
} else {
|
||||||
|
// 多选情况
|
||||||
|
if (!this.linkage) {
|
||||||
|
// 不需要联动
|
||||||
|
let emitData = null
|
||||||
|
if (node.checked) {
|
||||||
|
emitData = Array.from(
|
||||||
|
new Set([...this.selectList, node[this.dataValue]])
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
emitData = this.selectList.filter(
|
||||||
|
(id) => id !== node[this.dataValue]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.$emit('input', emitData)
|
||||||
|
} else {
|
||||||
|
// 需要联动
|
||||||
|
let emitData = [...this.selectList]
|
||||||
|
const parentNodes = this.getParentNode(node, this.treeData)
|
||||||
|
const childrenVal = this.getChildren(node).filter(
|
||||||
|
(item) => !item.disabled
|
||||||
|
)
|
||||||
|
if (node.checked) {
|
||||||
|
// 选中
|
||||||
|
emitData = Array.from(new Set([...emitData, node[this.dataValue]]))
|
||||||
|
if (childrenVal.length) {
|
||||||
|
emitData = Array.from(
|
||||||
|
new Set([
|
||||||
|
...emitData,
|
||||||
|
...childrenVal.map((item) => item[this.dataValue])
|
||||||
|
])
|
||||||
|
)
|
||||||
|
// 孩子节点全部选中并且清除半选状态
|
||||||
|
childrenVal.forEach((childNode) => {
|
||||||
|
childNode.partChecked = false
|
||||||
|
partCheckedSet.delete(childNode[this.dataValue])
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (parentNodes.length) {
|
||||||
|
let flag = false
|
||||||
|
// 有父元素 如果父元素下所有子元素全部选中,选中父元素
|
||||||
|
while (parentNodes.length) {
|
||||||
|
const item = parentNodes.shift()
|
||||||
|
if (!item.disabled) {
|
||||||
|
if (flag) {
|
||||||
|
// 前一个没选中并且为半选那么之后的全为半选
|
||||||
|
item.partChecked = true
|
||||||
|
partCheckedSet.add(item[this.dataValue])
|
||||||
|
} else {
|
||||||
|
const allChecked = item[this.dataChildren]
|
||||||
|
.filter((node) => node.visible && !node.disabled)
|
||||||
|
.every((node) => node.checked)
|
||||||
|
if (allChecked) {
|
||||||
|
item.checked = true
|
||||||
|
item.partChecked = false
|
||||||
|
partCheckedSet.delete(item[this.dataValue])
|
||||||
|
emitData = Array.from(
|
||||||
|
new Set([...emitData, item[this.dataValue]])
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
item.partChecked = true
|
||||||
|
partCheckedSet.add(item[this.dataValue])
|
||||||
|
flag = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 取消选中
|
||||||
|
emitData = emitData.filter((id) => id !== node[this.dataValue])
|
||||||
|
if (childrenVal.length) {
|
||||||
|
// 取消选中全部子节点
|
||||||
|
childrenVal.forEach((childNode) => {
|
||||||
|
emitData = emitData.filter(
|
||||||
|
(id) => id !== childNode[this.dataValue]
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (parentNodes.length) {
|
||||||
|
parentNodes.forEach((parentNode) => {
|
||||||
|
if (emitData.includes(parentNode[this.dataValue])) {
|
||||||
|
parentNode.checked = false
|
||||||
|
}
|
||||||
|
emitData = emitData.filter(
|
||||||
|
(id) => id !== parentNode[this.dataValue]
|
||||||
|
)
|
||||||
|
const hasChecked = parentNode[this.dataChildren]
|
||||||
|
.filter((node) => node.visible && !node.disabled)
|
||||||
|
.some((node) => node.checked || node.partChecked)
|
||||||
|
|
||||||
|
parentNode.partChecked = hasChecked
|
||||||
|
if (hasChecked) {
|
||||||
|
partCheckedSet.add(parentNode[this.dataValue])
|
||||||
|
} else {
|
||||||
|
partCheckedSet.delete(parentNode[this.dataValue])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.$emit('input', emitData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 点击名称折叠或展开
|
||||||
|
handleHideChildren(node) {
|
||||||
|
const status = !node.showChildren
|
||||||
|
this.getReflectNode(node, this.treeData).showChildren = status
|
||||||
|
this.getReflectNode(node, this.filterTreeData).showChildren = status
|
||||||
|
},
|
||||||
|
// 根据 dataValue 找节点
|
||||||
|
changeStatus(list, ids, needEmit = false) {
|
||||||
|
const arr = [...list]
|
||||||
|
let flag = true
|
||||||
|
needEmit && (this.selectedListBaseinfo = [])
|
||||||
|
|
||||||
|
while (arr.length) {
|
||||||
|
const item = arr.shift()
|
||||||
|
|
||||||
|
if (ids.includes(item[this.dataValue])) {
|
||||||
|
this.$set(item, 'checked', true)
|
||||||
|
needEmit && this.selectedListBaseinfo.push(item)
|
||||||
|
// 数据被选中清除半选状态
|
||||||
|
item.partChecked = false
|
||||||
|
partCheckedSet.delete(item[this.dataValue])
|
||||||
|
} else {
|
||||||
|
this.$set(item, 'checked', false)
|
||||||
|
if (item.visible && !item.disabled) {
|
||||||
|
flag = false
|
||||||
|
}
|
||||||
|
if (partCheckedSet.has(item[this.dataValue])) {
|
||||||
|
this.$set(item, 'partChecked', true)
|
||||||
|
} else {
|
||||||
|
this.$set(item, 'partChecked', false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item[this.dataChildren]?.length) {
|
||||||
|
arr.push(...item[this.dataChildren])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isSelectedAll = flag
|
||||||
|
needEmit && this.$emit('selectChange', [...this.selectedListBaseinfo])
|
||||||
|
},
|
||||||
|
// 移除选项
|
||||||
|
removeSelectedItem(node) {
|
||||||
|
this.isSelectedAll = false
|
||||||
|
if (this.linkage) {
|
||||||
|
this.handleNodeClick(node, false)
|
||||||
|
this.$emit('removeSelect', node)
|
||||||
|
} else {
|
||||||
|
const emitData = this.selectList.filter(
|
||||||
|
(item) => item !== node[this.dataValue]
|
||||||
|
)
|
||||||
|
this.$emit('removeSelect', node)
|
||||||
|
this.$emit('input', emitData)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 全部选中
|
||||||
|
handleSelectAll() {
|
||||||
|
this.isSelectedAll = !this.isSelectedAll
|
||||||
|
if (this.isSelectedAll) {
|
||||||
|
if (!this.mutiple) {
|
||||||
|
uni.showToast({
|
||||||
|
title: '单选模式下不能全选',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 1000
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let emitData = []
|
||||||
|
this.treeData.forEach((item) => {
|
||||||
|
if (item.visible || (item.disabled && item.checked)) {
|
||||||
|
emitData = Array.from(new Set([...emitData, item[this.dataValue]]))
|
||||||
|
if (item[this.dataChildren]?.length) {
|
||||||
|
emitData = Array.from(
|
||||||
|
new Set([
|
||||||
|
...emitData,
|
||||||
|
...this.getChildren(item)
|
||||||
|
.filter(
|
||||||
|
(item) =>
|
||||||
|
!item.disabled || (item.disabled && item.checked)
|
||||||
|
)
|
||||||
|
.map((item) => item[this.dataValue])
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.$emit('input', emitData)
|
||||||
|
} else {
|
||||||
|
this.clear()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 清空选项
|
||||||
|
clear() {
|
||||||
|
if (this.disabled) return
|
||||||
|
const emitData = []
|
||||||
|
partCheckedSet.clear()
|
||||||
|
this.selectedListBaseinfo.forEach((node) => {
|
||||||
|
if (node.visible && node.checked && node.disabled) {
|
||||||
|
emitData.push(node[this.dataValue])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
this.$emit('input', emitData)
|
||||||
|
},
|
||||||
|
// 异步加载节点
|
||||||
|
handleLoadNode({ source, target }) {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
const node = this.getReflectNode(source, this.treeData)
|
||||||
|
this.$set(
|
||||||
|
node,
|
||||||
|
this.dataChildren,
|
||||||
|
this.initData(
|
||||||
|
target,
|
||||||
|
source.visible,
|
||||||
|
source.path.split(this.pathHyphen)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.handleHideChildren(node)
|
||||||
|
})
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
this.$set(
|
||||||
|
source,
|
||||||
|
this.dataChildren,
|
||||||
|
this.initData(
|
||||||
|
target,
|
||||||
|
source.visible,
|
||||||
|
source.path.split(this.pathHyphen)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
this.handleHideChildren(source)
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$primary-color: #007aff;
|
||||||
|
$col-sm: 4px;
|
||||||
|
$col-base: 8px;
|
||||||
|
$col-lg: 12px;
|
||||||
|
$row-sm: 5px;
|
||||||
|
$row-base: 10px;
|
||||||
|
$row-lg: 15px;
|
||||||
|
$radius-sm: 3px;
|
||||||
|
$radius-base: 6px;
|
||||||
|
|
||||||
|
.custom-tree-select-content {
|
||||||
|
.select-list {
|
||||||
|
padding-left: $row-base;
|
||||||
|
min-height: 35px;
|
||||||
|
border: 1px solid #e5e5e5;
|
||||||
|
border-radius: $radius-sm;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
padding: calc(#{$col-sm} / 2) 0 calc(#{$col-sm} / 2) $row-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.select-items {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-item {
|
||||||
|
margin: $col-sm $row-base $col-sm 0;
|
||||||
|
padding: $col-sm $row-sm;
|
||||||
|
max-width: auto;
|
||||||
|
height: auto;
|
||||||
|
background-color: #eaeaea;
|
||||||
|
border-radius: $radius-sm;
|
||||||
|
color: #333;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: 1;
|
||||||
|
padding-right: $row-base;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
margin-right: $row-sm;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
.select-item {
|
||||||
|
.name {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.popup-content {
|
||||||
|
flex: 1;
|
||||||
|
background-color: #fff;
|
||||||
|
border-top-left-radius: 20px;
|
||||||
|
border-top-right-radius: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: $col-base 3rem;
|
||||||
|
border-bottom: 1px solid $uni-border-color;
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.left {
|
||||||
|
position: absolute;
|
||||||
|
left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
margin: $col-base $row-base 0;
|
||||||
|
background-color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.search-btn {
|
||||||
|
margin-left: $row-base;
|
||||||
|
height: 35px;
|
||||||
|
line-height: 35px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.select-content {
|
||||||
|
margin: $col-base $row-base;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroll-view-box {
|
||||||
|
touch-action: none;
|
||||||
|
flex: 1;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sentry {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data {
|
||||||
|
width: auto;
|
||||||
|
color: #C0C4CB !important;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data.center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,363 @@
|
|||||||
|
<template>
|
||||||
|
<view
|
||||||
|
class="custom-tree-select-content"
|
||||||
|
:class="{
|
||||||
|
border:
|
||||||
|
border &&
|
||||||
|
node[dataChildren] &&
|
||||||
|
node[dataChildren].length &&
|
||||||
|
node.showChildren
|
||||||
|
}"
|
||||||
|
:style="{ marginLeft: `${level ? 14 : 0}px` }"
|
||||||
|
>
|
||||||
|
<view v-if="node.visible" class="custom-tree-select-item">
|
||||||
|
<view class="item-content">
|
||||||
|
<view class="left">
|
||||||
|
<view
|
||||||
|
v-if="node[dataChildren] && node[dataChildren].length"
|
||||||
|
:class="['right-icon', { active: node.showChildren }]"
|
||||||
|
@click.stop="nameClick(node)"
|
||||||
|
>
|
||||||
|
<uni-icons type="forward" size="14" color="#333"></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view v-else class="smallcircle-filled">
|
||||||
|
<uni-icons
|
||||||
|
class="smallcircle-filled-icon"
|
||||||
|
type="smallcircle-filled"
|
||||||
|
size="10"
|
||||||
|
color="#333"
|
||||||
|
></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="loadingArr.includes(node[dataValue])"
|
||||||
|
class="loading-icon-box"
|
||||||
|
>
|
||||||
|
<uni-icons
|
||||||
|
class="loading-icon"
|
||||||
|
type="spinner-cycle"
|
||||||
|
size="14"
|
||||||
|
color="#333"
|
||||||
|
></uni-icons>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
class="name"
|
||||||
|
:style="node.disabled ? 'color: #999' : ''"
|
||||||
|
@click.stop="nameClick(node)"
|
||||||
|
>
|
||||||
|
<text>{{ node[dataLabel] }}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="
|
||||||
|
choseParent ||
|
||||||
|
(!choseParent && !node[dataChildren]) ||
|
||||||
|
(!choseParent && node[dataChildren] && !node[dataChildren].length)
|
||||||
|
"
|
||||||
|
:class="['check-box', { disabled: node.disabled }]"
|
||||||
|
@click.stop="nodeClick(node)"
|
||||||
|
>
|
||||||
|
<view
|
||||||
|
v-if="!node.checked && node.partChecked && linkage"
|
||||||
|
class="part-checked"
|
||||||
|
></view>
|
||||||
|
<uni-icons
|
||||||
|
v-if="node.checked"
|
||||||
|
type="checkmarkempty"
|
||||||
|
size="18"
|
||||||
|
:color="node.disabled ? '#333' : '#007aff'"
|
||||||
|
></uni-icons>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view
|
||||||
|
v-if="
|
||||||
|
node.showChildren && node[dataChildren] && node[dataChildren].length
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<data-select-item
|
||||||
|
v-for="item in listData"
|
||||||
|
:key="item[dataValue]"
|
||||||
|
:node="item"
|
||||||
|
:dataLabel="dataLabel"
|
||||||
|
:dataValue="dataValue"
|
||||||
|
:dataChildren="dataChildren"
|
||||||
|
:choseParent="choseParent"
|
||||||
|
:border="border"
|
||||||
|
:linkage="linkage"
|
||||||
|
:level="level + 1"
|
||||||
|
:load="load"
|
||||||
|
:lazyLoadChildren="lazyLoadChildren"
|
||||||
|
></data-select-item>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import dataSelectItem from './data-select-item.vue'
|
||||||
|
import { paging } from './utils'
|
||||||
|
export default {
|
||||||
|
name: 'data-select-item',
|
||||||
|
components: {
|
||||||
|
'data-select-item': dataSelectItem
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
node: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
},
|
||||||
|
choseParent: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
dataLabel: {
|
||||||
|
type: String,
|
||||||
|
default: 'name'
|
||||||
|
},
|
||||||
|
dataValue: {
|
||||||
|
type: String,
|
||||||
|
default: 'value'
|
||||||
|
},
|
||||||
|
dataChildren: {
|
||||||
|
type: String,
|
||||||
|
default: 'children'
|
||||||
|
},
|
||||||
|
border: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
linkage: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
level: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
load: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
},
|
||||||
|
lazyLoadChildren: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
listData: [],
|
||||||
|
clearTimerList: [],
|
||||||
|
loadingArr: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
watchData() {
|
||||||
|
const { node, dataChildren } = this
|
||||||
|
|
||||||
|
return {
|
||||||
|
node,
|
||||||
|
dataChildren
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
watchData: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
const { node, dataChildren } = newVal
|
||||||
|
if (
|
||||||
|
node.showChildren &&
|
||||||
|
node[dataChildren] &&
|
||||||
|
node[dataChildren].length
|
||||||
|
) {
|
||||||
|
this.resetClearTimerList()
|
||||||
|
this.renderTree(node[dataChildren])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
// 懒加载
|
||||||
|
renderTree(arr) {
|
||||||
|
const pagingArr = paging(arr)
|
||||||
|
this.listData.splice(0, this.listData.length, ...(pagingArr?.[0] || []))
|
||||||
|
this.lazyRenderList(pagingArr, 1)
|
||||||
|
},
|
||||||
|
// 懒加载具体逻辑
|
||||||
|
lazyRenderList(arr, startIndex) {
|
||||||
|
for (let i = startIndex; i < arr.length; i++) {
|
||||||
|
let timer = null
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
this.listData.push(...arr[i])
|
||||||
|
}, i * 500)
|
||||||
|
this.clearTimerList.push(() => clearTimeout(timer))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 中断懒加载
|
||||||
|
resetClearTimerList() {
|
||||||
|
const list = [...this.clearTimerList]
|
||||||
|
this.clearTimerList.splice(0, this.clearTimerList.length)
|
||||||
|
list.forEach((item) => item())
|
||||||
|
},
|
||||||
|
async nameClick(node) {
|
||||||
|
if (!node[this.dataChildren]?.length && this.lazyLoadChildren) {
|
||||||
|
this.loadingArr.push(node[this.dataValue])
|
||||||
|
try {
|
||||||
|
const res = await this.load(node)
|
||||||
|
if (Array.isArray(res)) {
|
||||||
|
uni.$emit('custom-tree-select-load', {
|
||||||
|
source: node,
|
||||||
|
target: res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
this.loadingArr = []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
!node.showChildren &&
|
||||||
|
node[this.dataChildren] &&
|
||||||
|
node[this.dataChildren].length
|
||||||
|
) {
|
||||||
|
// 打开
|
||||||
|
this.renderTree(node[this.dataChildren])
|
||||||
|
} else {
|
||||||
|
// 关闭
|
||||||
|
this.resetClearTimerList()
|
||||||
|
this.listData.splice(0, this.listData.length)
|
||||||
|
}
|
||||||
|
uni.$emit('custom-tree-select-name-click', node)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
nodeClick(node) {
|
||||||
|
if (!node.disabled) {
|
||||||
|
uni.$emit('custom-tree-select-node-click', node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
styleIsolation: 'shared'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
$primary-color: #007aff;
|
||||||
|
$col-sm: 4px;
|
||||||
|
$col-base: 8px;
|
||||||
|
$col-lg: 12px;
|
||||||
|
$row-sm: 5px;
|
||||||
|
$row-base: 10px;
|
||||||
|
$row-lg: 15px;
|
||||||
|
$radius-sm: 3px;
|
||||||
|
$radius-base: 6px;
|
||||||
|
$border-color: #c8c7cc;
|
||||||
|
|
||||||
|
.custom-tree-select-content {
|
||||||
|
&.border {
|
||||||
|
border-left: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .uni-checkbox-input {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-content {
|
||||||
|
margin: 0 0 $col-lg;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: 3px;
|
||||||
|
background-color: #fff;
|
||||||
|
transform: translateX(-2px);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.right-icon {
|
||||||
|
transition: 0.15s ease;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.smallcircle-filled {
|
||||||
|
width: 14px;
|
||||||
|
height: 13.6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.smallcircle-filled-icon {
|
||||||
|
transform-origin: center;
|
||||||
|
transform: scale(0.55);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-icon-box {
|
||||||
|
margin-right: $row-sm;
|
||||||
|
width: 14px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.loading-icon {
|
||||||
|
transform-origin: center;
|
||||||
|
animation: rotating infinite 0.2s ease;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-box {
|
||||||
|
width: 23.6px;
|
||||||
|
height: 23.6px;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: $radius-sm;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
background-color: rgb(225, 225, 225);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-checked {
|
||||||
|
width: 60%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: $primary-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotating {
|
||||||
|
from {
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
export function isString(data) {
|
||||||
|
return typeof data === 'string'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
export function paging(data, PAGENUM = 50) {
|
||||||
|
if (!Array.isArray(data) || !data.length) return data
|
||||||
|
const pages = []
|
||||||
|
data.forEach((item, index) => {
|
||||||
|
const i = Math.floor(index / PAGENUM)
|
||||||
|
if (!pages[i]) {
|
||||||
|
pages[i] = []
|
||||||
|
}
|
||||||
|
pages[i].push(item)
|
||||||
|
})
|
||||||
|
return pages
|
||||||
|
}
|
||||||
83
uni_modules/custom-tree-select/package.json
Normal file
83
uni_modules/custom-tree-select/package.json
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
{
|
||||||
|
"id": "custom-tree-select",
|
||||||
|
"displayName": "custom-tree-select树形选择器支持v-model",
|
||||||
|
"version": "4.1.0",
|
||||||
|
"description": "树形选择器基于uni-ui,v-model绑定数据,父级可选、数据回显、移除选项",
|
||||||
|
"keywords": [
|
||||||
|
"custom",
|
||||||
|
"tree-select",
|
||||||
|
"选择器",
|
||||||
|
"树形选择器"
|
||||||
|
],
|
||||||
|
"repository": "https://github.com/qzlthxp/custom-tree-select",
|
||||||
|
"engines": {
|
||||||
|
},
|
||||||
|
"dcloudext": {
|
||||||
|
"type": "component-vue",
|
||||||
|
"sale": {
|
||||||
|
"regular": {
|
||||||
|
"price": "0.00"
|
||||||
|
},
|
||||||
|
"sourcecode": {
|
||||||
|
"price": "0.00"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contact": {
|
||||||
|
"qq": ""
|
||||||
|
},
|
||||||
|
"declaration": {
|
||||||
|
"ads": "无",
|
||||||
|
"data": "插件不采集任何数据",
|
||||||
|
"permissions": "无"
|
||||||
|
},
|
||||||
|
"npmurl": ""
|
||||||
|
},
|
||||||
|
"uni_modules": {
|
||||||
|
"dependencies": [],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "u"
|
||||||
|
},
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "u"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "u",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "u",
|
||||||
|
"百度": "u",
|
||||||
|
"字节跳动": "u",
|
||||||
|
"QQ": "u",
|
||||||
|
"钉钉": "u",
|
||||||
|
"快手": "u",
|
||||||
|
"飞书": "u",
|
||||||
|
"京东": "u"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
228
uni_modules/custom-tree-select/readme.md
Normal file
228
uni_modules/custom-tree-select/readme.md
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
# custom-tree-select 使用指南
|
||||||
|
|
||||||
|
找工作中…,大佬们有内推加群联系我
|
||||||
|
|
||||||
|
**提示:** 使用该插件前确保你已经导入 `uni-popup` `uni-icons` `uni-easyinput` 插件。
|
||||||
|
|
||||||
|
当前插件主要作为表单中选择器来使用,如果只是作为弹窗会出现数据状态无法重置,此时推荐使用 [`custom-tree-popup`](https://ext.dcloud.net.cn/plugin?name=custom-tree-popup) 组件。
|
||||||
|
|
||||||
|
**如果在微信小程序中使用,在 `main.js` 文件中添加以下代码,2023/05/04 之前安装插件或者本地插件版本 `<=3.7.1` 需要添加**
|
||||||
|
|
||||||
|
```js
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
Vue.prototype.$bus = new Vue()
|
||||||
|
// #endif
|
||||||
|
```
|
||||||
|
|
||||||
|
**有问题可以加 QQ 群:297080738**
|
||||||
|
|
||||||
|
## 优势
|
||||||
|
|
||||||
|
💪:基于 `uni-popup`、 `uni-icons`、 `uni-easyinput` 插件进行开发,默认样式与 `uni-easyinput` 样式对标。
|
||||||
|
|
||||||
|
⚡:全面支持懒加载应对大量数据。
|
||||||
|
|
||||||
|
🚀:v-model 绑定数据、数据回显、移除选项。
|
||||||
|
|
||||||
|
⚙ :提供更多配置项。
|
||||||
|
|
||||||
|
📦:开箱即用。
|
||||||
|
|
||||||
|
## Props
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 | 版本要求 |
|
||||||
|
| :-------------------: | :---------: | :-----------------------------: | :----------------------------------------------------------: | --------- |
|
||||||
|
| canSelectAll | Boolean | false | 开启一键全选功能 | |
|
||||||
|
| contentHeight | String | '500px' \| 视窗高度的 75%的像素 | 弹出内选择器容器高度,为解决搜索时搜索框被顶出屏幕,监听输入框 focus 事件 动态修改弹窗内选择器容器高度 | |
|
||||||
|
| clearResetSearch | Boolean | false | 设置为 `true` 并且搜索之后,点击输入框清除按钮,会清空搜索内容并且会直接重置整个弹窗内树形选择器内容,默认情况下只有清除之后再次进行查询才会重置选择器 | |
|
||||||
|
| animation | Boolean | ture | 是否开启弹窗动画 | |
|
||||||
|
| is-mask-click | Boolean | true | 点击遮罩关闭弹窗 | |
|
||||||
|
| mask-background-color | String | rgba(0,0,0,0.4) | 蒙版颜色,建议使用 rgba 颜色值 | |
|
||||||
|
| background-color | String | none | 主窗口背景色 | |
|
||||||
|
| safe-area | Boolean | true | 是否适配底部安全区 | |
|
||||||
|
| **choseParent** | **Boolean** | **true** | **父节点是否可选** | |
|
||||||
|
| **linkage** | **Boolean** | **false** | **父子节点是否联动** | |
|
||||||
|
| placeholder | String | 请选择 | 空状态信息提示、弹窗标题 | |
|
||||||
|
| confirmText | String | 完成 | 确定按钮文字 | |
|
||||||
|
| confirmTextColor | String | #007aff | 确定按钮文字颜色 | |
|
||||||
|
| listData | Array | - | 展示的数据 | |
|
||||||
|
| **dataLabel** | **String** | **name** | **listData 中对应数据的 label** | |
|
||||||
|
| **dataValue** | **String** | **id** | **listData 中对应数据的 value** | |
|
||||||
|
| **dataChildren** | **String** | **children** | **listData 中对应数据的 children** | |
|
||||||
|
| clearable | Boolean | false | 是否显示清除按钮,点击清除所有已选项 | |
|
||||||
|
| **mutiple** | **Boolean** | **false** | **是否可以多选** | |
|
||||||
|
| **disabled** | **Boolean** | **false** | **是否允许修改** | |
|
||||||
|
| disabledList | Array | [] | 设置某些选项为不可选,数组元素为数据 dataValue 对应的值,也可以操作数据 disabled 属性实现 | |
|
||||||
|
| search | Boolean | false | 是否可以搜索(常用于数据较多的情况) | |
|
||||||
|
| showChildren | Boolean | false | 默认展开(数据内部 showChildren 属性优先级更高,可以设置全局收起,单独展开某一条数据) | |
|
||||||
|
| border | Boolean | false | 显示引导线 | |
|
||||||
|
| pathMode | Boolean | false | 路径模式,开启后选择框内展示选项所在层级的完整信息如:`城市/街道/小区` | >= 3.8.2 |
|
||||||
|
| pathHyphen | String | / | 路径模式下连字符 | >= 3.8.2 |
|
||||||
|
| lazyLoadChildren | Boolean | false | 是否允许动态加载获取子节点 | >= 3.8.4 |
|
||||||
|
| load | Function | (node) => Promise<void> | 动态加载函数,具体用法见下方示例 | >= 3.8.4 |
|
||||||
|
| **v-model/value** | **Array** | **[ ]** | **已选择的值,通过 v-model 进行绑定,例如:v-model="formData.selectedList"** (根据你绑定数据的类型自动返回相同类型的数据,String 类型通过 `,` 进行分隔。>=4.0.0 版本不在支持字符串类型传参,修改为数组类型) | >= 4.0.0 |
|
||||||
|
|
||||||
|
## listdata 特有属性
|
||||||
|
|
||||||
|
| 名称 | 类型 | 默认值 | 说明 |
|
||||||
|
| ------------ | ------- | ------ | ------------------ |
|
||||||
|
| disabled | Boolean | false | 选项是否可选 |
|
||||||
|
| visible | Boolean | true | 选项是否展示 |
|
||||||
|
| showChildren | Boolean | true | 选项是否展示子节点 |
|
||||||
|
|
||||||
|
## Events
|
||||||
|
|
||||||
|
| 事件名称 | 说明 | 返回值 |
|
||||||
|
| ------------ | ------------------------ | -------------------------------------- |
|
||||||
|
| change | 弹窗组件状态发生变化触发 | e={show: true | false, type:当前模式} |
|
||||||
|
| maskClick | 点击遮罩层触发 | |
|
||||||
|
| input | 选中数据或取消选中时触发 | 以数组形式返回已选择数据 |
|
||||||
|
| selectChange | 选中数据或取消选中时触发 | 以数组形式返回已选择数据完整信息 |
|
||||||
|
| removeSelect | 选择框移除选项时触发 | 返回对应数据的完整信息 |
|
||||||
|
|
||||||
|
## 基础使用示例
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!--/pages/index/index-->
|
||||||
|
<custom-tree-select :listData="listData" v-model="formData.selectedArr" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
selectedArr: [],
|
||||||
|
},
|
||||||
|
listData: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '城市1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: '街道1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: '小区1'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '城市2',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: '街道2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 禁用某些选项,或隐藏某些选项
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!--/pages/index/index-->
|
||||||
|
<custom-tree-select
|
||||||
|
mutiple
|
||||||
|
linkage
|
||||||
|
clearable
|
||||||
|
search
|
||||||
|
dataLabel="text"
|
||||||
|
dataValue="value"
|
||||||
|
:listData="listData"
|
||||||
|
:disabledList="[6]"
|
||||||
|
v-model="formData.selected"
|
||||||
|
></custom-tree-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
formData: {
|
||||||
|
selected: []
|
||||||
|
},
|
||||||
|
listData: [
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
text: '城市1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 3,
|
||||||
|
text: '街道1',
|
||||||
|
disabled: true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 2,
|
||||||
|
text: '城市2',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
value: 6,
|
||||||
|
text: '街道2'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 7,
|
||||||
|
text: '城市3',
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 动态加载节点
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<custom-tree-select
|
||||||
|
:listData="listData"
|
||||||
|
:load="load"
|
||||||
|
lazyLoadChildren
|
||||||
|
pathMode
|
||||||
|
v-model="selectedArr"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedArr: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
load(node) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
if (node) {
|
||||||
|
setTimeout(() => {
|
||||||
|
resolve([
|
||||||
|
{
|
||||||
|
value: '128047129041',
|
||||||
|
text: '测试异步加载'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
}, 2000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
84
uni_modules/uni-popup/changelog.md
Normal file
84
uni_modules/uni-popup/changelog.md
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
## 1.9.1(2024-04-02)
|
||||||
|
- 修复 uni-popup-dialog vue3下使用value无法进行绑定的bug(双向绑定兼容旧写法)
|
||||||
|
## 1.9.0(2024-03-28)
|
||||||
|
- 修复 uni-popup-dialog 双向绑定时初始化逻辑修正
|
||||||
|
## 1.8.9(2024-03-20)
|
||||||
|
- 修复 uni-popup-dialog 数据输入时修正为双向绑定
|
||||||
|
## 1.8.8(2024-02-20)
|
||||||
|
- 修复 uni-popup 在微信小程序下出现文字向上闪动的bug
|
||||||
|
## 1.8.7(2024-02-02)
|
||||||
|
- 新增 uni-popup-dialog 新增属性focus:input模式下,是否自动自动聚焦
|
||||||
|
## 1.8.6(2024-01-30)
|
||||||
|
- 新增 uni-popup-dialog 新增属性maxLength:限制输入框字数
|
||||||
|
## 1.8.5(2024-01-26)
|
||||||
|
- 新增 uni-popup-dialog 新增属性showClose:控制关闭按钮的显示
|
||||||
|
## 1.8.4(2023-11-15)
|
||||||
|
- 新增 uni-popup 支持uni-app-x 注意暂时仅支持 `maskClick` `@open` `@close`
|
||||||
|
## 1.8.3(2023-04-17)
|
||||||
|
- 修复 uni-popup 重复打开时的 bug
|
||||||
|
## 1.8.2(2023-02-02)
|
||||||
|
- uni-popup-dialog 组件新增 inputType 属性
|
||||||
|
## 1.8.1(2022-12-01)
|
||||||
|
- 修复 nvue 下 v-show 报错
|
||||||
|
## 1.8.0(2022-11-29)
|
||||||
|
- 优化 主题样式
|
||||||
|
## 1.7.9(2022-04-02)
|
||||||
|
- 修复 弹出层内部无法滚动的bug
|
||||||
|
## 1.7.8(2022-03-28)
|
||||||
|
- 修复 小程序中高度错误的bug
|
||||||
|
## 1.7.7(2022-03-17)
|
||||||
|
- 修复 快速调用open出现问题的Bug
|
||||||
|
## 1.7.6(2022-02-14)
|
||||||
|
- 修复 safeArea 属性不能设置为false的bug
|
||||||
|
## 1.7.5(2022-01-19)
|
||||||
|
- 修复 isMaskClick 失效的bug
|
||||||
|
## 1.7.4(2022-01-19)
|
||||||
|
- 新增 cancelText \ confirmText 属性 ,可自定义文本
|
||||||
|
- 新增 maskBackgroundColor 属性 ,可以修改蒙版颜色
|
||||||
|
- 优化 maskClick属性 更新为 isMaskClick ,解决微信小程序警告的问题
|
||||||
|
## 1.7.3(2022-01-13)
|
||||||
|
- 修复 设置 safeArea 属性不生效的bug
|
||||||
|
## 1.7.2(2021-11-26)
|
||||||
|
- 优化 组件示例
|
||||||
|
## 1.7.1(2021-11-26)
|
||||||
|
- 修复 vuedoc 文字错误
|
||||||
|
## 1.7.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-popup](https://uniapp.dcloud.io/component/uniui/uni-popup)
|
||||||
|
## 1.6.2(2021-08-24)
|
||||||
|
- 新增 支持国际化
|
||||||
|
## 1.6.1(2021-07-30)
|
||||||
|
- 优化 vue3下事件警告的问题
|
||||||
|
## 1.6.0(2021-07-13)
|
||||||
|
- 组件兼容 vue3,如何创建vue3项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||||
|
## 1.5.0(2021-06-23)
|
||||||
|
- 新增 mask-click 遮罩层点击事件
|
||||||
|
## 1.4.5(2021-06-22)
|
||||||
|
- 修复 nvue 平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
|
||||||
|
## 1.4.4(2021-06-18)
|
||||||
|
- 修复 H5平台中间弹出后,点击内容,再点击遮罩无法关闭的Bug
|
||||||
|
## 1.4.3(2021-06-08)
|
||||||
|
- 修复 错误的 watch 字段
|
||||||
|
- 修复 safeArea 属性不生效的问题
|
||||||
|
- 修复 点击内容,再点击遮罩无法关闭的Bug
|
||||||
|
## 1.4.2(2021-05-12)
|
||||||
|
- 新增 组件示例地址
|
||||||
|
## 1.4.1(2021-04-29)
|
||||||
|
- 修复 组件内放置 input 、textarea 组件,无法聚焦的问题
|
||||||
|
## 1.4.0 (2021-04-29)
|
||||||
|
- 新增 type 属性的 left\right 值,支持左右弹出
|
||||||
|
- 新增 open(String:type) 方法参数 ,可以省略 type 属性 ,直接传入类型打开指定弹窗
|
||||||
|
- 新增 backgroundColor 属性,可定义主窗口背景色,默认不显示背景色
|
||||||
|
- 新增 safeArea 属性,是否适配底部安全区
|
||||||
|
- 修复 App\h5\微信小程序底部安全区占位不对的Bug
|
||||||
|
- 修复 App 端弹出等待的Bug
|
||||||
|
- 优化 提升低配设备性能,优化动画卡顿问题
|
||||||
|
- 优化 更简单的组件自定义方式
|
||||||
|
## 1.2.9(2021-02-05)
|
||||||
|
- 优化 组件引用关系,通过uni_modules引用组件
|
||||||
|
## 1.2.8(2021-02-05)
|
||||||
|
- 调整为uni_modules目录规范
|
||||||
|
## 1.2.7(2021-02-05)
|
||||||
|
- 调整为uni_modules目录规范
|
||||||
|
- 新增 支持 PC 端
|
||||||
|
- 新增 uni-popup-message 、uni-popup-dialog扩展组件支持 PC 端
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// #ifdef H5
|
||||||
|
export default {
|
||||||
|
name: 'Keypress',
|
||||||
|
props: {
|
||||||
|
disable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const keyNames = {
|
||||||
|
esc: ['Esc', 'Escape'],
|
||||||
|
tab: 'Tab',
|
||||||
|
enter: 'Enter',
|
||||||
|
space: [' ', 'Spacebar'],
|
||||||
|
up: ['Up', 'ArrowUp'],
|
||||||
|
left: ['Left', 'ArrowLeft'],
|
||||||
|
right: ['Right', 'ArrowRight'],
|
||||||
|
down: ['Down', 'ArrowDown'],
|
||||||
|
delete: ['Backspace', 'Delete', 'Del']
|
||||||
|
}
|
||||||
|
const listener = ($event) => {
|
||||||
|
if (this.disable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const keyName = Object.keys(keyNames).find(key => {
|
||||||
|
const keyName = $event.key
|
||||||
|
const value = keyNames[key]
|
||||||
|
return value === keyName || (Array.isArray(value) && value.includes(keyName))
|
||||||
|
})
|
||||||
|
if (keyName) {
|
||||||
|
// 避免和其他按键事件冲突
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$emit(keyName, {})
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keyup', listener)
|
||||||
|
this.$once('hook:beforeDestroy', () => {
|
||||||
|
document.removeEventListener('keyup', listener)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
render: () => {}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
@@ -0,0 +1,316 @@
|
|||||||
|
<template>
|
||||||
|
<view class="uni-popup-dialog">
|
||||||
|
<view class="uni-dialog-title">
|
||||||
|
<text class="uni-dialog-title-text" :class="['uni-popup__'+dialogType]">{{titleText}}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="mode === 'base'" class="uni-dialog-content">
|
||||||
|
<slot>
|
||||||
|
<text class="uni-dialog-content-text">{{content}}</text>
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
<view v-else class="uni-dialog-content">
|
||||||
|
<slot>
|
||||||
|
<input class="uni-dialog-input" :maxlength="maxlength" v-model="val" :type="inputType"
|
||||||
|
:placeholder="placeholderText" :focus="focus">
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
<view class="uni-dialog-button-group">
|
||||||
|
<view class="uni-dialog-button" v-if="showClose" @click="closeDialog">
|
||||||
|
<text class="uni-dialog-button-text">{{closeText}}</text>
|
||||||
|
</view>
|
||||||
|
<view class="uni-dialog-button" :class="showClose?'uni-border-left':''" @click="onOk">
|
||||||
|
<text class="uni-dialog-button-text uni-button-color">{{okText}}</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import popup from '../uni-popup/popup.js'
|
||||||
|
import {
|
||||||
|
initVueI18n
|
||||||
|
} from '@dcloudio/uni-i18n'
|
||||||
|
import messages from '../uni-popup/i18n/index.js'
|
||||||
|
const {
|
||||||
|
t
|
||||||
|
} = initVueI18n(messages)
|
||||||
|
/**
|
||||||
|
* PopUp 弹出层-对话框样式
|
||||||
|
* @description 弹出层-对话框样式
|
||||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||||
|
* @property {String} value input 模式下的默认值
|
||||||
|
* @property {String} placeholder input 模式下输入提示
|
||||||
|
* @property {Boolean} focus input模式下是否自动聚焦,默认为true
|
||||||
|
* @property {String} type = [success|warning|info|error] 主题样式
|
||||||
|
* @value success 成功
|
||||||
|
* @value warning 提示
|
||||||
|
* @value info 消息
|
||||||
|
* @value error 错误
|
||||||
|
* @property {String} mode = [base|input] 模式、
|
||||||
|
* @value base 基础对话框
|
||||||
|
* @value input 可输入对话框
|
||||||
|
* @showClose {Boolean} 是否显示关闭按钮
|
||||||
|
* @property {String} content 对话框内容
|
||||||
|
* @property {Boolean} beforeClose 是否拦截取消事件
|
||||||
|
* @property {Number} maxlength 输入
|
||||||
|
* @event {Function} confirm 点击确认按钮触发
|
||||||
|
* @event {Function} close 点击取消按钮触发
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "uniPopupDialog",
|
||||||
|
mixins: [popup],
|
||||||
|
emits: ['confirm', 'close', 'update:modelValue', 'input'],
|
||||||
|
props: {
|
||||||
|
inputType: {
|
||||||
|
type: String,
|
||||||
|
default: 'text'
|
||||||
|
},
|
||||||
|
showClose: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// #ifdef VUE2
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
// #ifdef VUE3
|
||||||
|
modelValue: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
|
||||||
|
placeholder: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'error'
|
||||||
|
},
|
||||||
|
mode: {
|
||||||
|
type: String,
|
||||||
|
default: 'base'
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
beforeClose: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
cancelText: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
confirmText: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
maxlength: {
|
||||||
|
type: Number,
|
||||||
|
default: -1,
|
||||||
|
},
|
||||||
|
focus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
dialogType: 'error',
|
||||||
|
val: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
okText() {
|
||||||
|
return this.confirmText || t("uni-popup.ok")
|
||||||
|
},
|
||||||
|
closeText() {
|
||||||
|
return this.cancelText || t("uni-popup.cancel")
|
||||||
|
},
|
||||||
|
placeholderText() {
|
||||||
|
return this.placeholder || t("uni-popup.placeholder")
|
||||||
|
},
|
||||||
|
titleText() {
|
||||||
|
return this.title || t("uni-popup.title")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
type(val) {
|
||||||
|
this.dialogType = val
|
||||||
|
},
|
||||||
|
mode(val) {
|
||||||
|
if (val === 'input') {
|
||||||
|
this.dialogType = 'info'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
value(val) {
|
||||||
|
if (this.maxlength != -1 && this.mode === 'input') {
|
||||||
|
this.val = val.slice(0, this.maxlength);
|
||||||
|
} else {
|
||||||
|
this.val = val
|
||||||
|
}
|
||||||
|
},
|
||||||
|
val(val) {
|
||||||
|
// #ifdef VUE2
|
||||||
|
// TODO 兼容 vue2
|
||||||
|
this.$emit('input', val);
|
||||||
|
// #endif
|
||||||
|
// #ifdef VUE3
|
||||||
|
// TODO 兼容 vue3
|
||||||
|
this.$emit('update:modelValue', val);
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 对话框遮罩不可点击
|
||||||
|
this.popup.disableMask()
|
||||||
|
// this.popup.closeMask()
|
||||||
|
if (this.mode === 'input') {
|
||||||
|
this.dialogType = 'info'
|
||||||
|
this.val = this.value;
|
||||||
|
// #ifdef VUE3
|
||||||
|
this.val = this.modelValue;
|
||||||
|
// #endif
|
||||||
|
} else {
|
||||||
|
this.dialogType = this.type
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 点击确认按钮
|
||||||
|
*/
|
||||||
|
onOk() {
|
||||||
|
if (this.mode === 'input') {
|
||||||
|
this.$emit('confirm', this.val)
|
||||||
|
} else {
|
||||||
|
this.$emit('confirm')
|
||||||
|
}
|
||||||
|
if (this.beforeClose) return
|
||||||
|
this.popup.close()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 点击取消按钮
|
||||||
|
*/
|
||||||
|
closeDialog() {
|
||||||
|
this.$emit('close')
|
||||||
|
if (this.beforeClose) return
|
||||||
|
this.popup.close()
|
||||||
|
},
|
||||||
|
close() {
|
||||||
|
this.popup.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.uni-popup-dialog {
|
||||||
|
width: 300px;
|
||||||
|
border-radius: 11px;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-title {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-title-text {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-content {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-content-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #6C6C6C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-button-group {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
border-top-color: #f5f5f5;
|
||||||
|
border-top-style: solid;
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-button {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-border-left {
|
||||||
|
border-left-color: #f0f0f0;
|
||||||
|
border-left-style: solid;
|
||||||
|
border-left-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-button-text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-button-color {
|
||||||
|
color: #007aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-dialog-input {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px #eee solid;
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__success {
|
||||||
|
color: #4cd964;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__warn {
|
||||||
|
color: #f0ad4e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__error {
|
||||||
|
color: #dd524d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__info {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
<template>
|
||||||
|
<view class="uni-popup-message">
|
||||||
|
<view class="uni-popup-message__box fixforpc-width" :class="'uni-popup__'+type">
|
||||||
|
<slot>
|
||||||
|
<text class="uni-popup-message-text" :class="'uni-popup__'+type+'-text'">{{message}}</text>
|
||||||
|
</slot>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import popup from '../uni-popup/popup.js'
|
||||||
|
/**
|
||||||
|
* PopUp 弹出层-消息提示
|
||||||
|
* @description 弹出层-消息提示
|
||||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||||
|
* @property {String} type = [success|warning|info|error] 主题样式
|
||||||
|
* @value success 成功
|
||||||
|
* @value warning 提示
|
||||||
|
* @value info 消息
|
||||||
|
* @value error 错误
|
||||||
|
* @property {String} message 消息提示文字
|
||||||
|
* @property {String} duration 显示时间,设置为 0 则不会自动关闭
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'uniPopupMessage',
|
||||||
|
mixins:[popup],
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 主题 success/warning/info/error 默认 success
|
||||||
|
*/
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'success'
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 消息文字
|
||||||
|
*/
|
||||||
|
message: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 显示时间,设置为 0 则不会自动关闭
|
||||||
|
*/
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
default: 3000
|
||||||
|
},
|
||||||
|
maskShow:{
|
||||||
|
type:Boolean,
|
||||||
|
default:false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.popup.maskShow = this.maskShow
|
||||||
|
this.popup.messageChild = this
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
timerClose(){
|
||||||
|
if(this.duration === 0) return
|
||||||
|
clearTimeout(this.timer)
|
||||||
|
this.timer = setTimeout(()=>{
|
||||||
|
this.popup.close()
|
||||||
|
},this.duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" >
|
||||||
|
.uni-popup-message {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup-message__box {
|
||||||
|
background-color: #e1f3d8;
|
||||||
|
padding: 10px 15px;
|
||||||
|
border-color: #eee;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 500px) {
|
||||||
|
.fixforpc-width {
|
||||||
|
margin-top: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
flex: none;
|
||||||
|
min-width: 380px;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
max-width: 50%;
|
||||||
|
/* #endif */
|
||||||
|
/* #ifdef APP-NVUE */
|
||||||
|
max-width: 500px;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup-message-text {
|
||||||
|
font-size: 14px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__success {
|
||||||
|
background-color: #e1f3d8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__success-text {
|
||||||
|
color: #67C23A;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__warn {
|
||||||
|
background-color: #faecd8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__warn-text {
|
||||||
|
color: #E6A23C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__error {
|
||||||
|
background-color: #fde2e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__error-text {
|
||||||
|
color: #F56C6C;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__info {
|
||||||
|
background-color: #F2F6FC;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__info-text {
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
<template>
|
||||||
|
<view class="uni-popup-share">
|
||||||
|
<view class="uni-share-title"><text class="uni-share-title-text">{{shareTitleText}}</text></view>
|
||||||
|
<view class="uni-share-content">
|
||||||
|
<view class="uni-share-content-box">
|
||||||
|
<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" @click.stop="select(item,index)">
|
||||||
|
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image>
|
||||||
|
<text class="uni-share-text">{{item.text}}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="uni-share-button-box">
|
||||||
|
<button class="uni-share-button" @click="close">{{cancelText}}</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import popup from '../uni-popup/popup.js'
|
||||||
|
import {
|
||||||
|
initVueI18n
|
||||||
|
} from '@dcloudio/uni-i18n'
|
||||||
|
import messages from '../uni-popup/i18n/index.js'
|
||||||
|
const { t } = initVueI18n(messages)
|
||||||
|
export default {
|
||||||
|
name: 'UniPopupShare',
|
||||||
|
mixins:[popup],
|
||||||
|
emits:['select'],
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
beforeClose: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
bottomData: [{
|
||||||
|
text: '微信',
|
||||||
|
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/c2b17470-50be-11eb-b680-7980c8a877b8.png',
|
||||||
|
name: 'wx'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '支付宝',
|
||||||
|
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/d684ae40-50be-11eb-8ff1-d5dcf8779628.png',
|
||||||
|
name: 'ali'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: 'QQ',
|
||||||
|
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/e7a79520-50be-11eb-b997-9918a5dda011.png',
|
||||||
|
name: 'qq'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '新浪',
|
||||||
|
icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/0dacdbe0-50bf-11eb-8ff1-d5dcf8779628.png',
|
||||||
|
name: 'sina'
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// text: '百度',
|
||||||
|
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/1ec6e920-50bf-11eb-8a36-ebb87efcf8c0.png',
|
||||||
|
// name: 'copy'
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// text: '其他',
|
||||||
|
// icon: 'https://vkceyugu.cdn.bspapp.com/VKCEYUGU-dc-site/2e0fdfe0-50bf-11eb-b997-9918a5dda011.png',
|
||||||
|
// name: 'more'
|
||||||
|
// }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {},
|
||||||
|
computed: {
|
||||||
|
cancelText() {
|
||||||
|
return t("uni-popup.cancel")
|
||||||
|
},
|
||||||
|
shareTitleText() {
|
||||||
|
return this.title || t("uni-popup.shareTitle")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 选择内容
|
||||||
|
*/
|
||||||
|
select(item, index) {
|
||||||
|
this.$emit('select', {
|
||||||
|
item,
|
||||||
|
index
|
||||||
|
})
|
||||||
|
this.close()
|
||||||
|
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 关闭窗口
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
if(this.beforeClose) return
|
||||||
|
this.popup.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" >
|
||||||
|
.uni-popup-share {
|
||||||
|
background-color: #fff;
|
||||||
|
border-top-left-radius: 11px;
|
||||||
|
border-top-right-radius: 11px;
|
||||||
|
}
|
||||||
|
.uni-share-title {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
.uni-share-title-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.uni-share-content {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-content-box {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-content-item {
|
||||||
|
width: 90px;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-content-item:active {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-image {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-text {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #3B4144;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-button-box {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: flex;
|
||||||
|
/* #endif */
|
||||||
|
flex-direction: row;
|
||||||
|
padding: 10px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-button {
|
||||||
|
flex: 1;
|
||||||
|
border-radius: 50px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-share-button::after {
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
7
uni_modules/uni-popup/components/uni-popup/i18n/en.json
Normal file
7
uni_modules/uni-popup/components/uni-popup/i18n/en.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"uni-popup.cancel": "cancel",
|
||||||
|
"uni-popup.ok": "ok",
|
||||||
|
"uni-popup.placeholder": "pleace enter",
|
||||||
|
"uni-popup.title": "Hint",
|
||||||
|
"uni-popup.shareTitle": "Share to"
|
||||||
|
}
|
||||||
8
uni_modules/uni-popup/components/uni-popup/i18n/index.js
Normal file
8
uni_modules/uni-popup/components/uni-popup/i18n/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import en from './en.json'
|
||||||
|
import zhHans from './zh-Hans.json'
|
||||||
|
import zhHant from './zh-Hant.json'
|
||||||
|
export default {
|
||||||
|
en,
|
||||||
|
'zh-Hans': zhHans,
|
||||||
|
'zh-Hant': zhHant
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"uni-popup.cancel": "取消",
|
||||||
|
"uni-popup.ok": "确定",
|
||||||
|
"uni-popup.placeholder": "请输入",
|
||||||
|
"uni-popup.title": "提示",
|
||||||
|
"uni-popup.shareTitle": "分享到"
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"uni-popup.cancel": "取消",
|
||||||
|
"uni-popup.ok": "確定",
|
||||||
|
"uni-popup.placeholder": "請輸入",
|
||||||
|
"uni-popup.title": "提示",
|
||||||
|
"uni-popup.shareTitle": "分享到"
|
||||||
|
}
|
||||||
45
uni_modules/uni-popup/components/uni-popup/keypress.js
Normal file
45
uni_modules/uni-popup/components/uni-popup/keypress.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// #ifdef H5
|
||||||
|
export default {
|
||||||
|
name: 'Keypress',
|
||||||
|
props: {
|
||||||
|
disable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
const keyNames = {
|
||||||
|
esc: ['Esc', 'Escape'],
|
||||||
|
tab: 'Tab',
|
||||||
|
enter: 'Enter',
|
||||||
|
space: [' ', 'Spacebar'],
|
||||||
|
up: ['Up', 'ArrowUp'],
|
||||||
|
left: ['Left', 'ArrowLeft'],
|
||||||
|
right: ['Right', 'ArrowRight'],
|
||||||
|
down: ['Down', 'ArrowDown'],
|
||||||
|
delete: ['Backspace', 'Delete', 'Del']
|
||||||
|
}
|
||||||
|
const listener = ($event) => {
|
||||||
|
if (this.disable) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const keyName = Object.keys(keyNames).find(key => {
|
||||||
|
const keyName = $event.key
|
||||||
|
const value = keyNames[key]
|
||||||
|
return value === keyName || (Array.isArray(value) && value.includes(keyName))
|
||||||
|
})
|
||||||
|
if (keyName) {
|
||||||
|
// 避免和其他按键事件冲突
|
||||||
|
setTimeout(() => {
|
||||||
|
this.$emit(keyName, {})
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.addEventListener('keyup', listener)
|
||||||
|
// this.$once('hook:beforeDestroy', () => {
|
||||||
|
// document.removeEventListener('keyup', listener)
|
||||||
|
// })
|
||||||
|
},
|
||||||
|
render: () => {}
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
26
uni_modules/uni-popup/components/uni-popup/popup.js
Normal file
26
uni_modules/uni-popup/components/uni-popup/popup.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
this.popup = this.getParent()
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
/**
|
||||||
|
* 获取父元素实例
|
||||||
|
*/
|
||||||
|
getParent(name = 'uniPopup') {
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
90
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
Normal file
90
uni_modules/uni-popup/components/uni-popup/uni-popup.uvue
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<template>
|
||||||
|
<view class="popup-root" v-if="isOpen" v-show="isShow" @click="clickMask">
|
||||||
|
<view @click.stop>
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
type CloseCallBack = ()=> void;
|
||||||
|
let closeCallBack:CloseCallBack = () :void => {};
|
||||||
|
export default {
|
||||||
|
emits:["close","clickMask"],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isShow:false,
|
||||||
|
isOpen:false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
maskClick: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 设置show = true 时,如果没有 open 需要设置为 open
|
||||||
|
isShow:{
|
||||||
|
handler(isShow) {
|
||||||
|
// console.log("isShow",isShow)
|
||||||
|
if(isShow && this.isOpen == false){
|
||||||
|
this.isOpen = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate:true
|
||||||
|
},
|
||||||
|
// 设置isOpen = true 时,如果没有 isShow 需要设置为 isShow
|
||||||
|
isOpen:{
|
||||||
|
handler(isOpen) {
|
||||||
|
// console.log("isOpen",isOpen)
|
||||||
|
if(isOpen && this.isShow == false){
|
||||||
|
this.isShow = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate:true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods:{
|
||||||
|
open(){
|
||||||
|
// ...funs : CloseCallBack[]
|
||||||
|
// if(funs.length > 0){
|
||||||
|
// closeCallBack = funs[0]
|
||||||
|
// }
|
||||||
|
this.isOpen = true;
|
||||||
|
},
|
||||||
|
clickMask(){
|
||||||
|
if(this.maskClick == true){
|
||||||
|
this.$emit('clickMask')
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close(): void{
|
||||||
|
this.isOpen = false;
|
||||||
|
this.$emit('close')
|
||||||
|
closeCallBack()
|
||||||
|
},
|
||||||
|
hiden(){
|
||||||
|
this.isShow = false
|
||||||
|
},
|
||||||
|
show(){
|
||||||
|
this.isShow = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.popup-root {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 750rpx;
|
||||||
|
height: 100%;
|
||||||
|
flex: 1;
|
||||||
|
background-color: rgba(0, 0, 0, 0.3);
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
503
uni_modules/uni-popup/components/uni-popup/uni-popup.vue
Normal file
503
uni_modules/uni-popup/components/uni-popup/uni-popup.vue
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
<template>
|
||||||
|
<view v-if="showPopup" class="uni-popup" :class="[popupstyle, isDesktop ? 'fixforpc-z-index' : '']">
|
||||||
|
<view @touchstart="touchstart">
|
||||||
|
<uni-transition key="1" v-if="maskShow" name="mask" mode-class="fade" :styles="maskClass"
|
||||||
|
:duration="duration" :show="showTrans" @click="onTap" />
|
||||||
|
<uni-transition key="2" :mode-class="ani" name="content" :styles="transClass" :duration="duration"
|
||||||
|
:show="showTrans" @click="onTap">
|
||||||
|
<view class="uni-popup__wrapper" :style="getStyles" :class="[popupstyle]" @click="clear">
|
||||||
|
<slot />
|
||||||
|
</view>
|
||||||
|
</uni-transition>
|
||||||
|
</view>
|
||||||
|
<!-- #ifdef H5 -->
|
||||||
|
<keypress v-if="maskShow" @esc="onTap" />
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// #ifdef H5
|
||||||
|
import keypress from './keypress.js'
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PopUp 弹出层
|
||||||
|
* @description 弹出层组件,为了解决遮罩弹层的问题
|
||||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=329
|
||||||
|
* @property {String} type = [top|center|bottom|left|right|message|dialog|share] 弹出方式
|
||||||
|
* @value top 顶部弹出
|
||||||
|
* @value center 中间弹出
|
||||||
|
* @value bottom 底部弹出
|
||||||
|
* @value left 左侧弹出
|
||||||
|
* @value right 右侧弹出
|
||||||
|
* @value message 消息提示
|
||||||
|
* @value dialog 对话框
|
||||||
|
* @value share 底部分享示例
|
||||||
|
* @property {Boolean} animation = [true|false] 是否开启动画
|
||||||
|
* @property {Boolean} maskClick = [true|false] 蒙版点击是否关闭弹窗(废弃)
|
||||||
|
* @property {Boolean} isMaskClick = [true|false] 蒙版点击是否关闭弹窗
|
||||||
|
* @property {String} backgroundColor 主窗口背景色
|
||||||
|
* @property {String} maskBackgroundColor 蒙版颜色
|
||||||
|
* @property {String} borderRadius 设置圆角(左上、右上、右下和左下) 示例:"10px 10px 10px 10px"
|
||||||
|
* @property {Boolean} safeArea 是否适配底部安全区
|
||||||
|
* @event {Function} change 打开关闭弹窗触发,e={show: false}
|
||||||
|
* @event {Function} maskClick 点击遮罩触发
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'uniPopup',
|
||||||
|
components: {
|
||||||
|
// #ifdef H5
|
||||||
|
keypress
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
emits: ['change', 'maskClick'],
|
||||||
|
props: {
|
||||||
|
// 开启动画
|
||||||
|
animation: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 弹出层类型,可选值,top: 顶部弹出层;bottom:底部弹出层;center:全屏弹出层
|
||||||
|
// message: 消息提示 ; dialog : 对话框
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'center'
|
||||||
|
},
|
||||||
|
// maskClick
|
||||||
|
isMaskClick: {
|
||||||
|
type: Boolean,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
// TODO 2 个版本后废弃属性 ,使用 isMaskClick
|
||||||
|
maskClick: {
|
||||||
|
type: Boolean,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
backgroundColor: {
|
||||||
|
type: String,
|
||||||
|
default: 'none'
|
||||||
|
},
|
||||||
|
safeArea: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
maskBackgroundColor: {
|
||||||
|
type: String,
|
||||||
|
default: 'rgba(0, 0, 0, 0.4)'
|
||||||
|
},
|
||||||
|
borderRadius:{
|
||||||
|
type: String,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* 监听type类型
|
||||||
|
*/
|
||||||
|
type: {
|
||||||
|
handler: function(type) {
|
||||||
|
if (!this.config[type]) return
|
||||||
|
this[this.config[type]](true)
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
isDesktop: {
|
||||||
|
handler: function(newVal) {
|
||||||
|
if (!this.config[newVal]) return
|
||||||
|
this[this.config[this.type]](true)
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 监听遮罩是否可点击
|
||||||
|
* @param {Object} val
|
||||||
|
*/
|
||||||
|
maskClick: {
|
||||||
|
handler: function(val) {
|
||||||
|
this.mkclick = val
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
isMaskClick: {
|
||||||
|
handler: function(val) {
|
||||||
|
this.mkclick = val
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
// H5 下禁止底部滚动
|
||||||
|
showPopup(show) {
|
||||||
|
// #ifdef H5
|
||||||
|
// fix by mehaotian 处理 h5 滚动穿透的问题
|
||||||
|
document.getElementsByTagName('body')[0].style.overflow = show ? 'hidden' : 'visible'
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
duration: 300,
|
||||||
|
ani: [],
|
||||||
|
showPopup: false,
|
||||||
|
showTrans: false,
|
||||||
|
popupWidth: 0,
|
||||||
|
popupHeight: 0,
|
||||||
|
config: {
|
||||||
|
top: 'top',
|
||||||
|
bottom: 'bottom',
|
||||||
|
center: 'center',
|
||||||
|
left: 'left',
|
||||||
|
right: 'right',
|
||||||
|
message: 'top',
|
||||||
|
dialog: 'center',
|
||||||
|
share: 'bottom'
|
||||||
|
},
|
||||||
|
maskClass: {
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: 0,
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.4)'
|
||||||
|
},
|
||||||
|
transClass: {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderRadius: this.borderRadius || "0",
|
||||||
|
position: 'fixed',
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
},
|
||||||
|
maskShow: true,
|
||||||
|
mkclick: true,
|
||||||
|
popupstyle: 'top'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
getStyles() {
|
||||||
|
let res = { backgroundColor: this.bg };
|
||||||
|
if (this.borderRadius || "0") {
|
||||||
|
res = Object.assign(res, { borderRadius: this.borderRadius })
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
isDesktop() {
|
||||||
|
return this.popupWidth >= 500 && this.popupHeight >= 500
|
||||||
|
},
|
||||||
|
bg() {
|
||||||
|
if (this.backgroundColor === '' || this.backgroundColor === 'none') {
|
||||||
|
return 'transparent'
|
||||||
|
}
|
||||||
|
return this.backgroundColor
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const fixSize = () => {
|
||||||
|
const {
|
||||||
|
windowWidth,
|
||||||
|
windowHeight,
|
||||||
|
windowTop,
|
||||||
|
safeArea,
|
||||||
|
screenHeight,
|
||||||
|
safeAreaInsets
|
||||||
|
} = uni.getSystemInfoSync()
|
||||||
|
this.popupWidth = windowWidth
|
||||||
|
this.popupHeight = windowHeight + (windowTop || 0)
|
||||||
|
// TODO fix by mehaotian 是否适配底部安全区 ,目前微信ios 、和 app ios 计算有差异,需要框架修复
|
||||||
|
if (safeArea && this.safeArea) {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
this.safeAreaInsets = screenHeight - safeArea.bottom
|
||||||
|
// #endif
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
this.safeAreaInsets = safeAreaInsets.bottom
|
||||||
|
// #endif
|
||||||
|
} else {
|
||||||
|
this.safeAreaInsets = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fixSize()
|
||||||
|
// #ifdef H5
|
||||||
|
// window.addEventListener('resize', fixSize)
|
||||||
|
// this.$once('hook:beforeDestroy', () => {
|
||||||
|
// window.removeEventListener('resize', fixSize)
|
||||||
|
// })
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
// #ifndef VUE3
|
||||||
|
// TODO vue2
|
||||||
|
destroyed() {
|
||||||
|
this.setH5Visible()
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
// #ifdef VUE3
|
||||||
|
// TODO vue3
|
||||||
|
unmounted() {
|
||||||
|
this.setH5Visible()
|
||||||
|
},
|
||||||
|
// #endif
|
||||||
|
activated() {
|
||||||
|
this.setH5Visible(!this.showPopup);
|
||||||
|
},
|
||||||
|
deactivated() {
|
||||||
|
this.setH5Visible(true);
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// this.mkclick = this.isMaskClick || this.maskClick
|
||||||
|
if (this.isMaskClick === null && this.maskClick === null) {
|
||||||
|
this.mkclick = true
|
||||||
|
} else {
|
||||||
|
this.mkclick = this.isMaskClick !== null ? this.isMaskClick : this.maskClick
|
||||||
|
}
|
||||||
|
if (this.animation) {
|
||||||
|
this.duration = 300
|
||||||
|
} else {
|
||||||
|
this.duration = 0
|
||||||
|
}
|
||||||
|
// TODO 处理 message 组件生命周期异常的问题
|
||||||
|
this.messageChild = null
|
||||||
|
// TODO 解决头条冒泡的问题
|
||||||
|
this.clearPropagation = false
|
||||||
|
this.maskClass.backgroundColor = this.maskBackgroundColor
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
setH5Visible(visible = true) {
|
||||||
|
// #ifdef H5
|
||||||
|
// fix by mehaotian 处理 h5 滚动穿透的问题
|
||||||
|
document.getElementsByTagName('body')[0].style.overflow = visible ? "visible" : "hidden";
|
||||||
|
// #endif
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 公用方法,不显示遮罩层
|
||||||
|
*/
|
||||||
|
closeMask() {
|
||||||
|
this.maskShow = false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 公用方法,遮罩层禁止点击
|
||||||
|
*/
|
||||||
|
disableMask() {
|
||||||
|
this.mkclick = false
|
||||||
|
},
|
||||||
|
// TODO nvue 取消冒泡
|
||||||
|
clear(e) {
|
||||||
|
// #ifndef APP-NVUE
|
||||||
|
e.stopPropagation()
|
||||||
|
// #endif
|
||||||
|
this.clearPropagation = true
|
||||||
|
},
|
||||||
|
|
||||||
|
open(direction) {
|
||||||
|
// fix by mehaotian 处理快速打开关闭的情况
|
||||||
|
if (this.showPopup) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let innerType = ['top', 'center', 'bottom', 'left', 'right', 'message', 'dialog', 'share']
|
||||||
|
if (!(direction && innerType.indexOf(direction) !== -1)) {
|
||||||
|
direction = this.type
|
||||||
|
}
|
||||||
|
if (!this.config[direction]) {
|
||||||
|
console.error('缺少类型:', direction)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this[this.config[direction]]()
|
||||||
|
this.$emit('change', {
|
||||||
|
show: true,
|
||||||
|
type: direction
|
||||||
|
})
|
||||||
|
},
|
||||||
|
close(type) {
|
||||||
|
this.showTrans = false
|
||||||
|
this.$emit('change', {
|
||||||
|
show: false,
|
||||||
|
type: this.type
|
||||||
|
})
|
||||||
|
clearTimeout(this.timer)
|
||||||
|
// // 自定义关闭事件
|
||||||
|
// this.customOpen && this.customClose()
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
this.showPopup = false
|
||||||
|
}, 300)
|
||||||
|
},
|
||||||
|
// TODO 处理冒泡事件,头条的冒泡事件有问题 ,先这样兼容
|
||||||
|
touchstart() {
|
||||||
|
this.clearPropagation = false
|
||||||
|
},
|
||||||
|
|
||||||
|
onTap() {
|
||||||
|
if (this.clearPropagation) {
|
||||||
|
// fix by mehaotian 兼容 nvue
|
||||||
|
this.clearPropagation = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$emit('maskClick')
|
||||||
|
if (!this.mkclick) return
|
||||||
|
this.close()
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 顶部弹出样式处理
|
||||||
|
*/
|
||||||
|
top(type) {
|
||||||
|
this.popupstyle = this.isDesktop ? 'fixforpc-top' : 'top'
|
||||||
|
this.ani = ['slide-top']
|
||||||
|
this.transClass = {
|
||||||
|
position: 'fixed',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: this.bg,
|
||||||
|
borderRadius:this.borderRadius || "0"
|
||||||
|
}
|
||||||
|
// TODO 兼容 type 属性 ,后续会废弃
|
||||||
|
if (type) return
|
||||||
|
this.showPopup = true
|
||||||
|
this.showTrans = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.messageChild && this.type === 'message') {
|
||||||
|
this.messageChild.timerClose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 底部弹出样式处理
|
||||||
|
*/
|
||||||
|
bottom(type) {
|
||||||
|
this.popupstyle = 'bottom'
|
||||||
|
this.ani = ['slide-bottom']
|
||||||
|
this.transClass = {
|
||||||
|
position: 'fixed',
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
paddingBottom: this.safeAreaInsets + 'px',
|
||||||
|
backgroundColor: this.bg,
|
||||||
|
borderRadius:this.borderRadius || "0",
|
||||||
|
}
|
||||||
|
// TODO 兼容 type 属性 ,后续会废弃
|
||||||
|
if (type) return
|
||||||
|
this.showPopup = true
|
||||||
|
this.showTrans = true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 中间弹出样式处理
|
||||||
|
*/
|
||||||
|
center(type) {
|
||||||
|
this.popupstyle = 'center'
|
||||||
|
//微信小程序下,组合动画会出现文字向上闪动问题,再此做特殊处理
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
this.ani = ['fade']
|
||||||
|
// #endif
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
this.ani = ['zoom-out', 'fade']
|
||||||
|
// #endif
|
||||||
|
this.transClass = {
|
||||||
|
position: 'fixed',
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
/* #endif */
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
borderRadius:this.borderRadius || "0"
|
||||||
|
}
|
||||||
|
// TODO 兼容 type 属性 ,后续会废弃
|
||||||
|
if (type) return
|
||||||
|
this.showPopup = true
|
||||||
|
this.showTrans = true
|
||||||
|
},
|
||||||
|
left(type) {
|
||||||
|
this.popupstyle = 'left'
|
||||||
|
this.ani = ['slide-left']
|
||||||
|
this.transClass = {
|
||||||
|
position: 'fixed',
|
||||||
|
left: 0,
|
||||||
|
bottom: 0,
|
||||||
|
top: 0,
|
||||||
|
backgroundColor: this.bg,
|
||||||
|
borderRadius:this.borderRadius || "0",
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column'
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
// TODO 兼容 type 属性 ,后续会废弃
|
||||||
|
if (type) return
|
||||||
|
this.showPopup = true
|
||||||
|
this.showTrans = true
|
||||||
|
},
|
||||||
|
right(type) {
|
||||||
|
this.popupstyle = 'right'
|
||||||
|
this.ani = ['slide-right']
|
||||||
|
this.transClass = {
|
||||||
|
position: 'fixed',
|
||||||
|
bottom: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
backgroundColor: this.bg,
|
||||||
|
borderRadius:this.borderRadius || "0",
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column'
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
// TODO 兼容 type 属性 ,后续会废弃
|
||||||
|
if (type) return
|
||||||
|
this.showPopup = true
|
||||||
|
this.showTrans = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.uni-popup {
|
||||||
|
position: fixed;
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
z-index: 99;
|
||||||
|
|
||||||
|
/* #endif */
|
||||||
|
&.top,
|
||||||
|
&.left,
|
||||||
|
&.right {
|
||||||
|
/* #ifdef H5 */
|
||||||
|
top: var(--window-top);
|
||||||
|
/* #endif */
|
||||||
|
/* #ifndef H5 */
|
||||||
|
top: 0;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.uni-popup__wrapper {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
display: block;
|
||||||
|
/* #endif */
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
/* iphonex 等安全区设置,底部安全区适配 */
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
// padding-bottom: constant(safe-area-inset-bottom);
|
||||||
|
// padding-bottom: env(safe-area-inset-bottom);
|
||||||
|
/* #endif */
|
||||||
|
&.left,
|
||||||
|
&.right {
|
||||||
|
/* #ifdef H5 */
|
||||||
|
padding-top: var(--window-top);
|
||||||
|
/* #endif */
|
||||||
|
/* #ifndef H5 */
|
||||||
|
padding-top: 0;
|
||||||
|
/* #endif */
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixforpc-z-index {
|
||||||
|
/* #ifndef APP-NVUE */
|
||||||
|
z-index: 999;
|
||||||
|
/* #endif */
|
||||||
|
}
|
||||||
|
|
||||||
|
.fixforpc-top {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
88
uni_modules/uni-popup/package.json
Normal file
88
uni_modules/uni-popup/package.json
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"id": "uni-popup",
|
||||||
|
"displayName": "uni-popup 弹出层",
|
||||||
|
"version": "1.9.1",
|
||||||
|
"description": " Popup 组件,提供常用的弹层",
|
||||||
|
"keywords": [
|
||||||
|
"uni-ui",
|
||||||
|
"弹出层",
|
||||||
|
"弹窗",
|
||||||
|
"popup",
|
||||||
|
"弹框"
|
||||||
|
],
|
||||||
|
"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-transition"
|
||||||
|
],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y",
|
||||||
|
"alipay": "n"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
},
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
uni_modules/uni-popup/readme.md
Normal file
17
uni_modules/uni-popup/readme.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## Popup 弹出层
|
||||||
|
> **组件名:uni-popup**
|
||||||
|
> 代码块: `uPopup`
|
||||||
|
> 关联组件:`uni-transition`
|
||||||
|
|
||||||
|
|
||||||
|
弹出层组件,在应用中弹出一个消息提示窗口、提示框等
|
||||||
|
|
||||||
|
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-popup)
|
||||||
|
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
24
uni_modules/uni-transition/changelog.md
Normal file
24
uni_modules/uni-transition/changelog.md
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
## 1.3.3(2024-04-23)
|
||||||
|
- 修复 当元素会受变量影响自动隐藏的bug
|
||||||
|
## 1.3.2(2023-05-04)
|
||||||
|
- 修复 NVUE 平台报错的问题
|
||||||
|
## 1.3.1(2021-11-23)
|
||||||
|
- 修复 init 方法初始化问题
|
||||||
|
## 1.3.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-transition](https://uniapp.dcloud.io/component/uniui/uni-transition)
|
||||||
|
## 1.2.1(2021-09-27)
|
||||||
|
- 修复 init 方法不生效的 Bug
|
||||||
|
## 1.2.0(2021-07-30)
|
||||||
|
- 组件兼容 vue3,如何创建 vue3 项目,详见 [uni-app 项目支持 vue3 介绍](https://ask.dcloud.net.cn/article/37834)
|
||||||
|
## 1.1.1(2021-05-12)
|
||||||
|
- 新增 示例地址
|
||||||
|
- 修复 示例项目缺少组件的 Bug
|
||||||
|
## 1.1.0(2021-04-22)
|
||||||
|
- 新增 通过方法自定义动画
|
||||||
|
- 新增 custom-class 非 NVUE 平台支持自定义 class 定制样式
|
||||||
|
- 优化 动画触发逻辑,使动画更流畅
|
||||||
|
- 优化 支持单独的动画类型
|
||||||
|
- 优化 文档示例
|
||||||
|
## 1.0.2(2021-02-05)
|
||||||
|
- 调整为 uni_modules 目录规范
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
// const defaultOption = {
|
||||||
|
// duration: 300,
|
||||||
|
// timingFunction: 'linear',
|
||||||
|
// delay: 0,
|
||||||
|
// transformOrigin: '50% 50% 0'
|
||||||
|
// }
|
||||||
|
// #ifdef APP-NVUE
|
||||||
|
const nvueAnimation = uni.requireNativePlugin('animation')
|
||||||
|
// #endif
|
||||||
|
class MPAnimation {
|
||||||
|
constructor(options, _this) {
|
||||||
|
this.options = options
|
||||||
|
// 在iOS10+QQ小程序平台下,传给原生的对象一定是个普通对象而不是Proxy对象,否则会报parameter should be Object instead of ProxyObject的错误
|
||||||
|
this.animation = uni.createAnimation({
|
||||||
|
...options
|
||||||
|
})
|
||||||
|
this.currentStepAnimates = {}
|
||||||
|
this.next = 0
|
||||||
|
this.$ = _this
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
_nvuePushAnimates(type, args) {
|
||||||
|
let aniObj = this.currentStepAnimates[this.next]
|
||||||
|
let styles = {}
|
||||||
|
if (!aniObj) {
|
||||||
|
styles = {
|
||||||
|
styles: {},
|
||||||
|
config: {}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
styles = aniObj
|
||||||
|
}
|
||||||
|
if (animateTypes1.includes(type)) {
|
||||||
|
if (!styles.styles.transform) {
|
||||||
|
styles.styles.transform = ''
|
||||||
|
}
|
||||||
|
let unit = ''
|
||||||
|
if(type === 'rotate'){
|
||||||
|
unit = 'deg'
|
||||||
|
}
|
||||||
|
styles.styles.transform += `${type}(${args+unit}) `
|
||||||
|
} else {
|
||||||
|
styles.styles[type] = `${args}`
|
||||||
|
}
|
||||||
|
this.currentStepAnimates[this.next] = styles
|
||||||
|
}
|
||||||
|
_animateRun(styles = {}, config = {}) {
|
||||||
|
let ref = this.$.$refs['ani'].ref
|
||||||
|
if (!ref) return
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
nvueAnimation.transition(ref, {
|
||||||
|
styles,
|
||||||
|
...config
|
||||||
|
}, res => {
|
||||||
|
resolve()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_nvueNextAnimate(animates, step = 0, fn) {
|
||||||
|
let obj = animates[step]
|
||||||
|
if (obj) {
|
||||||
|
let {
|
||||||
|
styles,
|
||||||
|
config
|
||||||
|
} = obj
|
||||||
|
this._animateRun(styles, config).then(() => {
|
||||||
|
step += 1
|
||||||
|
this._nvueNextAnimate(animates, step, fn)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.currentStepAnimates = {}
|
||||||
|
typeof fn === 'function' && fn()
|
||||||
|
this.isEnd = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
step(config = {}) {
|
||||||
|
// #ifndef APP-NVUE
|
||||||
|
this.animation.step(config)
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-NVUE
|
||||||
|
this.currentStepAnimates[this.next].config = Object.assign({}, this.options, config)
|
||||||
|
this.currentStepAnimates[this.next].styles.transformOrigin = this.currentStepAnimates[this.next].config.transformOrigin
|
||||||
|
this.next++
|
||||||
|
// #endif
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
run(fn) {
|
||||||
|
// #ifndef APP-NVUE
|
||||||
|
this.$.animationData = this.animation.export()
|
||||||
|
this.$.timer = setTimeout(() => {
|
||||||
|
typeof fn === 'function' && fn()
|
||||||
|
}, this.$.durationTime)
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-NVUE
|
||||||
|
this.isEnd = false
|
||||||
|
let ref = this.$.$refs['ani'] && this.$.$refs['ani'].ref
|
||||||
|
if(!ref) return
|
||||||
|
this._nvueNextAnimate(this.currentStepAnimates, 0, fn)
|
||||||
|
this.next = 0
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const animateTypes1 = ['matrix', 'matrix3d', 'rotate', 'rotate3d', 'rotateX', 'rotateY', 'rotateZ', 'scale', 'scale3d',
|
||||||
|
'scaleX', 'scaleY', 'scaleZ', 'skew', 'skewX', 'skewY', 'translate', 'translate3d', 'translateX', 'translateY',
|
||||||
|
'translateZ'
|
||||||
|
]
|
||||||
|
const animateTypes2 = ['opacity', 'backgroundColor']
|
||||||
|
const animateTypes3 = ['width', 'height', 'left', 'right', 'top', 'bottom']
|
||||||
|
animateTypes1.concat(animateTypes2, animateTypes3).forEach(type => {
|
||||||
|
MPAnimation.prototype[type] = function(...args) {
|
||||||
|
// #ifndef APP-NVUE
|
||||||
|
this.animation[type](...args)
|
||||||
|
// #endif
|
||||||
|
// #ifdef APP-NVUE
|
||||||
|
this._nvuePushAnimates(type, args)
|
||||||
|
// #endif
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export function createAnimation(option, _this) {
|
||||||
|
if(!_this) return
|
||||||
|
clearTimeout(_this.timer)
|
||||||
|
return new MPAnimation(option, _this)
|
||||||
|
}
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
<template>
|
||||||
|
<!-- #ifndef APP-NVUE -->
|
||||||
|
<view v-show="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
|
||||||
|
<!-- #endif -->
|
||||||
|
<!-- #ifdef APP-NVUE -->
|
||||||
|
<view v-if="isShow" ref="ani" :animation="animationData" :class="customClass" :style="transformStyles" @click="onClick"><slot></slot></view>
|
||||||
|
<!-- #endif -->
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { createAnimation } from './createAnimation'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transition 过渡动画
|
||||||
|
* @description 简单过渡动画组件
|
||||||
|
* @tutorial https://ext.dcloud.net.cn/plugin?id=985
|
||||||
|
* @property {Boolean} show = [false|true] 控制组件显示或隐藏
|
||||||
|
* @property {Array|String} modeClass = [fade|slide-top|slide-right|slide-bottom|slide-left|zoom-in|zoom-out] 过渡动画类型
|
||||||
|
* @value fade 渐隐渐出过渡
|
||||||
|
* @value slide-top 由上至下过渡
|
||||||
|
* @value slide-right 由右至左过渡
|
||||||
|
* @value slide-bottom 由下至上过渡
|
||||||
|
* @value slide-left 由左至右过渡
|
||||||
|
* @value zoom-in 由小到大过渡
|
||||||
|
* @value zoom-out 由大到小过渡
|
||||||
|
* @property {Number} duration 过渡动画持续时间
|
||||||
|
* @property {Object} styles 组件样式,同 css 样式,注意带’-‘连接符的属性需要使用小驼峰写法如:`backgroundColor:red`
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'uniTransition',
|
||||||
|
emits:['click','change'],
|
||||||
|
props: {
|
||||||
|
show: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
modeClass: {
|
||||||
|
type: [Array, String],
|
||||||
|
default() {
|
||||||
|
return 'fade'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
duration: {
|
||||||
|
type: Number,
|
||||||
|
default: 300
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
type: Object,
|
||||||
|
default() {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
customClass:{
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
onceRender:{
|
||||||
|
type:Boolean,
|
||||||
|
default:false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isShow: false,
|
||||||
|
transform: '',
|
||||||
|
opacity: 1,
|
||||||
|
animationData: {},
|
||||||
|
durationTime: 300,
|
||||||
|
config: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
show: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (newVal) {
|
||||||
|
this.open()
|
||||||
|
} else {
|
||||||
|
// 避免上来就执行 close,导致动画错乱
|
||||||
|
if (this.isShow) {
|
||||||
|
this.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 生成样式数据
|
||||||
|
stylesObject() {
|
||||||
|
let styles = {
|
||||||
|
...this.styles,
|
||||||
|
'transition-duration': this.duration / 1000 + 's'
|
||||||
|
}
|
||||||
|
let transform = ''
|
||||||
|
for (let i in styles) {
|
||||||
|
let line = this.toLine(i)
|
||||||
|
transform += line + ':' + styles[i] + ';'
|
||||||
|
}
|
||||||
|
return transform
|
||||||
|
},
|
||||||
|
// 初始化动画条件
|
||||||
|
transformStyles() {
|
||||||
|
return 'transform:' + this.transform + ';' + 'opacity:' + this.opacity + ';' + this.stylesObject
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 动画默认配置
|
||||||
|
this.config = {
|
||||||
|
duration: this.duration,
|
||||||
|
timingFunction: 'ease',
|
||||||
|
transformOrigin: '50% 50%',
|
||||||
|
delay: 0
|
||||||
|
}
|
||||||
|
this.durationTime = this.duration
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* ref 触发 初始化动画
|
||||||
|
*/
|
||||||
|
init(obj = {}) {
|
||||||
|
if (obj.duration) {
|
||||||
|
this.durationTime = obj.duration
|
||||||
|
}
|
||||||
|
this.animation = createAnimation(Object.assign(this.config, obj),this)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 点击组件触发回调
|
||||||
|
*/
|
||||||
|
onClick() {
|
||||||
|
this.$emit('click', {
|
||||||
|
detail: this.isShow
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* ref 触发 动画分组
|
||||||
|
* @param {Object} obj
|
||||||
|
*/
|
||||||
|
step(obj, config = {}) {
|
||||||
|
if (!this.animation) return
|
||||||
|
for (let i in obj) {
|
||||||
|
try {
|
||||||
|
if(typeof obj[i] === 'object'){
|
||||||
|
this.animation[i](...obj[i])
|
||||||
|
}else{
|
||||||
|
this.animation[i](obj[i])
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`方法 ${i} 不存在`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.animation.step(config)
|
||||||
|
return this
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* ref 触发 执行动画
|
||||||
|
*/
|
||||||
|
run(fn) {
|
||||||
|
if (!this.animation) return
|
||||||
|
this.animation.run(fn)
|
||||||
|
},
|
||||||
|
// 开始过度动画
|
||||||
|
open() {
|
||||||
|
clearTimeout(this.timer)
|
||||||
|
this.transform = ''
|
||||||
|
this.isShow = true
|
||||||
|
let { opacity, transform } = this.styleInit(false)
|
||||||
|
if (typeof opacity !== 'undefined') {
|
||||||
|
this.opacity = opacity
|
||||||
|
}
|
||||||
|
this.transform = transform
|
||||||
|
// 确保动态样式已经生效后,执行动画,如果不加 nextTick ,会导致 wx 动画执行异常
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// TODO 定时器保证动画完全执行,目前有些问题,后面会取消定时器
|
||||||
|
this.timer = setTimeout(() => {
|
||||||
|
this.animation = createAnimation(this.config, this)
|
||||||
|
this.tranfromInit(false).step()
|
||||||
|
this.animation.run()
|
||||||
|
this.$emit('change', {
|
||||||
|
detail: this.isShow
|
||||||
|
})
|
||||||
|
}, 20)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 关闭过度动画
|
||||||
|
close(type) {
|
||||||
|
if (!this.animation) return
|
||||||
|
this.tranfromInit(true)
|
||||||
|
.step()
|
||||||
|
.run(() => {
|
||||||
|
this.isShow = false
|
||||||
|
this.animationData = null
|
||||||
|
this.animation = null
|
||||||
|
let { opacity, transform } = this.styleInit(false)
|
||||||
|
this.opacity = opacity || 1
|
||||||
|
this.transform = transform
|
||||||
|
this.$emit('change', {
|
||||||
|
detail: this.isShow
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 处理动画开始前的默认样式
|
||||||
|
styleInit(type) {
|
||||||
|
let styles = {
|
||||||
|
transform: ''
|
||||||
|
}
|
||||||
|
let buildStyle = (type, mode) => {
|
||||||
|
if (mode === 'fade') {
|
||||||
|
styles.opacity = this.animationType(type)[mode]
|
||||||
|
} else {
|
||||||
|
styles.transform += this.animationType(type)[mode] + ' '
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof this.modeClass === 'string') {
|
||||||
|
buildStyle(type, this.modeClass)
|
||||||
|
} else {
|
||||||
|
this.modeClass.forEach(mode => {
|
||||||
|
buildStyle(type, mode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return styles
|
||||||
|
},
|
||||||
|
// 处理内置组合动画
|
||||||
|
tranfromInit(type) {
|
||||||
|
let buildTranfrom = (type, mode) => {
|
||||||
|
let aniNum = null
|
||||||
|
if (mode === 'fade') {
|
||||||
|
aniNum = type ? 0 : 1
|
||||||
|
} else {
|
||||||
|
aniNum = type ? '-100%' : '0'
|
||||||
|
if (mode === 'zoom-in') {
|
||||||
|
aniNum = type ? 0.8 : 1
|
||||||
|
}
|
||||||
|
if (mode === 'zoom-out') {
|
||||||
|
aniNum = type ? 1.2 : 1
|
||||||
|
}
|
||||||
|
if (mode === 'slide-right') {
|
||||||
|
aniNum = type ? '100%' : '0'
|
||||||
|
}
|
||||||
|
if (mode === 'slide-bottom') {
|
||||||
|
aniNum = type ? '100%' : '0'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.animation[this.animationMode()[mode]](aniNum)
|
||||||
|
}
|
||||||
|
if (typeof this.modeClass === 'string') {
|
||||||
|
buildTranfrom(type, this.modeClass)
|
||||||
|
} else {
|
||||||
|
this.modeClass.forEach(mode => {
|
||||||
|
buildTranfrom(type, mode)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.animation
|
||||||
|
},
|
||||||
|
animationType(type) {
|
||||||
|
return {
|
||||||
|
fade: type ? 0 : 1,
|
||||||
|
'slide-top': `translateY(${type ? '0' : '-100%'})`,
|
||||||
|
'slide-right': `translateX(${type ? '0' : '100%'})`,
|
||||||
|
'slide-bottom': `translateY(${type ? '0' : '100%'})`,
|
||||||
|
'slide-left': `translateX(${type ? '0' : '-100%'})`,
|
||||||
|
'zoom-in': `scaleX(${type ? 1 : 0.8}) scaleY(${type ? 1 : 0.8})`,
|
||||||
|
'zoom-out': `scaleX(${type ? 1 : 1.2}) scaleY(${type ? 1 : 1.2})`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 内置动画类型与实际动画对应字典
|
||||||
|
animationMode() {
|
||||||
|
return {
|
||||||
|
fade: 'opacity',
|
||||||
|
'slide-top': 'translateY',
|
||||||
|
'slide-right': 'translateX',
|
||||||
|
'slide-bottom': 'translateY',
|
||||||
|
'slide-left': 'translateX',
|
||||||
|
'zoom-in': 'scale',
|
||||||
|
'zoom-out': 'scale'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 驼峰转中横线
|
||||||
|
toLine(name) {
|
||||||
|
return name.replace(/([A-Z])/g, '-$1').toLowerCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
85
uni_modules/uni-transition/package.json
Normal file
85
uni_modules/uni-transition/package.json
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"id": "uni-transition",
|
||||||
|
"displayName": "uni-transition 过渡动画",
|
||||||
|
"version": "1.3.3",
|
||||||
|
"description": "元素的简单过渡动画",
|
||||||
|
"keywords": [
|
||||||
|
"uni-ui",
|
||||||
|
"uniui",
|
||||||
|
"动画",
|
||||||
|
"过渡",
|
||||||
|
"过渡动画"
|
||||||
|
],
|
||||||
|
"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"],
|
||||||
|
"encrypt": [],
|
||||||
|
"platforms": {
|
||||||
|
"cloud": {
|
||||||
|
"tcb": "y",
|
||||||
|
"aliyun": "y",
|
||||||
|
"alipay": "n"
|
||||||
|
},
|
||||||
|
"client": {
|
||||||
|
"App": {
|
||||||
|
"app-vue": "y",
|
||||||
|
"app-nvue": "y"
|
||||||
|
},
|
||||||
|
"H5-mobile": {
|
||||||
|
"Safari": "y",
|
||||||
|
"Android Browser": "y",
|
||||||
|
"微信浏览器(Android)": "y",
|
||||||
|
"QQ浏览器(Android)": "y"
|
||||||
|
},
|
||||||
|
"H5-pc": {
|
||||||
|
"Chrome": "y",
|
||||||
|
"IE": "y",
|
||||||
|
"Edge": "y",
|
||||||
|
"Firefox": "y",
|
||||||
|
"Safari": "y"
|
||||||
|
},
|
||||||
|
"小程序": {
|
||||||
|
"微信": "y",
|
||||||
|
"阿里": "y",
|
||||||
|
"百度": "y",
|
||||||
|
"字节跳动": "y",
|
||||||
|
"QQ": "y"
|
||||||
|
},
|
||||||
|
"快应用": {
|
||||||
|
"华为": "u",
|
||||||
|
"联盟": "u"
|
||||||
|
},
|
||||||
|
"Vue": {
|
||||||
|
"vue2": "y",
|
||||||
|
"vue3": "y"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
uni_modules/uni-transition/readme.md
Normal file
11
uni_modules/uni-transition/readme.md
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
|
||||||
|
## Transition 过渡动画
|
||||||
|
> **组件名:uni-transition**
|
||||||
|
> 代码块: `uTransition`
|
||||||
|
|
||||||
|
|
||||||
|
元素过渡动画
|
||||||
|
|
||||||
|
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-transition)
|
||||||
|
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
|
||||||
Reference in New Issue
Block a user