初始代码
This commit is contained in:
96
pages/my/abouts/index.vue
Normal file
96
pages/my/abouts/index.vue
Normal file
@@ -0,0 +1,96 @@
|
||||
<template>
|
||||
<view class="abouts-v">
|
||||
<view class="head-box u-flex-col">
|
||||
<view class="head-inner">
|
||||
<view class="version">
|
||||
{{sysVersion}}
|
||||
</view>
|
||||
<image src="/static/image/yunzhupaas.png" mode="widthFix" class="head-img"></image>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="content u-p-l-32 u-p-r-32 u-p-t-30 u-font-28">
|
||||
<text>零和万物科技有限公司是一家做零和万物快速开发平台的企业,针对软件传统开发遇到招人难、留人难、用人成本高、技术更新换代快等一系列问题,只需要一套YUNZHUPAAS平台、您遇到的一系列问题就依然而解。
|
||||
YUNZHUPAAS采用主流的两大技术Java/.Net开发,是一套低代码开发平台,可视化开发环境,有拖拽式的代码生成器,灵活的权限配置、SaaS服务,强大的接口对接,随心可变的工作流引擎,一站式开发多端使用Web、Android、IOS、微信小程序,并且有以构建业务流程、逻辑和数据模型等所需的功能;为企业项目节省80%的重回工作,让开发者将重心放在业务逻辑,不必烦恼底层架构设计,可短时间开发出如ERP、OA、CRM、HR、MIS以及电信、银行、政府、企业等各行业的企业应用系统。
|
||||
零和万物科技有限公司以诚信为根本、服务为基础理念,通过持续不断地研发技术创新、强化平台质量和颜值,为企业保驾护航!</text>
|
||||
</view>
|
||||
<view class="copyright">{{copyright}}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import resources from '@/libs/resources.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
logoSrc: resources.banner.loginlogo,
|
||||
copyright: 'Copyright © 2024 零和万物科技有限公司出品',
|
||||
sysVersion: ''
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
this.sysVersion = uni.getStorageSync('sysVersion') || this.define.sysVersion
|
||||
this.copyright = uni.getStorageSync('copyright') || 'Copyright © 2024 零和万物科技有限公司出品'
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.abouts-v {
|
||||
.head-box {
|
||||
height: 308rpx;
|
||||
background: url('@/pages/my/static/image/about-head.png') center no-repeat;
|
||||
background-size: 100% 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.head-inner {
|
||||
position: relative;
|
||||
|
||||
.head-img {
|
||||
width: 212rpx;
|
||||
height: 60rpx;
|
||||
margin-top: 20rpx;
|
||||
}
|
||||
|
||||
.version {
|
||||
position: absolute;
|
||||
background-color: #FFFFFF;
|
||||
color: #0F5BD2;
|
||||
padding: 0 8rpx;
|
||||
border-radius: 20rpx 0rpx 20rpx 0rpx;
|
||||
top: -34rpx;
|
||||
left: 218rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.abouts-hd {
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
background-color: #3281ff;
|
||||
height: 280rpx;
|
||||
color: #FFFFFF;
|
||||
padding-top: 20rpx;
|
||||
|
||||
image {
|
||||
width: 160rpx;
|
||||
height: 160rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.copyright {
|
||||
height: 80rpx;
|
||||
bottom: 0;
|
||||
line-height: 80rpx;
|
||||
|
||||
}
|
||||
|
||||
.content {
|
||||
height: calc(100vh - 486rpx);
|
||||
overflow-y: scroll;
|
||||
line-height: 48rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
30
pages/my/accountSecurity/index.vue
Normal file
30
pages/my/accountSecurity/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<view class="accountSecurity-v">
|
||||
<u-cell-group>
|
||||
<u-cell-item title="注销账号" @click="openPage('/pages/my/cancellation/index')"></u-cell-item>
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openPage(path) {
|
||||
uni.navigateTo({
|
||||
url: path
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
</style>
|
||||
238
pages/my/business/index.vue
Normal file
238
pages/my/business/index.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<view class="page_v u-flex-col">
|
||||
<view class="lists_box" v-if="show" v-for="(item,index) in list" :key="index"
|
||||
:class="item.isDefault ? 'active' : '' " @click="clickRadio(item)">
|
||||
<view class="icon-checked-box" v-if="item.isDefault">
|
||||
<text>{{majorType === 'Organize' ? '默认' : '主岗'}}</text>
|
||||
<view class="icon-checked">
|
||||
<u-icon name="checkbox-mark" color="#fff" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="list_inner">
|
||||
<text class="icon-ym"
|
||||
:class="majorType === 'Organize' ? 'icon-ym-organization' : 'icon-ym-wf-outgoingApply'"></text>
|
||||
<text class="txt">{{item.fullName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!show" class="notData-box u-flex-col">
|
||||
<view class="u-flex-col notData-inner">
|
||||
<image :src="icon" mode="" class="iconImg"></image>
|
||||
<text class="notData-inner-text">{{$t('common.noData')}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getUserOrganizes,
|
||||
getUserPositions,
|
||||
setMajor,
|
||||
getCurrentUser
|
||||
} from '@/api/common.js'
|
||||
import resources from '@/libs/resources.js'
|
||||
import {
|
||||
useUserStore
|
||||
} from '@/store/modules/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
list: [],
|
||||
organizeList: [],
|
||||
majorType: '',
|
||||
icon: resources.message.nodata,
|
||||
oldVal: "",
|
||||
show: true,
|
||||
disabled: false
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
this.majorType = e.majorType
|
||||
this.getUserOrganizes(this.majorType)
|
||||
let title = this.majorType === 'Organize' ? this.$t('app.my.organization') : this.$t('app.my.position')
|
||||
uni.setNavigationBarTitle({
|
||||
title: title
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
getUserOrganizes(majorType) {
|
||||
let method = majorType === 'Organize' ? getUserOrganizes : getUserPositions
|
||||
method().then(res => {
|
||||
let data = res.data || []
|
||||
if (data.length === []) {
|
||||
this.show = this.list.length > 0 ? true : false
|
||||
return
|
||||
}
|
||||
this.organizeList = JSON.parse(JSON.stringify(data));
|
||||
this.list = this.organizeList;
|
||||
this.list.map(o => {
|
||||
if (o.isDefault) {
|
||||
this.value = o.id
|
||||
this.oldVal = o.id
|
||||
return
|
||||
}
|
||||
})
|
||||
this.show = this.list.length > 0 ? true : false
|
||||
})
|
||||
},
|
||||
clickRadio(item) {
|
||||
if (this.disabled || item.isDefault) return
|
||||
this.changeMajor(item.id)
|
||||
},
|
||||
change(id) {
|
||||
this.value = id
|
||||
this.list.map((o, i) => {
|
||||
o.isDefault = false;
|
||||
if (o.id === id) o.isDefault = true;
|
||||
})
|
||||
},
|
||||
changeMajor(majorId) {
|
||||
let query = {
|
||||
majorId,
|
||||
majorType: this.majorType
|
||||
}
|
||||
setMajor(query).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.value = majorId
|
||||
this.disabled = true
|
||||
this.$u.toast('修改成功')
|
||||
this.$nextTick(() => {
|
||||
this.change(majorId)
|
||||
})
|
||||
this.getCurrentUser()
|
||||
}
|
||||
}).catch(() => {
|
||||
this.value = this.oldVal
|
||||
})
|
||||
},
|
||||
getCurrentUser() {
|
||||
const userStore = useUserStore()
|
||||
userStore.getCurrentUser().then(() => {
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 500)
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.page_v {
|
||||
/* #ifdef MP */
|
||||
background-color: #f0f2f6;
|
||||
/* #endif */
|
||||
height: calc(100vh - 88rpx);
|
||||
padding: 0 20rpx;
|
||||
|
||||
.notData-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
.notData-inner {
|
||||
width: 280rpx;
|
||||
height: 308rpx;
|
||||
align-items: center;
|
||||
|
||||
.iconImg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.notData-inner-text {
|
||||
padding: 30rpx 0;
|
||||
color: #909399;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.active {
|
||||
border: 1rpx solid #2979FF;
|
||||
color: #2979FF;
|
||||
|
||||
.icon-ym-organization {
|
||||
&::before {
|
||||
color: #2979FF !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lists_box {
|
||||
width: 100%;
|
||||
height: 180rpx;
|
||||
border-radius: 8rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background-color: #FFFFFF;
|
||||
margin-top: 20rpx;
|
||||
|
||||
.icon-checked-box {
|
||||
display: flex;
|
||||
width: 140rpx;
|
||||
height: 80rpx;
|
||||
position: absolute;
|
||||
transform: scale(0.9);
|
||||
right: -4rpx;
|
||||
bottom: -2rpx;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.icon-checked {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border: 40rpx solid #1890ff;
|
||||
border-left: 40rpx solid transparent;
|
||||
border-top: 40rpx solid transparent;
|
||||
border-bottom-right-radius: 12rpx;
|
||||
position: absolute;
|
||||
transform: scale(0.95);
|
||||
right: -8rpx;
|
||||
bottom: -6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.list_inner {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 40rpx;
|
||||
align-items: center;
|
||||
|
||||
.icon-ym-wf-outgoingApply {
|
||||
&::before {
|
||||
margin-right: 6rpx;
|
||||
font-size: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-ym-organization {
|
||||
&::before {
|
||||
margin-right: 6rpx;
|
||||
font-size: 40rpx;
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
|
||||
.txt_icon {}
|
||||
|
||||
.txt {
|
||||
width: 100%;
|
||||
align-items: flex-end;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
124
pages/my/cancellation/index.vue
Normal file
124
pages/my/cancellation/index.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<view class="cancellation-v">
|
||||
<view class="cancellation-hd">
|
||||
<image :src="accountSecurity"></image>
|
||||
</view>
|
||||
<view class="content u-flex-col">
|
||||
<view class="content-text u-flex-col">
|
||||
<text class="content-title u-font-36 u-type-primary">确认注销账户?</text>
|
||||
<text class="content-tip u-font-28">注销账户后以下数据将全部清空</text>
|
||||
<view class="list u-flex-col u-font-26">
|
||||
<text class="item">企业组织架构和员工信息</text>
|
||||
<text class="item">所有数据和聊天记录</text>
|
||||
<text class="item">删除和永久注销YUNZHUPAAS账户</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="btn">
|
||||
<u-button type="primary" @click="handleClick">注销账号</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
accountCancel,
|
||||
logout
|
||||
} from '@/api/common.js'
|
||||
import resources from '@/libs/resources.js'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
accountSecurity: resources.banner.accountSecurity
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
token() {
|
||||
return uni.getStorageSync('token')
|
||||
},
|
||||
userInfo() {
|
||||
return uni.getStorageSync('userInfo') || {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '您的YUNZHUPAAS账号将被删除,您确定要注销YUNZHUPAAS账号么?',
|
||||
success: res => {
|
||||
if (res.confirm) {
|
||||
if (this.userInfo.isAdministrator) return this.$u.toast('管理员账号不能注销')
|
||||
accountCancel(this.token).then((res) => {
|
||||
this.$u.toast(res.msg)
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.cancellation-v {
|
||||
.cancellation-hd {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.content-text {
|
||||
justify-content: center;
|
||||
padding: 176rpx 0 0 190rpx;
|
||||
}
|
||||
|
||||
.content-title {
|
||||
height: 100rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.content-tip {
|
||||
height: 80rpx;
|
||||
color: #252B3A;
|
||||
}
|
||||
|
||||
.list {
|
||||
.item {
|
||||
margin-bottom: 35rpx;
|
||||
color: #666;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background-color: #356efe;
|
||||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 0 32rpx;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
bottom: 40rpx;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
193
pages/my/contactUs/index.vue
Normal file
193
pages/my/contactUs/index.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<view class="contacts-v" v-show="show">
|
||||
<view class="contactusBanner">
|
||||
<image :src="contactus" mode="widthFix"></image>
|
||||
</view>
|
||||
<view class="contactus u-flex-col">
|
||||
<view class="u-flex items u-m-b-20" v-for="(item,index) in list" @click="Jump(index)" :key="index">
|
||||
<view :class="item.bcg" style="" class="items-iconBox u-m-r-50 u-padding-15">
|
||||
<u-icon :name="item.icon" color="#ffffff" size="54"></u-icon>
|
||||
</view>
|
||||
<view class="u-flex-col u-flex-1">
|
||||
<text>{{item.name}}</text>
|
||||
<text class="againColor">{{item.title}}</text>
|
||||
</view>
|
||||
<view>
|
||||
<u-icon name="arrow-right" color="#969799" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="serviceTime u-flex-col u-p-l-32 u-p-b-20 u-p-t-20">
|
||||
<text class="u-font-xl" type='title'>服务时间</text>
|
||||
<text class="textSize">工作日:{{workingHours}}</text>
|
||||
<text class="textSize">节假日:{{holidayWorkingHours}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="copyright">{{copyright}}</view>
|
||||
<u-popup v-model="showPopup" mode="center">
|
||||
<view class="center-box">
|
||||
<image class="image" :src="wechat_qrcode" :data-path="wechat_qrcode" @longpress="saveImage" />
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import resources from '@/libs/resources.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
contactus: resources.banner.contactus,
|
||||
wechat_qrcode: resources.common.wechat_qrcode,
|
||||
holidayWorkingHours: '9:30-12:00 ,13:30-17:30',
|
||||
workingHours: '8:30-12:00 ,13:00-20:00',
|
||||
tell: '400-8888-888',
|
||||
url: 'https://www.yunzhupaas.com',
|
||||
showPopup: false,
|
||||
list: [{
|
||||
name: '微信公众号',
|
||||
title: '扫码关注官网微信公众号',
|
||||
icon: 'weixin-fill',
|
||||
bcg: 'u-type-success-bg'
|
||||
},
|
||||
{
|
||||
name: '服务热线',
|
||||
title: '400-8888-888',
|
||||
icon: 'kefu-ermai',
|
||||
bcg: 'u-type-warning-bg'
|
||||
},
|
||||
{
|
||||
name: '官方网站',
|
||||
title: 'www.yunzhupaas.com',
|
||||
icon: 'ie',
|
||||
bcg: 'u-type-primary-bg'
|
||||
}
|
||||
],
|
||||
copyright: 'Copyright © 2024 零和万物科技有限公司出品',
|
||||
show: false
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
uni.showLoading({
|
||||
title: '加载中'
|
||||
});
|
||||
this.copyright = uni.getStorageSync('copyright') || 'Copyright © 2024 零和万物科技有限公司出品'
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
this.show = true
|
||||
}, 800)
|
||||
|
||||
},
|
||||
methods: {
|
||||
Jump(index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
this.showPopup = true
|
||||
break;
|
||||
case 1:
|
||||
uni.makePhoneCall({
|
||||
phoneNumber: this.tell,
|
||||
})
|
||||
break;
|
||||
case 2:
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(this.url);
|
||||
// #endif
|
||||
// #ifndef APP-PLUS
|
||||
uni.navigateTo({
|
||||
url: '/pages/apply/externalLink/index?fullName=零和万物科技有限公司&url=' + encodeURIComponent(
|
||||
this.url)
|
||||
})
|
||||
// #endif
|
||||
break;
|
||||
}
|
||||
},
|
||||
saveImage(e) {
|
||||
// #ifdef APP-PLUS
|
||||
uni.getImageInfo({
|
||||
src: e.currentTarget.dataset.path,
|
||||
success: (res) => {
|
||||
uni.saveImageToPhotosAlbum({
|
||||
filePath: res.path,
|
||||
success: function() {
|
||||
helper.msg('保存成功', 'success');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
width: 100%;
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.contacts-v {
|
||||
width: 100%;
|
||||
|
||||
.contactusBanner {
|
||||
width: 100%;
|
||||
height: 280rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.contactus {
|
||||
margin: 18rpx 16rpx 0;
|
||||
|
||||
.againColor {
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.items {
|
||||
padding: 20rpx 32rpx;
|
||||
background-color: #FFFFFF;
|
||||
justify-content: start;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.items-iconBox {
|
||||
border-radius: 50%;
|
||||
height: 88rpx;
|
||||
width: 88rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.serviceTime {
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.center-box {
|
||||
width: 420rpx;
|
||||
height: 420rpx;
|
||||
|
||||
image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.textSize {
|
||||
height: 66rpx;
|
||||
line-height: 66rpx;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
text[type="title"] {
|
||||
height: 86rpx;
|
||||
line-height: 86rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
294
pages/my/entrustAgent/components/flowSelect/Select.vue
Normal file
294
pages/my/entrustAgent/components/flowSelect/Select.vue
Normal file
@@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<u-popup class="yunzhupaas-tree-select-popup" :maskCloseAble="maskCloseAble" mode="right" :popup="false"
|
||||
v-model="showPopup" :safeAreaInsetBottom="safeAreaInsetBottom" :z-index="uZIndex" width="100%">
|
||||
<view class="yunzhupaas-tree-select-body">
|
||||
<view class="yunzhupaas-tree-select-title">
|
||||
<text class="icon-ym icon-ym-report-icon-preview-pagePre u-font-40 backIcon"
|
||||
@click.stop="handleConfirm"></text>
|
||||
<view class="title">{{$t('app.my.flowSelect')}}</view>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-select-search">
|
||||
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
|
||||
:show-action="false" @change="search(swiperCurrent)" bg-color="#f0f2f6" shape="square">
|
||||
</u-search>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-selected">
|
||||
<view class="yunzhupaas-tree-selected-head">
|
||||
<view>{{$t('component.yunzhupaas.common.selected')}}</view>
|
||||
<view v-if="multiple" class="clear-btn" @click="cleanAll">{{$t('component.yunzhupaas.common.clearAll')}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-selected-box">
|
||||
<scroll-view scroll-y="true" style="max-height: 240rpx;">
|
||||
<view class="yunzhupaas-tree-selected-list">
|
||||
<view class="u-selectTag u-selectTag-flow" v-for="(list,index) in selectList" :key="index">
|
||||
<view class="yunzhupaas-tree-selected-content">
|
||||
<view class="name-box">
|
||||
<view class="name">{{list.fullName}}</view>
|
||||
<u-icon name="close" class="close" @click='delSelect(index)'></u-icon>
|
||||
</view>
|
||||
<view class="organize">{{list.organize}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sticky-tabs">
|
||||
<u-tabs ref="tabs" :list="tabsList" :current="tabIndex" @change="tabChange" :is-scroll="true"
|
||||
name="fullName">
|
||||
</u-tabs>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-select-tree">
|
||||
<scroll-view :style="{height: '100%'}" :refresher-enabled="false" :refresher-threshold="100"
|
||||
:scroll-with-animation='true' :refresher-triggered="triggered" @scrolltolower="handleScrollToLower"
|
||||
:scroll-y="true">
|
||||
<view class="lists_box list_top">
|
||||
<view class="list-cell-txt" v-for="(list,index) in list" :key="index"
|
||||
@click="handleNodeClick(list)">
|
||||
<view class="u-font-30 content">
|
||||
<view class="nameSty">{{list.fullName}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="list.length<1" class="nodata u-flex-col">
|
||||
<image :src="noDataIcon" mode="widthFix" class="noDataIcon"></image>
|
||||
{{$t('common.noData')}}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="yunzhupaas-tree-select-actions">
|
||||
<u-button class="buttom-btn" @click.stop="handleConfirm">{{$t('common.cancelText')}}</u-button>
|
||||
<u-button class="buttom-btn" type="primary"
|
||||
@click.stop="handleConfirm">{{$t('common.okText')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const defaultProps = {
|
||||
label: 'fullName',
|
||||
value: 'id',
|
||||
}
|
||||
import resources from '@/libs/resources.js'
|
||||
import {
|
||||
getFlowSelector
|
||||
} from '@/api/workFlow/flowEngine.js'
|
||||
import {
|
||||
useBaseStore
|
||||
} from '@/store/modules/base'
|
||||
|
||||
const baseStore = useBaseStore()
|
||||
export default {
|
||||
props: {
|
||||
selectType: {
|
||||
type: String,
|
||||
default: 'all'
|
||||
},
|
||||
selectedData: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 弹出的z-index值
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否允许通过点击遮罩关闭Picker
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 顶部标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isAuthority: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
noDataIcon: resources.message.nodata,
|
||||
tabWidth: 150,
|
||||
tabIndex: 0,
|
||||
tabsList: [],
|
||||
keyword: '',
|
||||
selectList: [],
|
||||
list: [],
|
||||
// 因为内部的滑动机制限制,请将tabs组件和swiper组件的current用不同变量赋值
|
||||
current: 0, // tabs组件的current值,表示当前活动的tab选项
|
||||
swiperCurrent: 0, // swiper组件的current值,表示当前那个swiper-item是活动的
|
||||
pagination: {
|
||||
currentPage: 1,
|
||||
pageSize: 20
|
||||
},
|
||||
total: 0,
|
||||
categoryId: '',
|
||||
triggered: false,
|
||||
moving: false,
|
||||
showPopup: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 在select弹起的时候,重新初始化所有数据
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.showPopup = val
|
||||
if (val) setTimeout(() => this.init(), 10);
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
uZIndex() {
|
||||
// 如果用户有传递z-index值,优先使用
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
},
|
||||
},
|
||||
created() {
|
||||
setTimeout(() => {
|
||||
this.triggered = true;
|
||||
}, 1000)
|
||||
baseStore.getDictionaryData({
|
||||
sort: 'businessType'
|
||||
}).then(res => {
|
||||
this.tabsList.push({
|
||||
id: 0,
|
||||
encode: "all",
|
||||
fullName: "全部流程",
|
||||
})
|
||||
this.tabsList.push(...res)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.upCallback()
|
||||
this.selectList = JSON.parse(JSON.stringify(this.selectedData)) || []
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tabs.init()
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
delSelect(index) {
|
||||
this.selectList.splice(index, 1);
|
||||
},
|
||||
cleanAll() {
|
||||
this.selectList = [];
|
||||
},
|
||||
handleNodeClick(obj) {
|
||||
if (!this.multiple) this.selectList = []
|
||||
var isExist = false;
|
||||
for (var i = 0; i < this.selectList.length; i++) {
|
||||
if (this.selectList[i].id == obj.id) {
|
||||
isExist = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
!isExist && this.selectList.push(obj);
|
||||
},
|
||||
//父组件调用
|
||||
resetData() {
|
||||
this.list = []
|
||||
this.pagination = {
|
||||
currentPage: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
},
|
||||
search(index) {
|
||||
this.pagination.currentPage = 1
|
||||
this.searchTimer && clearTimeout(this.searchTimer)
|
||||
this.searchTimer = setTimeout(() => {
|
||||
this.list = [];
|
||||
this.upCallback()
|
||||
}, 300)
|
||||
},
|
||||
// 切换菜单
|
||||
tabChange(index) {
|
||||
this.tabIndex = index
|
||||
this.pagination.currentPage = 1
|
||||
this.fullName = this.tabsList[this.tabIndex].fullName
|
||||
this.categoryId = !this.tabsList[this.tabIndex].id ? '' : this.tabsList[this.tabIndex].id
|
||||
this.list = [];
|
||||
this.upCallback()
|
||||
},
|
||||
handleScrollToLower() {
|
||||
if (this.pagination.pageSize * this.pagination.currentPage < this.total) {
|
||||
this.pagination.currentPage = this.pagination.currentPage + 1;
|
||||
this.upCallback()
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '没有更多信息啦!',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
upCallback() {
|
||||
let query = {
|
||||
currentPage: this.pagination.currentPage,
|
||||
pageSize: this.pagination.pageSize,
|
||||
keyword: this.keyword,
|
||||
category: this.categoryId ? this.categoryId : "",
|
||||
isAuthority: this.isAuthority == 2 ? 0 : 1,
|
||||
isDelegate: 1
|
||||
}
|
||||
this.loading = false
|
||||
getFlowSelector(query).then(res => {
|
||||
const list = res.data.list || [];
|
||||
list.map((o) => {
|
||||
o.fullName = o.fullName + '/' + o.enCode
|
||||
})
|
||||
this.list = this.list.concat(list);
|
||||
this.pagination = res.data.pagination
|
||||
this.total = this.pagination.total
|
||||
})
|
||||
},
|
||||
handleConfirm() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this.moving) return;
|
||||
// #endif
|
||||
this.keyword = '';
|
||||
this.$emit('confirm', this.selectList);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.lists_box {
|
||||
height: 100%;
|
||||
|
||||
.list-cell-txt {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 20rpx 32rpx;
|
||||
overflow: hidden;
|
||||
color: $u-content-color;
|
||||
font-size: 28rpx;
|
||||
line-height: 48rpx;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
116
pages/my/entrustAgent/components/flowSelect/index.vue
Normal file
116
pages/my/entrustAgent/components/flowSelect/index.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<view class="yunzhupaas-tree-select">
|
||||
<u-input input-align='right' type="select" :select-open="selectShow" v-model="innerValue"
|
||||
:placeholder="placeholder" @click="openSelect"></u-input>
|
||||
<flowSelect v-model="selectShow" @confirm="selectConfirm" :multiple="multiple" :selectedData="selectedData"
|
||||
:clearable="clearable" ref="flowSelect" :toUserId="toUserId" :isAuthority='current'>
|
||||
</flowSelect>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import flowSelect from './Select.vue';
|
||||
import {
|
||||
getFlowEngineListByIds
|
||||
} from '@/api/workFlow/flowEngine.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
flowSelect
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
default: ''
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
toUserId: {
|
||||
type: [String, Array],
|
||||
default: ''
|
||||
},
|
||||
current: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectShow: false,
|
||||
innerValue: '',
|
||||
selectedData: [],
|
||||
selectedIds: [],
|
||||
delegateUser: '',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(val) {
|
||||
if (!val || !val.length) return this.innerValue = ''
|
||||
this.setDefault(val)
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDefault(id) {
|
||||
if (!id || !id.length) return this.innerValue = ''
|
||||
getFlowEngineListByIds(id).then(res => {
|
||||
let data = res.data || []
|
||||
this.innerValue = data.map(o => o.fullName).join()
|
||||
this.selectedData = data.map(o => {
|
||||
return {
|
||||
...o,
|
||||
fullName: o.fullName + '/' + o.enCode,
|
||||
};
|
||||
});
|
||||
})
|
||||
},
|
||||
openSelect() {
|
||||
if (this.disabled) return
|
||||
if (!this.toUserId.length) return this.$u.toast('请先选择受委托人');
|
||||
this.selectShow = true
|
||||
this.$refs.flowSelect.resetData()
|
||||
this.setDefault()
|
||||
},
|
||||
selectConfirm(e) {
|
||||
this.selectShow = false
|
||||
this.selectedData = e;
|
||||
let label = ''
|
||||
let value = []
|
||||
this.defaultValue = []
|
||||
for (let i = 0; i < e.length; i++) {
|
||||
label += (i ? ',' : '') + e[i].fullName
|
||||
value.push(e[i].id)
|
||||
}
|
||||
this.defaultValue = value
|
||||
this.innerValue = label
|
||||
if (!this.multiple) {
|
||||
this.$emit('update:modelValue', value)
|
||||
this.$emit('change', value.join(), e[0])
|
||||
return
|
||||
}
|
||||
this.$emit('update:modelValue', value)
|
||||
this.$emit('change', value, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.yunzhupaas-tree-select {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
512
pages/my/entrustAgent/components/userSelect/Tree.vue
Normal file
512
pages/my/entrustAgent/components/userSelect/Tree.vue
Normal file
@@ -0,0 +1,512 @@
|
||||
<template>
|
||||
<u-popup class="yunzhupaas-tree-select-popup" :maskCloseAble="maskCloseAble" mode="right" v-model="showPopup"
|
||||
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" width="100%">
|
||||
<view class="yunzhupaas-tree-select-body">
|
||||
<view class="yunzhupaas-tree-select-title">
|
||||
<text class="icon-ym icon-ym-report-icon-preview-pagePre u-font-40 backIcon" @tap="close"></text>
|
||||
<view class="title">选择用户</view>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-select-search">
|
||||
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
|
||||
:show-action="false" @change="search(swiperCurrent)" bg-color="#f0f2f6" shape="square">
|
||||
</u-search>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-selected">
|
||||
<view class="yunzhupaas-tree-selected-head">
|
||||
<view>{{$t('component.yunzhupaas.common.selected')}}</view>
|
||||
<view v-if="multiple" class="clear-btn" @click="cleanAll">
|
||||
{{$t('component.yunzhupaas.common.clearAll')}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="yunzhupaas-tree-selected-box">
|
||||
<scroll-view scroll-y="true" style="max-height: 240rpx;">
|
||||
<view class="yunzhupaas-tree-selected-list">
|
||||
<view class="u-selectTag" v-for="(list,index) in selectList" :key="index">
|
||||
<u-avatar class="avatar" :src="baseURL+list.headIcon" mode="circle"
|
||||
size="mini"></u-avatar>
|
||||
<view class="yunzhupaas-tree-selected-content">
|
||||
<view class="name-box">
|
||||
<view class="name">{{list.fullName}}</view>
|
||||
<u-icon name="close" class="close" @click='delSelect(index)'></u-icon>
|
||||
</view>
|
||||
<view class="organize">{{list.organize}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="listTitle" v-if="scope != '1'">全部数据</view>
|
||||
<view class="yunzhupaas-user-content" v-if="scope == '1'">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<u-tabs-swiper activeColor="#1890ff" ref="tabs" :list="tabsList" :current="current" @change="change"
|
||||
:is-scroll="false" :show-bar="false"></u-tabs-swiper>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<u-tabs-swiper activeColor="#1890ff" ref="tabs" :list="tabsList" :current="current" @change="change"
|
||||
:is-scroll="false"></u-tabs-swiper>
|
||||
<!-- #endif -->
|
||||
<swiper :current="swiperCurrent" @transition="transition" @animationfinish="animationfinish"
|
||||
class="swiper-box">
|
||||
<swiper-item>
|
||||
<scroll-view :scroll-y="true" class="scroll-view">
|
||||
<ly-tree ref="tree" :node-key="realProps.value" :expand-on-click-node="true"
|
||||
:tree-data="options0" check-on-click-node :show-checkbox="false"
|
||||
:default-expand-all="false" :highlight-current="true" @node-click="handleNodeClick"
|
||||
:props="realProps" :show-node-icon="true" :show-radio="false" :load="loadNode" lazy />
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
|
||||
<swiper-item>
|
||||
<scroll-view :scroll-y="true" class="scroll-view">
|
||||
<ly-tree ref="tree" :node-key="realProps.value" :expand-on-click-node="true"
|
||||
check-on-click-node :show-checkbox="false" :default-expand-all="false"
|
||||
:highlight-current="true" @node-click="handleNodeClick" :props="realProps"
|
||||
:show-node-icon="true" :show-radio="false" :tree-data="options" />
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
|
||||
<swiper-item>
|
||||
<scroll-view :scroll-y="true" class="scroll-view">
|
||||
<ly-tree ref="tree" :node-key="realProps.value" :expand-on-click-node="true"
|
||||
check-on-click-node :show-checkbox="false" :default-expand-all="false"
|
||||
:highlight-current="true" @node-click="handleNodeClick" :props="realProps"
|
||||
:show-node-icon="true" :show-radio="false" :tree-data="options" />
|
||||
</scroll-view>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
<view v-else class="yunzhupaas-tree-select-tree">
|
||||
<scroll-view class="scroll-view" :refresher-enabled="false" :refresher-threshold="100"
|
||||
:scroll-with-animation='true' :refresher-triggered="triggered" @scrolltolower="handleScrollToLower"
|
||||
:scroll-y="true">
|
||||
<view class="lists_box">
|
||||
<view class="list-cell-txt u-border-bottom" v-for="(list,index) in list" :key="index"
|
||||
@click="onSelect(list)">
|
||||
<u-avatar class="avatar" :src="baseURL+list.headIcon" mode="circle"
|
||||
size="default"></u-avatar>
|
||||
<view class="u-font-30 content">
|
||||
<view>{{list.fullName}}</view>
|
||||
<view class="organize">{{list.organize}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="list.length<1" class="nodata u-flex-col">
|
||||
<image :src="noDataIcon" mode="widthFix" class="noDataIcon"></image>
|
||||
{{$t('common.noData')}}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="yunzhupaas-tree-select-actions">
|
||||
<u-button class="buttom-btn" @click="close()">{{$t('common.cancelText')}}</u-button>
|
||||
<u-button class="buttom-btn" type="primary"
|
||||
@click.stop="handleConfirm">{{$t('common.okText')}}</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
<script>
|
||||
/**
|
||||
* tree-select 树形选择器
|
||||
* @property {Boolean} v-model 布尔值变量,用于控制选择器的弹出与收起
|
||||
* @property {Boolean} safe-area-inset-bottom 是否开启底部安全区适配(默认false)
|
||||
* @property {String} cancel-color 取消按钮的颜色(默认#606266)
|
||||
* @property {String} confirm-color 确认按钮的颜色(默认#2979ff)
|
||||
* @property {String} confirm-text 确认按钮的文字
|
||||
* @property {String} cancel-text 取消按钮的文字
|
||||
* @property {Boolean} mask-close-able 是否允许通过点击遮罩关闭Picker(默认true)
|
||||
* @property {String Number} z-index 弹出时的z-index值(默认10075)
|
||||
* @event {Function} confirm 点击确定按钮,返回当前选择的值
|
||||
*/
|
||||
const defaultProps = {
|
||||
label: 'fullName',
|
||||
value: 'id',
|
||||
icon: 'icon',
|
||||
children: 'children'
|
||||
}
|
||||
import {
|
||||
getUserSelectorNew,
|
||||
getSubordinates,
|
||||
getOrganization,
|
||||
getSelectedUserList,
|
||||
getReceiveUserList
|
||||
} from '@/api/common.js'
|
||||
import resources from '@/libs/resources.js'
|
||||
import LyTree from '@/components/ly-tree/ly-tree.vue'
|
||||
export default {
|
||||
name: "tree-select",
|
||||
components: {
|
||||
LyTree
|
||||
},
|
||||
props: {
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
selectedData: {
|
||||
type: Array,
|
||||
default () {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
// 是否显示边框
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// "取消"按钮的颜色
|
||||
cancelColor: {
|
||||
type: String,
|
||||
default: '#606266'
|
||||
},
|
||||
// "确定"按钮的颜色
|
||||
confirmColor: {
|
||||
type: String,
|
||||
default: '#2979ff'
|
||||
},
|
||||
// 弹出的z-index值
|
||||
zIndex: {
|
||||
type: [String, Number],
|
||||
default: 999
|
||||
},
|
||||
safeAreaInsetBottom: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 是否允许通过点击遮罩关闭Picker
|
||||
maskCloseAble: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
props: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
label: 'fullName',
|
||||
value: 'id',
|
||||
icon: 'icon',
|
||||
children: 'children',
|
||||
isLeaf: 'isLeaf'
|
||||
})
|
||||
},
|
||||
//多选
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// 顶部标题
|
||||
title: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
// 取消按钮的文字
|
||||
cancelText: {
|
||||
type: String,
|
||||
default: '取消'
|
||||
},
|
||||
// 确认按钮的文字
|
||||
confirmText: {
|
||||
type: String,
|
||||
default: '确认'
|
||||
},
|
||||
scope: {
|
||||
type: Number,
|
||||
default: 1
|
||||
},
|
||||
entrustType: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
noDataIcon: resources.message.nodata,
|
||||
triggered: false,
|
||||
moving: false,
|
||||
selectList: [],
|
||||
keyword: '',
|
||||
tabsList: [{
|
||||
name: '全部数据'
|
||||
},
|
||||
{
|
||||
name: '当前组织'
|
||||
},
|
||||
{
|
||||
name: '我的下属'
|
||||
}
|
||||
],
|
||||
current: 0,
|
||||
swiperCurrent: 0,
|
||||
options: [],
|
||||
options0: [],
|
||||
list: [],
|
||||
pagination: {
|
||||
currentPage: 1,
|
||||
pageSize: 20
|
||||
},
|
||||
total: 0,
|
||||
height: 0,
|
||||
showPopup: false,
|
||||
query: {}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
// 在select弹起的时候,重新初始化所有数据
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.showPopup = val
|
||||
this.resetData()
|
||||
if (val) setTimeout(() => this.init(), 10);
|
||||
}
|
||||
},
|
||||
selectedData: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.selectList = val
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
},
|
||||
uZIndex() {
|
||||
// 如果用户有传递z-index值,优先使用
|
||||
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
|
||||
},
|
||||
realProps() {
|
||||
return {
|
||||
...defaultProps,
|
||||
...this.props
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.sysConfigInfo = uni.getStorageSync('sysConfigInfo') || {}
|
||||
this._freshing = false;
|
||||
setTimeout(() => {
|
||||
this.triggered = true;
|
||||
}, 1000)
|
||||
this.resetData()
|
||||
},
|
||||
methods: {
|
||||
handleScrollToLower() {
|
||||
this.getInfoList()
|
||||
},
|
||||
getInfoList() {
|
||||
this.query = {
|
||||
...this.pagination,
|
||||
type: this.sysConfigInfo[this.entrustType === 0 ? 'delegateScope' : 'proxyScope'],
|
||||
keyword: this.keyword
|
||||
}
|
||||
getReceiveUserList(this.query).then(res => {
|
||||
const list = res.data.list;
|
||||
if (!list.length && this.pagination.currentPage != 1) return uni.showToast({
|
||||
title: '没有更多信息啦!',
|
||||
icon: 'none'
|
||||
})
|
||||
this.list = this.list.concat(list);
|
||||
this.pagination.currentPage++
|
||||
})
|
||||
},
|
||||
init() {
|
||||
if (this.scope != '1') this.getInfoList()
|
||||
},
|
||||
onSelect(list) {
|
||||
if (!this.multiple) this.selectList = []
|
||||
let flag = false;
|
||||
for (let i = 0; i < this.selectList.length; i++) {
|
||||
if (this.selectList[i].id === list.id) {
|
||||
flag = true;
|
||||
return
|
||||
}
|
||||
};
|
||||
!flag && this.selectList.push(list)
|
||||
},
|
||||
loadNode(node, resolve) {
|
||||
if (node.level === 0) {
|
||||
getUserSelectorNew(node.level).then(res => {
|
||||
resolve(res.data.list)
|
||||
})
|
||||
} else {
|
||||
getUserSelectorNew(node.data.id).then(res => {
|
||||
const data = res.data.list
|
||||
resolve(data)
|
||||
})
|
||||
}
|
||||
},
|
||||
change(index) {
|
||||
this.swiperCurrent = index;
|
||||
this.keyword = ''
|
||||
if (this.swiperCurrent !== 0) this.handOff(this.swiperCurrent)
|
||||
},
|
||||
handOff(swiperCurrent) {
|
||||
let method = swiperCurrent == 1 ? getOrganization : getSubordinates;
|
||||
method(this.keyword).then(res => {
|
||||
this.options = res.data
|
||||
})
|
||||
},
|
||||
search(index) {
|
||||
this.searchTimer && clearTimeout(this.searchTimer)
|
||||
this.searchTimer = setTimeout(() => {
|
||||
this.pagination = {
|
||||
currentPage: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
if (this.scope == 1) {
|
||||
if (index !== 0) this.handOff(index)
|
||||
getUserSelectorNew(0, this.keyword).then(res => {
|
||||
this.options0 = res.data.list
|
||||
})
|
||||
} else {
|
||||
this.list = []
|
||||
this.getInfoList()
|
||||
}
|
||||
}, 300)
|
||||
},
|
||||
transition({
|
||||
detail: {
|
||||
dx
|
||||
}
|
||||
}) {
|
||||
this.$refs.tabs.setDx(dx);
|
||||
},
|
||||
animationfinish({
|
||||
detail: {
|
||||
current
|
||||
}
|
||||
}) {
|
||||
this.$refs.tabs.setFinishCurrent(current);
|
||||
this.swiperCurrent = current;
|
||||
this.current = current;
|
||||
if (this.swiperCurrent !== 0) this.handOff(this.swiperCurrent)
|
||||
},
|
||||
handleNodeClick(obj) {
|
||||
if (this.swiperCurrent === 0 && obj.data.type !== 'user') return
|
||||
if (!this.multiple) this.selectList = []
|
||||
var isExist = false;
|
||||
for (var i = 0; i < this.selectList.length; i++) {
|
||||
if (this.selectList[i].id == obj.data.id) {
|
||||
isExist = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
!isExist && this.selectList.push(obj.data);
|
||||
},
|
||||
delSelect(index) {
|
||||
this.selectList.splice(index, 1);
|
||||
},
|
||||
cleanAll() {
|
||||
this.selectList = [];
|
||||
},
|
||||
handleConfirm() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this.moving) return;
|
||||
// #endif
|
||||
this.keyword = '';
|
||||
this.current = 0
|
||||
this.swiperCurrent = 0
|
||||
this.$emit('confirm', this.selectList);
|
||||
this.close();
|
||||
},
|
||||
// 标识滑动开始,只有微信小程序才有这样的事件
|
||||
pickstart() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.moving = true;
|
||||
// #endif
|
||||
},
|
||||
// 标识滑动结束
|
||||
pickend() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.moving = false;
|
||||
// #endif
|
||||
},
|
||||
filterNode(value, data) {
|
||||
if (!value) return true;
|
||||
return data[this.realProps.label].indexOf(value) !== -1;
|
||||
},
|
||||
close() {
|
||||
this.$emit('close');
|
||||
},
|
||||
resetData() {
|
||||
this.list = [];
|
||||
this.keyword = ''
|
||||
this.pagination = {
|
||||
currentPage: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.yunzhupaas-user-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.swiper-box {
|
||||
flex: 1;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.listTitle {
|
||||
padding: 10rpx 30rpx;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.scroll-view {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.lists_box {
|
||||
height: 100%;
|
||||
|
||||
.nodata {
|
||||
height: 100%;
|
||||
margin: auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #909399;
|
||||
|
||||
.noDataIcon {
|
||||
width: 300rpx;
|
||||
height: 210rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.list-cell-txt {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 20rpx 32rpx;
|
||||
overflow: hidden;
|
||||
color: $u-content-color;
|
||||
font-size: 28rpx;
|
||||
line-height: 24px;
|
||||
background-color: #fff;
|
||||
|
||||
.content {
|
||||
width: 85%;
|
||||
margin-left: 15rpx;
|
||||
|
||||
.organize {
|
||||
white-space: nowrap;
|
||||
overflow: hidden; //超出的文本隐藏
|
||||
text-overflow: ellipsis
|
||||
}
|
||||
}
|
||||
|
||||
.department {
|
||||
color: #9A9A9A;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
121
pages/my/entrustAgent/components/userSelect/index.vue
Normal file
121
pages/my/entrustAgent/components/userSelect/index.vue
Normal file
@@ -0,0 +1,121 @@
|
||||
<template>
|
||||
<view class="yunzhupaas-tree-select">
|
||||
<u-input input-align='right' type="select" :select-open="selectShow" v-model="innerValue"
|
||||
:placeholder="placeholder" @click="openSelect" v-if="isInput" />
|
||||
<Tree v-model="selectShow" :options="options" :multiple="multiple" :selectedData="selectedData"
|
||||
:clearable="clearable" ref="userTree" @close="handleClose" @confirm="handleConfirm" :scope="scope"
|
||||
:entrustType="entrustType" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Tree from './Tree.vue';
|
||||
import {
|
||||
getUserInfoList
|
||||
} from '@/api/common.js'
|
||||
export default {
|
||||
components: {
|
||||
Tree
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
default: ''
|
||||
},
|
||||
isInput: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择'
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
entrustType: {
|
||||
type: [String, Number],
|
||||
default: 0
|
||||
},
|
||||
scope: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectShow: false,
|
||||
innerValue: '',
|
||||
selectedData: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
handler(val) {
|
||||
this.setDefault()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setDefault() {
|
||||
if (!this.modelValue || !this.modelValue.length) return this.setNullValue();
|
||||
this.selectedData = []
|
||||
const ids = this.multiple ? this.modelValue : [this.modelValue];
|
||||
if (!Array.isArray(ids)) return;
|
||||
getUserInfoList(ids).then(res => {
|
||||
if (!this.modelValue || !this.modelValue.length) return this.setNullValue();
|
||||
const list = res.data.list
|
||||
this.selectedData = list
|
||||
this.innerValue = this.selectedData.map(o => o.fullName).join();
|
||||
})
|
||||
},
|
||||
setNullValue() {
|
||||
this.innerValue = '';
|
||||
this.selectedData = [];
|
||||
},
|
||||
openSelect() {
|
||||
if (this.disabled) return
|
||||
this.selectShow = true
|
||||
},
|
||||
handleClose() {
|
||||
this.selectShow = false
|
||||
},
|
||||
handleConfirm(e) {
|
||||
this.selectedData = e;
|
||||
let label = ''
|
||||
let value = []
|
||||
for (let i = 0; i < e.length; i++) {
|
||||
label += (!label ? '' : ',') + e[i].fullName
|
||||
value.push(e[i].id)
|
||||
}
|
||||
this.defaultValue = value
|
||||
this.innerValue = label
|
||||
if (!this.multiple) {
|
||||
this.$emit('update:modelValue', value[0])
|
||||
this.$emit('change', value[0], e[0])
|
||||
} else {
|
||||
this.$emit('update:modelValue', value)
|
||||
this.$emit('change', value, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.yunzhupaas-tree-select {
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
198
pages/my/entrustAgent/detail.vue
Normal file
198
pages/my/entrustAgent/detail.vue
Normal file
@@ -0,0 +1,198 @@
|
||||
<template>
|
||||
<view class="yunzhupaas-wrap personalData">
|
||||
<view class="u-p-l-20 u-p-r-20 content">
|
||||
<u-form :model="dataForm" :errorType="['toast']" label-width="180" label-align="left" ref="dataForm">
|
||||
<u-form-item :label="titleList[config?.current]" prop='toUserId' required>
|
||||
<view class="txt">
|
||||
{{dataForm.userName}}
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item v-if="config.current == 0 || config.current == 2">
|
||||
<view class="u-flex tag-box">
|
||||
<view v-for="(item,index) in infoList" :key="index" size="mini">{{item.toUserName}}<u-tag
|
||||
class="u-m-l-8" size="mini"
|
||||
:type="item.status=== 0?'info':item.status=== 1?'success':'error'"
|
||||
:text="item.status === 0 ? '待确认' : item.status === 1 ? '已接受' : '已拒绝'" /></view>
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item :label="config.current == 2 ? '代理流程' : '委托流程'">
|
||||
<view class="txt">{{dataForm.flowName}}</view>
|
||||
</u-form-item>
|
||||
<u-form-item label="开始时间" prop='startTime' required>
|
||||
<view class="txt">
|
||||
{{$u.timeFormat(dataForm.startTime,'yyyy-mm-dd hh:MM:ss')}}
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item label="结束时间" prop='endTime' required>
|
||||
<view class="txt">
|
||||
{{$u.timeFormat(dataForm.endTime,'yyyy-mm-dd hh:MM:ss')}}
|
||||
</view>
|
||||
</u-form-item>
|
||||
<u-form-item label="委托说明">
|
||||
<u-input input-align='right' v-model="dataForm.description" type="textarea" placeholder="请输入"
|
||||
disabled />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<view class="flowBefore-actions" v-if="config.confirmStatus != 1 && config.status != 2">
|
||||
<u-button class="buttom-btn" type="primary" @click.stop="jumpForm"
|
||||
v-if="isEdit">{{$t('common.editText')}}</u-button>
|
||||
<template v-if='isAccept'>
|
||||
<u-button class="buttom-btn" type="primary" @click.stop="getResult('accept')">接受</u-button>
|
||||
<u-button class="buttom-btn" @click="getResult('refuse')" type="error">拒绝</u-button>
|
||||
</template>
|
||||
<u-button class="buttom-btn" type="primary" @click.stop="entrustStop" v-if="isStop">终止</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
entrustStop,
|
||||
getPrincipalDetails,
|
||||
entrustHandle,
|
||||
FlowDelegateInfo
|
||||
} from '@/api/workFlow/entrust.js'
|
||||
export default {
|
||||
data() {
|
||||
const data = {
|
||||
dataForm: {
|
||||
id: '',
|
||||
userId: '',
|
||||
toUserId: [],
|
||||
flowId: [],
|
||||
description: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
flowName: '',
|
||||
toUserName: '',
|
||||
type: undefined,
|
||||
},
|
||||
config: {},
|
||||
infoList: [],
|
||||
titleList: ['受委托人', '委托人', '代理人', '被代理人'],
|
||||
}
|
||||
return data
|
||||
},
|
||||
computed: {
|
||||
isAccept() {
|
||||
if (this.config.current == 1 && this.config.confirmStatus == 2) return false
|
||||
if (this.config.current == 0 || this.config.current == 2) return false
|
||||
if (this.config.status == 0 || this.config.status == 1) return true
|
||||
return false
|
||||
},
|
||||
isStop() {
|
||||
return (this.config.current == 0 || this.config.current == 2) && this.config.status == 1
|
||||
},
|
||||
isEdit() {
|
||||
return (this.config.current == 0 || this.config.current == 2) && this.config.status == 0
|
||||
}
|
||||
},
|
||||
onShow() {
|
||||
uni.$on('refreshDetail', () => {
|
||||
this.flowDelegateInfo()
|
||||
})
|
||||
},
|
||||
onUnload() {
|
||||
uni.$emit('refresh')
|
||||
uni.$off('refreshDetail')
|
||||
},
|
||||
onLoad(e) {
|
||||
this.config = JSON.parse(decodeURIComponent(e.data));
|
||||
if (this.config) this.init()
|
||||
},
|
||||
methods: {
|
||||
flowDelegateInfo() {
|
||||
FlowDelegateInfo(this.config.id).then(res => {
|
||||
this.dataForm = res.data || {}
|
||||
this.dataForm.flowId = this.dataForm.flowId ? this.dataForm.flowId.split(",") : [];
|
||||
this.config = {
|
||||
...this.config,
|
||||
...this.dataForm
|
||||
}
|
||||
if (this.dataForm.id) {
|
||||
if (this.config.current == 0 || this.config.current == 2) this.getPrincipalDetails()
|
||||
}
|
||||
})
|
||||
},
|
||||
init() {
|
||||
this.dataForm.id = this.config.id || ''
|
||||
this.dataForm.userName = this.config.userName
|
||||
this.dataForm.flowName = this.config.flowName
|
||||
this.dataForm.description = this.config.description
|
||||
this.dataForm.startTime = this.config.startTime
|
||||
this.dataForm.endTime = this.config.endTime
|
||||
if (this.dataForm.id) {
|
||||
if (this.config.current == 1) this.dataForm = this.config || {}
|
||||
if (this.config.current == 0 || this.config.current == 2) this.getPrincipalDetails()
|
||||
}
|
||||
},
|
||||
getPrincipalDetails() {
|
||||
getPrincipalDetails(this.dataForm.id).then(res => {
|
||||
this.infoList = res.data || []
|
||||
this.dataForm.userName = this.infoList.map(o => o.toUserName).join()
|
||||
})
|
||||
},
|
||||
entrustStop() {
|
||||
let currTime = Math.round(new Date())
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '结束后,流程不再进行委托!',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
entrustStop(this.dataForm.id).then(res => {
|
||||
this.dataForm.endTime = currTime
|
||||
uni.$emit('refresh')
|
||||
uni.navigateBack()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
getResult(entrustType) {
|
||||
let data = {
|
||||
'type': entrustType === 'accept' ? 1 : 2
|
||||
}
|
||||
entrustHandle(this.dataForm.id, data).then(res => {
|
||||
uni.$emit('refresh')
|
||||
uni.navigateBack()
|
||||
})
|
||||
},
|
||||
jumpForm() {
|
||||
let isEdit = this.infoList.some(o => o.status == 1)
|
||||
if (isEdit) return this.$u.toast("已有人接受,不可编辑");
|
||||
uni.navigateTo({
|
||||
url: `/pages/my/entrustAgent/form?data=${encodeURIComponent(JSON.stringify(this.config))}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #fff;
|
||||
|
||||
:deep(.u-form-item) {
|
||||
min-height: 112rpx;
|
||||
}
|
||||
|
||||
.u-form {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.txt {
|
||||
text-align: right;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
268
pages/my/entrustAgent/form.vue
Normal file
268
pages/my/entrustAgent/form.vue
Normal file
@@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<view class="yunzhupaas-wrap personalData">
|
||||
<view class="u-p-l-20 u-p-r-20 content">
|
||||
<u-form :model="dataForm" :errorType="['toast']" label-width="180" label-align="left" ref="dataForm">
|
||||
<u-form-item :label="titleList[current]" prop='toUserId' required>
|
||||
<userSelect v-model="dataForm.toUserId" @change="toChangeUser" :disabled="disabled" multiple
|
||||
:placeholder="$t('common.chooseText')" :scope="scope" :entrustType="type" />
|
||||
</u-form-item>
|
||||
<u-form-item :label="current == 2 ? '代理流程' : '委托流程'">
|
||||
<flowSelect v-model="dataForm.flowId" :toUserId="dataForm.toUserId"
|
||||
:placeholder="$t('app.my.allFlow')" multiple @change="onChange" :disabled="disabled" clearable
|
||||
:current="current" />
|
||||
</u-form-item>
|
||||
<u-form-item label="开始时间" prop='startTime' required>
|
||||
<YunzhupaasDatePicker v-model="dataForm.startTime" :placeholder="$t('common.chooseTextPrefix')"
|
||||
:disabled="disabled" @change="change" format="yyyy-MM-dd HH:mm:ss" />
|
||||
</u-form-item>
|
||||
<u-form-item label="结束时间" prop='endTime' required>
|
||||
<YunzhupaasDatePicker v-model="dataForm.endTime" :placeholder="$t('common.chooseTextPrefix')"
|
||||
@change="change" :disabled="disabled" format="yyyy-MM-dd HH:mm:ss" />
|
||||
</u-form-item>
|
||||
<u-form-item label="委托说明">
|
||||
<u-input input-align='right' v-model="dataForm.description" type="textarea"
|
||||
:placeholder="$t('common.inputTextPrefix')" :disabled="disabled" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<view class="flowBefore-actions">
|
||||
<u-button class="buttom-btn" type="primary" @click.stop="entrustStop"
|
||||
v-if="config.status == 1">{{$t('app.my.stop')}}</u-button>
|
||||
<template v-else>
|
||||
<u-button class="buttom-btn" @click="getResult('cancel')">{{$t('common.cancelText')}}</u-button>
|
||||
<u-button class="buttom-btn" type="primary"
|
||||
@click.stop="getResult('confirm')">{{$t('common.okText')}}</u-button>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
UpdateUser
|
||||
} from '@/api/common'
|
||||
import {
|
||||
Create,
|
||||
Update,
|
||||
FlowDelegateInfo
|
||||
} from '@/api/workFlow/entrust.js'
|
||||
import flowSelect from './components/flowSelect/index.vue'
|
||||
import userSelect from './components/userSelect/index.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
flowSelect,
|
||||
userSelect
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showBtn: false,
|
||||
showctionSheet: false,
|
||||
show: false,
|
||||
props: {
|
||||
label: 'fullName',
|
||||
value: 'enCode'
|
||||
},
|
||||
dataForm: {
|
||||
id: '',
|
||||
toUserId: [],
|
||||
flowId: [],
|
||||
description: '',
|
||||
startTime: '',
|
||||
endTime: '',
|
||||
flowName: '',
|
||||
toUserName: '',
|
||||
type: 0,
|
||||
},
|
||||
typeOptions: [{
|
||||
enCode: "0",
|
||||
fullName: '发起委托'
|
||||
}, {
|
||||
enCode: "1",
|
||||
fullName: '审批委托'
|
||||
}],
|
||||
userInfo: {},
|
||||
current: '1',
|
||||
rules: {
|
||||
toUserId: [{
|
||||
required: true,
|
||||
message: `受委托人不能为空`,
|
||||
trigger: ['change', 'blur'],
|
||||
type: 'array'
|
||||
}],
|
||||
endTime: [{
|
||||
required: true,
|
||||
message: '结束时间不能为空',
|
||||
trigger: 'blur',
|
||||
type: 'number'
|
||||
}],
|
||||
startTime: [{
|
||||
required: true,
|
||||
message: '开始时间不能为空',
|
||||
trigger: 'blur',
|
||||
type: 'number'
|
||||
}]
|
||||
},
|
||||
myNameAccount: '',
|
||||
actionList: [],
|
||||
disabled: false,
|
||||
config: {},
|
||||
scope: 1,
|
||||
titleList: ['受委托人', '委托人', '代理人', '被代理人'],
|
||||
type: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
this.userInfo = uni.getStorageSync('userInfo') || {}
|
||||
if (e.data) this.config = JSON.parse(decodeURIComponent(e?.data));
|
||||
if (this.config) this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.current = this.config.current || 0
|
||||
this.type = this.config.type
|
||||
this.config.status = this.config.status || 0
|
||||
this.disabled =
|
||||
this.config.status == 1 ? true : false
|
||||
this.dataForm.id = this.config.id || ''
|
||||
let {
|
||||
delegateScope,
|
||||
proxyScope
|
||||
} = uni.getStorageSync('sysConfigInfo') || {}
|
||||
this.scope = this.config.type == '0' ? delegateScope : proxyScope
|
||||
uni.setNavigationBarTitle({
|
||||
title: this.dataForm.id ? this.$t('common.editText') : this.$t('common.addText')
|
||||
})
|
||||
this.rules.toUserId[0].message = `${this.type == 1 ? '代理人' : '受委托人'}不能为空`;
|
||||
if (this.current != 2) {
|
||||
this.rules.type = [{
|
||||
required: true,
|
||||
message: '委托类型不能为空',
|
||||
trigger: ['change', 'blur'],
|
||||
type: 'number'
|
||||
}]
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.$refs.dataForm.setRules(this.rules);
|
||||
})
|
||||
//初始化数据
|
||||
if (this.dataForm.id) {
|
||||
this.$nextTick(() => {
|
||||
FlowDelegateInfo(this.dataForm.id).then(res => {
|
||||
this.dataForm = res.data || {}
|
||||
this.dataForm.flowId = this.dataForm.flowId ? this.dataForm.flowId.split(",") :
|
||||
[]
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
entrustStop() {
|
||||
let currTime = Math.round(new Date())
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '结束后,流程不再进行委托!',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
entrustStop(this.dataForm.id).then(res => {
|
||||
this.dataForm.endTime = currTime
|
||||
uni.$emit('refresh')
|
||||
uni.navigateBack()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
onChange(id, listData) {
|
||||
if (listData && listData.length) {
|
||||
let arr = []
|
||||
listData.forEach(item => {
|
||||
arr.push(item.fullName)
|
||||
})
|
||||
this.dataForm.flowName = arr.join(",")
|
||||
} else {
|
||||
this.dataForm.flowName = "全部流程"
|
||||
}
|
||||
},
|
||||
change(val, list) {
|
||||
this.$nextTick(() => {
|
||||
this.$emit('change', this.dataForm)
|
||||
})
|
||||
},
|
||||
toChangeUser(id, selectedData) {
|
||||
this.dataForm.toUserName = selectedData.map(user => user.fullName).join(',');
|
||||
return this.dataForm.toUserName
|
||||
},
|
||||
// 点击确定或者取消
|
||||
getResult(event = null) {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this.moving) return;
|
||||
// #endif
|
||||
this.keyword = '';
|
||||
if (event === 'cancel') return this.close();
|
||||
this.submit()
|
||||
},
|
||||
close() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
submit() {
|
||||
let startTime = this.dataForm.startTime;
|
||||
let endTime = this.dataForm.endTime;
|
||||
this.dataForm.type = this.config.type || 0
|
||||
this.$refs.dataForm.validate(valid => {
|
||||
if (valid) {
|
||||
if (startTime > endTime) return this.$u.toast('开始时间不能大于等于结束时间')
|
||||
const formMethod = this.dataForm.id ? Update : Create
|
||||
let params = {
|
||||
...this.dataForm
|
||||
}
|
||||
params.flowId = this.dataForm.flowId ? this.dataForm.flowId.join(",") : ""
|
||||
if (!params.flowId) params.flowName = "全部流程"
|
||||
formMethod(params).then(res => {
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
complete: () => {
|
||||
setTimeout(() => {
|
||||
uni.$emit('refreshDetail')
|
||||
uni.navigateBack()
|
||||
}, 1500)
|
||||
}
|
||||
});
|
||||
}).catch()
|
||||
}
|
||||
});
|
||||
},
|
||||
onTypeChange() {
|
||||
this.dataForm.flowId = []
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.content {
|
||||
background-color: #fff;
|
||||
|
||||
:deep(.u-form-item) {
|
||||
min-height: 112rpx;
|
||||
}
|
||||
|
||||
.u-form {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tag-box {
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
336
pages/my/entrustAgent/index.vue
Normal file
336
pages/my/entrustAgent/index.vue
Normal file
@@ -0,0 +1,336 @@
|
||||
<template>
|
||||
<view class="flowLaunch-v">
|
||||
<view class="notice-warp">
|
||||
<view class="search-box">
|
||||
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
|
||||
:show-action="false" @change="search" bg-color="#f0f2f6" shape="square" />
|
||||
</view>
|
||||
<u-tabs :list="entrustList" v-model="current" @change="change" :is-scroll='false' name="fullName">
|
||||
</u-tabs>
|
||||
</view>
|
||||
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
|
||||
:up="upOption" :bottombar="false" top="200">
|
||||
<view class="flow-list">
|
||||
<view class="flow-list-box">
|
||||
<uni-swipe-action ref="swipeAction">
|
||||
<uni-swipe-action-item v-for="(item, index) in list" :key="item.id" :threshold="0"
|
||||
:right-options="options" @click="handleClick(index)" :disabled="actionItemDisabled(item)">
|
||||
<view class="item" :id="'item'+index" ref="mydom" @click="goDetail(item)">
|
||||
<view class="item-left">
|
||||
<text class="title u-line-1 u-font-24 u-m-b-18">
|
||||
{{titleList[current]}}:
|
||||
<text
|
||||
class="titInner">{{current == '0' || current == '2' ? item.toUserName : item.userName }}</text>
|
||||
</text>
|
||||
<text class="title u-line-1 u-font-24 u-m-b-18">
|
||||
{{current == 2 ? '代理流程:' :'委托流程:' }}
|
||||
<text class="titInner">{{item.flowName ? item.flowName : ''}}</text>
|
||||
</text>
|
||||
<text class="time title u-font-24 u-m-b-18">
|
||||
开始时间:
|
||||
<text
|
||||
class="titInner">{{item.startTime?$u.timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss'):''}}</text>
|
||||
</text>
|
||||
<text class="time title u-font-24 "
|
||||
:class="current == 1 || current == 3 ?'u-m-b-18': ''">
|
||||
结束时间:
|
||||
<text
|
||||
class="titInner">{{item.endTime?$u.timeFormat(item.endTime, 'yyyy-mm-dd hh:MM:ss'):''}}</text>
|
||||
</text>
|
||||
<view class="time title u-font-24" v-if="current == 1 || current == 3 ">
|
||||
确认状态:
|
||||
<u-tag
|
||||
:type="item.confirmStatus == 0 ? 'info' : item.confirmStatus == 1 ? 'success' : 'error'"
|
||||
:text="item.confirmStatus == 0 ? '待确认' : item.confirmStatus == 1 ? '已接受' : '已拒绝'"
|
||||
size="mini" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="item-right">
|
||||
<image :src="item.entrustStatus.flowStatus" mode="widthFix"
|
||||
class="item-right-img" />
|
||||
</view>
|
||||
</view>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</view>
|
||||
</view>
|
||||
</mescroll-body>
|
||||
<view class="com-addBtn" v-if="current == 0 || current == 2" @click="addPage()">
|
||||
<u-icon name="plus" size="48" color="#fff" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import resources from '@/libs/resources.js'
|
||||
import {
|
||||
FlowDelegateList,
|
||||
DeleteDelagate,
|
||||
getDelegateUser
|
||||
} from '@/api/workFlow/entrust.js'
|
||||
import {
|
||||
getFlowLaunchList,
|
||||
delFlowLaunch
|
||||
} from '@/api/workFlow/template'
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
import flowlist from '../../workFlow/flowTodo/flowList.vue'
|
||||
export default {
|
||||
components: {
|
||||
flowlist
|
||||
},
|
||||
mixins: [MescrollMixin],
|
||||
data() {
|
||||
return {
|
||||
keyword: '',
|
||||
list: [],
|
||||
downOption: {
|
||||
use: true,
|
||||
auto: true
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
num: 0,
|
||||
size: 20,
|
||||
time: null
|
||||
},
|
||||
empty: {
|
||||
use: true,
|
||||
icon: resources.message.nodata,
|
||||
tip: this.$t('common.noData'),
|
||||
fixed: true,
|
||||
top: "450rpx",
|
||||
},
|
||||
textNoMore: this.$t('app.apply.noMoreData'),
|
||||
},
|
||||
entrustList: [{
|
||||
fullName: this.$t('app.my.myEntrust')
|
||||
},
|
||||
{
|
||||
fullName: this.$t('app.my.entrustMe')
|
||||
},
|
||||
{
|
||||
fullName: this.$t('app.my.myAgency')
|
||||
},
|
||||
{
|
||||
fullName: this.$t('app.my.agencyMe')
|
||||
}
|
||||
],
|
||||
titleList: ['受委托人', '委托人', '代理人', '被代理人'],
|
||||
current: 0,
|
||||
options: [{
|
||||
text: '删除',
|
||||
style: {
|
||||
backgroundColor: '#dd524d'
|
||||
}
|
||||
}]
|
||||
}
|
||||
},
|
||||
onLoad(e) {
|
||||
uni.$on('refresh', () => {
|
||||
this.list = [];
|
||||
this.mescroll.resetUpScroll();
|
||||
})
|
||||
if (e.index) this.current = Number(e.index)
|
||||
},
|
||||
onShow() {
|
||||
uni.$on('refreshDetail', () => {
|
||||
this.list = [];
|
||||
this.mescroll.resetUpScroll();
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
actionItemDisabled(item) {
|
||||
if (this.current == 1 || this.current == 3) return true
|
||||
return !(item.status == 0 || item.status == 2)
|
||||
},
|
||||
addPage() {
|
||||
let url = '/entrustAgent/form'
|
||||
const data = {
|
||||
current: this.current,
|
||||
type: this.current == 0 ? 0 : 1
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: `/pages/my${url}?data=${encodeURIComponent(JSON.stringify(data))}`
|
||||
})
|
||||
},
|
||||
handleClick(index) {
|
||||
const item = this.list[index]
|
||||
DeleteDelagate(item.id).then(res => {
|
||||
this.$u.toast(res.msg)
|
||||
this.mescroll.resetUpScroll()
|
||||
})
|
||||
},
|
||||
upCallback(page) {
|
||||
let query = {
|
||||
currentPage: page.num,
|
||||
pageSize: page.size,
|
||||
keyword: this.keyword,
|
||||
type: this.current + 1
|
||||
}
|
||||
FlowDelegateList(query, {
|
||||
load: page.num == 1
|
||||
}).then(res => {
|
||||
this.mescroll.endSuccess(res.data.list.length);
|
||||
if (page.num == 1) this.list = [];
|
||||
// #ifndef APP-HARMONY
|
||||
const list = res.data.list.map(o => ({
|
||||
'entrustStatus': this.getEntrustStatus(o),
|
||||
...o
|
||||
}))
|
||||
// #endif
|
||||
// #ifdef APP-HARMONY
|
||||
const list = res.data.list.map(o =>
|
||||
Object.assign({}, {
|
||||
'entrustStatus': this.getEntrustStatus(o)
|
||||
}, o)
|
||||
);
|
||||
// #endif
|
||||
|
||||
this.list = this.list.concat(list);
|
||||
}).catch(() => {
|
||||
this.mescroll.endErr();
|
||||
})
|
||||
},
|
||||
// 状态
|
||||
getEntrustStatus(o) {
|
||||
let status = o.status;
|
||||
let flowStatus;
|
||||
switch (status) {
|
||||
case 0: //未生效
|
||||
flowStatus = resources.status.notEffective
|
||||
break;
|
||||
case 1: //生效中
|
||||
flowStatus = resources.status.effectuate
|
||||
break;
|
||||
default: //已失效
|
||||
flowStatus = resources.status.expired
|
||||
break;
|
||||
}
|
||||
return {
|
||||
flowStatus,
|
||||
status
|
||||
}
|
||||
},
|
||||
search() {
|
||||
// 节流,避免输入过快多次请求
|
||||
this.searchTimer && clearTimeout(this.searchTimer)
|
||||
this.searchTimer = setTimeout(() => {
|
||||
this.list = [];
|
||||
this.mescroll.resetUpScroll();
|
||||
}, 300)
|
||||
},
|
||||
change(index) {
|
||||
this.keyword = ''
|
||||
this.current = index;
|
||||
this.list = [];
|
||||
this.search()
|
||||
},
|
||||
// 详情
|
||||
goDetail(item) {
|
||||
const data = {
|
||||
...item,
|
||||
current: this.current,
|
||||
type: this.current == 0 ? 0 : 1
|
||||
};
|
||||
uni.navigateTo({
|
||||
url: `/pages/my/entrustAgent/detail?data=${encodeURIComponent(JSON.stringify(data))}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.search_sticky {
|
||||
z-index: 990;
|
||||
position: sticky;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.flowLaunch-v {
|
||||
width: 100%;
|
||||
|
||||
.flow-list-box {
|
||||
width: 95%;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.common-lable {
|
||||
font-size: 24rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #409EFF;
|
||||
border: 1px solid #409EFF;
|
||||
background-color: #e5f3fe;
|
||||
|
||||
}
|
||||
|
||||
.urgent-lable {
|
||||
color: #E6A23C;
|
||||
border: 1px solid #E6A23C;
|
||||
background-color: #fef6e5;
|
||||
}
|
||||
|
||||
.important-lable {
|
||||
color: #F56C6C;
|
||||
border: 1px solid #F56C6C;
|
||||
background-color: #fee5e5;
|
||||
}
|
||||
|
||||
.item-right {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
height: 88rpx;
|
||||
|
||||
.item-right-img {
|
||||
width: 102rpx;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.item-left-top {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: baseline;
|
||||
|
||||
.common-lable {
|
||||
font-size: 24rpx;
|
||||
padding: 2rpx 8rpx;
|
||||
|
||||
border-radius: 8rpx;
|
||||
color: #409EFF;
|
||||
border: 1px solid #409EFF;
|
||||
background-color: #e5f3fe;
|
||||
// margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.urgent-lable {
|
||||
color: #E6A23C;
|
||||
border: 1px solid #E6A23C;
|
||||
background-color: #fef6e5;
|
||||
}
|
||||
|
||||
.important-lable {
|
||||
color: #F56C6C;
|
||||
border: 1px solid #F56C6C;
|
||||
background-color: #fee5e5;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: unset;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
217
pages/my/modifyPsd/index.vue
Normal file
217
pages/my/modifyPsd/index.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<view class="yunzhupaas-wrap yunzhupaas-wrap-workflow">
|
||||
<view class="" style="background-color: #fff;">
|
||||
<u-form :model="dataForm" :rules="rules" ref="dataForm" :errorType="['toast']" label-position="left"
|
||||
label-width="150" label-align="left">
|
||||
<view class="u-p-l-20 u-p-r-20">
|
||||
<u-form-item label="旧密码" prop="oldPassword" required>
|
||||
<u-input v-model="dataForm.oldPassword" placeholder="请输入" type="password"></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="u-p-l-20 u-p-r-20">
|
||||
<u-form-item label="新密码" prop="password" required>
|
||||
<u-input v-model="dataForm.password" placeholder="请输入" type="password"></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="u-p-l-20 u-p-r-20">
|
||||
<u-form-item label="重复密码" prop="repeatPsd" required>
|
||||
<u-input v-model="dataForm.repeatPsd" placeholder="请输入" type="password"></u-input>
|
||||
</u-form-item>
|
||||
</view>
|
||||
<view class="u-p-l-20 u-p-r-20">
|
||||
<u-form-item label="验证码" prop="code" required>
|
||||
<view class="u-flex">
|
||||
<u-input v-model="dataForm.code" placeholder="请输入"></u-input>
|
||||
<view style="flex: 0.1;">
|
||||
<u-image :showLoading="true" :src="baseURL+imgUrl" width="130px" height="38px"
|
||||
@click="changeCode">
|
||||
</u-image>
|
||||
</view>
|
||||
</view>
|
||||
</u-form-item>
|
||||
</view>
|
||||
</u-form>
|
||||
</view>
|
||||
<!-- 底部按钮 -->
|
||||
<view class="flowBefore-actions">
|
||||
<u-button class="buttom-btn" type="primary" @click.stop="dataFormSubmit">保存</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import md5Libs from "@/uni_modules/vk-uview-ui/libs/function/md5";
|
||||
import {
|
||||
updatePassword,
|
||||
getSystemConfig
|
||||
} from "@/api/common.js"
|
||||
import {
|
||||
useUserStore
|
||||
} from '@/store/modules/user'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
var validatePass = (rule, value, callback) => {
|
||||
// const passwordreg = /(?=.*\d)(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{6,16}/
|
||||
//是否包含数字
|
||||
const containsNumbers = /[0-9]+/
|
||||
//是否包含小写字符
|
||||
const includeLowercaseLetters = /[a-z]+/
|
||||
//是否包含大写字符
|
||||
const includeUppercaseLetters = /[A-Z]+/
|
||||
//是否包含字符
|
||||
const containsCharacters = /\W/
|
||||
if (value === '') {
|
||||
callback(new Error('新密码不能为空'));
|
||||
} else if (this.baseForm.passwordStrengthLimit == 1) {
|
||||
if (this.baseForm.passwordLengthMin) {
|
||||
if (value.length < this.baseForm.passwordLengthMinNumber) {
|
||||
callback(new Error('新密码长度不能小于' + this.baseForm.passwordLengthMinNumber + '位'));
|
||||
}
|
||||
}
|
||||
if (this.baseForm.containsNumbers) {
|
||||
if (!containsNumbers.test(value)) {
|
||||
callback(new Error('新密码必须包含数字'));
|
||||
}
|
||||
}
|
||||
if (this.baseForm.includeLowercaseLetters) {
|
||||
if (!includeLowercaseLetters.test(value)) {
|
||||
callback(new Error('新密码必须包含小写字母'));
|
||||
}
|
||||
}
|
||||
if (this.baseForm.includeUppercaseLetters) {
|
||||
if (!includeUppercaseLetters.test(value)) {
|
||||
callback(new Error('新密码必须包含大写字字母'));
|
||||
}
|
||||
}
|
||||
if (this.baseForm.containsCharacters) {
|
||||
if (!containsCharacters.test(value)) {
|
||||
callback(new Error('新密码必须包含字符'));
|
||||
}
|
||||
}
|
||||
callback();
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
var validatePass2 = (rule, value, callback) => {
|
||||
if (value === '') {
|
||||
callback(new Error('重复密码不能为空'));
|
||||
} else if (value !== this.dataForm.password) {
|
||||
callback(new Error('两次密码输入不一致'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
return {
|
||||
imgUrl: '',
|
||||
timestamp: '',
|
||||
dataForm: {
|
||||
oldPassword: '',
|
||||
password: '',
|
||||
repeatPsd: '',
|
||||
code: '',
|
||||
timestamp: ''
|
||||
},
|
||||
baseForm: {
|
||||
passwordStrengthLimit: 0,
|
||||
passwordLengthMin: false,
|
||||
passwordLengthMinNumber: 0,
|
||||
containsNumbers: false,
|
||||
includeLowercaseLetters: false,
|
||||
includeUppercaseLetters: false,
|
||||
containsCharacters: false,
|
||||
},
|
||||
rules: {
|
||||
oldPassword: [{
|
||||
required: true,
|
||||
message: '旧密码不能为空',
|
||||
trigger: 'blur'
|
||||
}],
|
||||
password: [{
|
||||
required: true,
|
||||
validator: validatePass,
|
||||
trigger: 'blur'
|
||||
}],
|
||||
repeatPsd: [{
|
||||
required: true,
|
||||
validator: validatePass2,
|
||||
trigger: 'blur'
|
||||
}],
|
||||
code: [{
|
||||
required: true,
|
||||
message: '验证码不能为空',
|
||||
trigger: 'blur'
|
||||
}]
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.changeCode(),
|
||||
this.initData()
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.dataForm.setRules(this.rules)
|
||||
},
|
||||
methods: {
|
||||
initData() {
|
||||
this.$nextTick(() => {
|
||||
getSystemConfig().then(res => {
|
||||
this.baseForm = res.data
|
||||
this.baseForm.passwordLengthMin = this.baseForm.passwordLengthMin ? true : false
|
||||
this.baseForm.containsNumbers = this.baseForm.containsNumbers ? true : false
|
||||
this.baseForm.includeLowercaseLetters = this.baseForm.includeLowercaseLetters ?
|
||||
true : false
|
||||
this.baseForm.includeUppercaseLetters = this.baseForm.includeUppercaseLetters ?
|
||||
true : false
|
||||
this.baseForm.containsCharacters = this.baseForm.containsCharacters ? true : false
|
||||
}).catch(() => {})
|
||||
})
|
||||
},
|
||||
changeCode() {
|
||||
let timestamp = Math.random()
|
||||
this.timestamp = timestamp
|
||||
this.imgUrl = `/api/file/ImageCode/${timestamp}`
|
||||
},
|
||||
dataFormSubmit() {
|
||||
this.$refs['dataForm'].validate((valid) => {
|
||||
if (valid) {
|
||||
let query = {
|
||||
oldPassword: md5Libs.md5(this.dataForm.oldPassword),
|
||||
password: md5Libs.md5(this.dataForm.password),
|
||||
code: this.dataForm.code,
|
||||
timestamp: this.timestamp
|
||||
}
|
||||
updatePassword(query).then(res => {
|
||||
this.$u.toast(res.msg)
|
||||
const userStore = useUserStore()
|
||||
userStore.logout().then(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
this.changeImg()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.yunzhupaas-wrap.yunzhupaas-wrap-workflow {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
:deep(.u-form-item) {
|
||||
background-color: #fff;
|
||||
min-height: 112rpx;
|
||||
}
|
||||
</style>
|
||||
68
pages/my/personalData/components/accountInformation.vue
Normal file
68
pages/my/personalData/components/accountInformation.vue
Normal file
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<view class="personalData-v">
|
||||
<u-cell-group class="" style="padding: 0 20rpx;" :border-bottom="false" :border="false">
|
||||
<u-cell-item title="账户" :value="data.account" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="所属组织" :value="data.organize" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="直属主管" :value="data.manager" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="岗位" :value="data.position" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="职级" :value="data.ranks" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="角色" :value="data.roleId" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="注册时间" :value="data.creatorTime" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="上次登录" :value="data.prevLogTime" :arrow="false" :title-style="titleStyle" />
|
||||
<u-cell-item title="入职时间" :value="data.entryDate" :arrow="false" :title-style="titleStyle"
|
||||
:border-bottom="false" />
|
||||
</u-cell-group>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
accountData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
titleStyle: {
|
||||
color: '#303133'
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
},
|
||||
data() {
|
||||
let obj = {
|
||||
...this.accountData
|
||||
}
|
||||
const {
|
||||
creatorTime,
|
||||
prevLogTime,
|
||||
entryDate
|
||||
} = obj
|
||||
obj.creatorTime = this.$u.timeFormat(obj.creatorTime, 'yyyy-mm-dd hh:MM') || ''
|
||||
obj.prevLogTime = this.$u.timeFormat(obj.prevLogTime, 'yyyy-mm-dd hh:MM') || ''
|
||||
obj.entryDate = this.$u.timeFormat(obj.entryDate, 'yyyy-mm-dd hh:MM') || ''
|
||||
return obj
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.personalData-v {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
:deep(.u-cell) {
|
||||
height: 112rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
</style>
|
||||
179
pages/my/personalData/components/commonText.vue
Normal file
179
pages/my/personalData/components/commonText.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<view class="common_v">
|
||||
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :up="upOption"
|
||||
:bottombar="false" top="120">
|
||||
<uni-swipe-action ref="swipeAction">
|
||||
<uni-swipe-action-item v-for="(item, index) in commonWordsList" :key="index" :threshold="0"
|
||||
:right-options="actionData" :auto-close="false" @click="bindClick(item,$event)">
|
||||
<view class="action-item">
|
||||
{{item.commonWordsText}}
|
||||
</view>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</mescroll-uni>
|
||||
<view class="flowBefore-actions">
|
||||
<u-button class="buttom-btn" type="primary" @click='editCommonWord'>添加常用语</u-button>
|
||||
</view>
|
||||
</view>
|
||||
<uni-popup ref="inputDialog" type="dialog">
|
||||
<uni-popup-dialog ref="inputClose" @confirm="confirm" mode="input" class="popup-dialog"
|
||||
borderRadius="20px 20px 20px 20px" beforeClose @close="close" title="审批常用语">
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<u-input v-model="commonWordsText" type="textarea" placeholder="请输入内容" :auto-height="false"
|
||||
maxlength="99999" height="150" />
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<textarea v-model="commonWordsText" :maxlength="99999" placeholder="请输入内容"
|
||||
style="padding: 20rpx 0; "></textarea>
|
||||
<!-- #endif -->
|
||||
</uni-popup-dialog>
|
||||
</uni-popup>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
commonWords,
|
||||
Create,
|
||||
Update,
|
||||
Delete
|
||||
} from "@/api/commonWords.js";
|
||||
import NoData from "@/components/noData";
|
||||
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
|
||||
import resources from '@/libs/resources.js'
|
||||
export default {
|
||||
mixins: [MescrollMixin],
|
||||
components: {
|
||||
NoData
|
||||
},
|
||||
props: {
|
||||
showCommonWords: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
downOption: {
|
||||
use: true,
|
||||
auto: true
|
||||
},
|
||||
upOption: {
|
||||
page: {
|
||||
num: 0,
|
||||
size: 30,
|
||||
time: null
|
||||
},
|
||||
empty: {
|
||||
use: true,
|
||||
icon: resources.message.nodata,
|
||||
tip: this.$t('common.noData'),
|
||||
fixed: true,
|
||||
top: "360rpx"
|
||||
},
|
||||
textNoMore: this.$t('app.apply.noMoreData')
|
||||
},
|
||||
actionData: [{
|
||||
style: {
|
||||
backgroundColor: '#1890ff'
|
||||
},
|
||||
text: '编辑'
|
||||
},
|
||||
{
|
||||
style: {
|
||||
backgroundColor: '#F56C6C'
|
||||
},
|
||||
text: '删除'
|
||||
}
|
||||
],
|
||||
commonWordsText: '',
|
||||
commonWordsData: {},
|
||||
commonWordsList: [],
|
||||
showAdd: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
upCallback(page) {
|
||||
const query = {
|
||||
currentPage: page.num,
|
||||
pageSize: page.size,
|
||||
commonWordsType: 1
|
||||
}
|
||||
commonWords(query).then(res => {
|
||||
const curPageData = res.data.list || [] // 当前页数据
|
||||
if (page.num == 1) this.commonWordsList = []; // 第一页需手动制空列表
|
||||
this.mescroll.endSuccess(res.data.list.length);
|
||||
this.commonWordsList = this.commonWordsList.concat(curPageData); //追加新数据
|
||||
}).catch(() => {
|
||||
this.mescroll.endErr();
|
||||
})
|
||||
},
|
||||
bindClick(item, e) {
|
||||
if (e.index == 0) this.editCommonWord(item)
|
||||
if (e.index == 1) this.delCommonWord(item)
|
||||
},
|
||||
editCommonWord(item) {
|
||||
this.$refs.inputDialog.open()
|
||||
let data = {
|
||||
commonWordsText: "",
|
||||
enabledMark: 1,
|
||||
id: 0,
|
||||
sortCode: 0,
|
||||
systemIds: [],
|
||||
systemNames: [],
|
||||
};
|
||||
if (item.id) {
|
||||
this.commonWordsText = item.commonWordsText;
|
||||
this.commonWordsData = {
|
||||
...item,
|
||||
systemIds: [],
|
||||
systemNames: []
|
||||
};
|
||||
} else {
|
||||
this.commonWordsText = "";
|
||||
this.commonWordsData = data;
|
||||
}
|
||||
},
|
||||
delCommonWord(item) {
|
||||
Delete(item.id).then(res => {
|
||||
this.$u.toast(res.msg)
|
||||
this.mescroll.resetUpScroll()
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.$refs.inputDialog.close()
|
||||
},
|
||||
confirm() {
|
||||
this.commonWordsData.commonWordsText = this.commonWordsText;
|
||||
this.commonWordsData.commonWordsType = 1
|
||||
if (!this.commonWordsText) return this.$u.toast(`审批常用语不能为空`);
|
||||
let funs = this.commonWordsData.id === 0 ? Create : Update;
|
||||
funs(this.commonWordsData).then((res) => {
|
||||
this.close()
|
||||
this.commonWordsText = "";
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
icon: "none",
|
||||
complete: () => {
|
||||
this.mescroll.resetUpScroll()
|
||||
},
|
||||
});
|
||||
}).catch(() => {
|
||||
this.close()
|
||||
this.mescroll.resetUpScroll()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.action-item {
|
||||
width: 100%;
|
||||
min-height: 3.6rem;
|
||||
background-color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1rpx solid #eee;
|
||||
padding: 10rpx 20rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
216
pages/my/personalData/components/personalData.vue
Normal file
216
pages/my/personalData/components/personalData.vue
Normal file
@@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<view class="yunzhupaas-wrap personalData">
|
||||
<view style="background-color: #fff;" class="u-p-l-20 u-p-r-20">
|
||||
<u-form :model="dataForm" :errorType="['toast']" label-position="left" label-width="150" label-align="right"
|
||||
ref="dataForm">
|
||||
<u-form-item label="姓名" prop='realName' required>
|
||||
<u-input input-align='right' v-model="dataForm.realName" placeholder="请输入"></u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="民族">
|
||||
<YunzhupaasSelect v-model="dataForm.nation" placeholder="请选择" :options='nationOptions' />
|
||||
</u-form-item>
|
||||
<u-form-item label="性别">
|
||||
<YunzhupaasSelect v-model="dataForm.gender" placeholder="请选择" :options='genderOptions' :props='props' />
|
||||
</u-form-item>
|
||||
<u-form-item label="籍贯">
|
||||
<u-input input-align='right' v-model="dataForm.nativePlace" placeholder="请输入"></u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="证件类型">
|
||||
<YunzhupaasSelect v-model="dataForm.certificatesType" placeholder="请选择"
|
||||
:options='certificatesTypeOptions' />
|
||||
</u-form-item>
|
||||
<u-form-item label="证件号码">
|
||||
<u-input input-align='right' v-model="dataForm.certificatesNumber" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="文化程度">
|
||||
<YunzhupaasSelect v-model="dataForm.education" placeholder="请选择" :options='educationOptions' />
|
||||
</u-form-item>
|
||||
<u-form-item label="出生年月">
|
||||
<YunzhupaasDatePicker v-model="dataForm.birthday" placeholder="请选择" />
|
||||
</u-form-item>
|
||||
<u-form-item label="办公电话">
|
||||
<u-input input-align='right' v-model="dataForm.telePhone" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="办公座机">
|
||||
<u-input input-align='right' v-model="dataForm.landline" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="手机号码">
|
||||
<u-input input-align='right' v-model="dataForm.mobilePhone" placeholder="请输">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="电子邮箱">
|
||||
<u-input input-align='right' v-model="dataForm.email" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="紧急联系">
|
||||
<u-input input-align='right' v-model="dataForm.urgentContacts" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="紧急电话">
|
||||
<u-input input-align='right' v-model="dataForm.urgentTelePhone" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="通讯地址">
|
||||
<u-input input-align='right' v-model="dataForm.postalAddress" placeholder="请输入">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
<u-form-item label="自我介绍">
|
||||
<u-input input-align='right' v-model="dataForm.signature" placeholder="请输入" type="textarea" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
</view>
|
||||
<view class="flowBefore-actions">
|
||||
<u-button class="buttom-btn" type="primary" @click='submit'>保存</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
UpdateUser
|
||||
} from '@/api/common'
|
||||
import {
|
||||
useBaseStore
|
||||
} from '@/store/modules/base'
|
||||
const baseStore = useBaseStore()
|
||||
export default {
|
||||
props: {
|
||||
personalData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
data() {
|
||||
const data = {
|
||||
show: false,
|
||||
props: {
|
||||
label: 'fullName',
|
||||
value: 'enCode'
|
||||
},
|
||||
dataForm: {
|
||||
birthday: null,
|
||||
certificatesNumber: "",
|
||||
certificatesType: "",
|
||||
education: "",
|
||||
email: "",
|
||||
gender: "",
|
||||
landline: "",
|
||||
mobilePhone: "",
|
||||
nation: "",
|
||||
nativePlace: "",
|
||||
postalAddress: "",
|
||||
realName: "",
|
||||
signature: null,
|
||||
telePhone: "",
|
||||
urgentContacts: "",
|
||||
urgentTelePhone: "",
|
||||
id: null
|
||||
},
|
||||
nationOptions: [],
|
||||
genderOptions: [],
|
||||
certificatesTypeOptions: [],
|
||||
educationOptions: [],
|
||||
rules: {
|
||||
realName: [{
|
||||
required: true,
|
||||
message: '请输入姓名',
|
||||
trigger: ['change', 'blur'],
|
||||
}]
|
||||
}
|
||||
}
|
||||
return data
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
personalData: {
|
||||
handler(val) {
|
||||
this.init()
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$refs.dataForm.setRules(this.rules);
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
let initData = JSON.parse(JSON.stringify(this.personalData))
|
||||
for (let key in initData) {
|
||||
for (let k in this.dataForm) {
|
||||
if (key === k) {
|
||||
this.dataForm[key] = initData[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
this.getOptions()
|
||||
},
|
||||
getOptions() {
|
||||
baseStore.getDictionaryData({
|
||||
sort: 'Education'
|
||||
}).then((res) => {
|
||||
this.educationOptions = JSON.parse(JSON.stringify(res))
|
||||
baseStore.getDictionaryData({
|
||||
sort: 'certificateType'
|
||||
}).then((res) => {
|
||||
this.certificatesTypeOptions = JSON.parse(JSON.stringify(res))
|
||||
})
|
||||
baseStore.getDictionaryData({
|
||||
sort: 'sex'
|
||||
}).then(res => {
|
||||
this.genderOptions = JSON.parse(JSON.stringify(res))
|
||||
})
|
||||
baseStore.getDictionaryData({
|
||||
sort: 'Nation'
|
||||
}).then(res => {
|
||||
this.nationOptions = JSON.parse(JSON.stringify(res))
|
||||
})
|
||||
})
|
||||
this.show = true
|
||||
},
|
||||
submit() {
|
||||
this.$refs.dataForm.validate(valid => {
|
||||
if (valid) {
|
||||
UpdateUser(this.dataForm).then(res => {
|
||||
uni.showToast({
|
||||
title: '保存成功',
|
||||
duration: 800,
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack()
|
||||
}, 1000)
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.slot-btn {
|
||||
width: 329rpx;
|
||||
height: 140rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgb(244, 245, 246);
|
||||
border-radius: 10rpx;
|
||||
}
|
||||
|
||||
.slot-btn__hover {
|
||||
background-color: rgb(235, 236, 238);
|
||||
}
|
||||
</style>
|
||||
260
pages/my/personalData/components/signList.vue
Normal file
260
pages/my/personalData/components/signList.vue
Normal file
@@ -0,0 +1,260 @@
|
||||
<template>
|
||||
<view>
|
||||
<view class="page_v u-flex-col">
|
||||
<view>
|
||||
<view v-if="show" v-for="(item,index) in signImg" :key="index" :class="item.isDefault ? 'active' : '' "
|
||||
class="lists_box" @longpress="handleTouchStart(item,index)">
|
||||
<view class="signImgBox">
|
||||
<image :src="item.signImg" mode="scaleToFill" class="signImg"></image>
|
||||
</view>
|
||||
<view class="icon-checked-box" v-if="item.isDefault">
|
||||
<view class="icon-checked">
|
||||
<u-icon name="checkbox-mark" color="#fff" size="28"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sign-mask" v-if="!item.isDefault && item.isSet" :id="index">
|
||||
<view class="sign-mask-btn">
|
||||
<u-button @click.prevent="del(item.id,index)">删除</u-button>
|
||||
<u-button type="primary" @click.prevent="setDefault(item.id,index)">设为默认</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<YunzhupaasSign ref="signRef" @change="signData" :showBtn="false" />
|
||||
<NoData v-if="!show" :paddingTop="460"></NoData>
|
||||
</view>
|
||||
<view class="flowBefore-actions">
|
||||
<u-button class="buttom-btn" type="primary" @click='showAction = true'>添加签名</u-button>
|
||||
</view>
|
||||
<u-action-sheet @click="handleAction" :list="actionList" :tips="{ text: '' , color: '#000' , fontSize: 30 }"
|
||||
v-model="showAction">
|
||||
</u-action-sheet>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import NoData from '@/components/noData'
|
||||
import {
|
||||
pathToBase64
|
||||
} from '@/libs/file.js'
|
||||
import {
|
||||
getSignImgList,
|
||||
createSignImg,
|
||||
setDefSignImg,
|
||||
delSignImg
|
||||
} from '@/api/common'
|
||||
export default {
|
||||
components: {
|
||||
NoData
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
show: true,
|
||||
signImg: [],
|
||||
isSet: false,
|
||||
showAction: false,
|
||||
actionList: [{
|
||||
text: '在线签名',
|
||||
id: 1
|
||||
},
|
||||
{
|
||||
text: '图片上传',
|
||||
id: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.comUploadUrl
|
||||
},
|
||||
token() {
|
||||
return uni.getStorageSync('token')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getSignImgList()
|
||||
},
|
||||
methods: {
|
||||
getSignImgList() {
|
||||
getSignImgList().then(res => {
|
||||
let signList = JSON.parse(JSON.stringify(res.data)) || []
|
||||
this.show = signList.length > 0 ? true : false
|
||||
this.signImg = signList.map(o => ({
|
||||
isSet: false,
|
||||
...o
|
||||
}))
|
||||
})
|
||||
},
|
||||
signData(e) {
|
||||
if (e) {
|
||||
let data = {
|
||||
'signImg': e,
|
||||
'isDefault': 0
|
||||
}
|
||||
createSignImg(data).then((res) => {
|
||||
this.getSignImgList()
|
||||
})
|
||||
}
|
||||
},
|
||||
handleTouchStart(item, index) {
|
||||
this.signImg.map((o, i) => {
|
||||
o.isSet = false
|
||||
})
|
||||
item.isSet = true
|
||||
},
|
||||
del(id, index) {
|
||||
delSignImg(id, index).then((res) => {
|
||||
this.signImg.splice(index, 1)
|
||||
})
|
||||
},
|
||||
setDefault(id, index) {
|
||||
let userInfo = uni.getStorageSync('userInfo')
|
||||
setDefSignImg(id).then((res) => {
|
||||
this.signImg.map((o, i) => {
|
||||
o.isDefault = false;
|
||||
if (index == i) {
|
||||
o.isDefault = true
|
||||
o.isSet = false
|
||||
userInfo.signImg = o.signImg
|
||||
uni.setStorageSync('userInfo', userInfo)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
},
|
||||
handleAction(e) {
|
||||
if (e == 0) {
|
||||
this.$refs.signRef.addSign();
|
||||
} else {
|
||||
uni.chooseImage({
|
||||
count: 1, //默认9
|
||||
sizeType: ['original', 'compressed'], //可以指定是原图还是压缩图,默认二者都有
|
||||
sourceType: ['album'],
|
||||
success: (res) => {
|
||||
let tempFilePaths = res.tempFilePaths[0]
|
||||
// #ifdef H5
|
||||
let isAccept = new RegExp('image/*').test(res.tempFiles[0].type)
|
||||
if (!isAccept) return this.$u.toast(`请上传图片`)
|
||||
// #endif
|
||||
if ((res.tempFiles[0].size / 1024) > 500) return this.$u.toast('操作失败,图片大小超出500K')
|
||||
// #ifdef APP-HARMONY
|
||||
this.harmony(tempFilePaths)
|
||||
// #endif
|
||||
// #ifndef APP-HARMONY
|
||||
pathToBase64(tempFilePaths).then(base64 => {
|
||||
this.signData(base64)
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
harmony(tempFilePaths) {
|
||||
uni.uploadFile({
|
||||
url: this.baseURL + 'imgToBase64',
|
||||
filePath: tempFilePaths,
|
||||
name: 'file',
|
||||
header: {
|
||||
'Authorization': this.token
|
||||
},
|
||||
success: (uploadFileRes) => {
|
||||
let res = JSON.parse(uploadFileRes.data)
|
||||
this.signData(res.data)
|
||||
},
|
||||
fail: (err) => {}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
.page_v {
|
||||
height: 100%;
|
||||
padding: 0 20rpx;
|
||||
|
||||
.active {
|
||||
border: 1rpx solid #2979FF;
|
||||
color: #2979FF;
|
||||
|
||||
.icon-ym-organization {
|
||||
&::before {
|
||||
color: #2979FF !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sign-mask {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
background: rgba(0, 0, 0, .3);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.sign-mask-btn {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.lists_box {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
border-radius: 8rpx;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
background-color: #FFFFFF;
|
||||
margin-bottom: 20rpx;
|
||||
overflow: hidden;
|
||||
|
||||
.signImgBox {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
|
||||
.signImg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-checked-box {
|
||||
display: flex;
|
||||
width: 140rpx;
|
||||
height: 80rpx;
|
||||
position: absolute;
|
||||
transform: scale(0.9);
|
||||
right: -4rpx;
|
||||
bottom: -2rpx;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.icon-checked {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
border: 40rpx solid #1890ff;
|
||||
border-left: 40rpx solid transparent;
|
||||
border-top: 40rpx solid transparent;
|
||||
border-bottom-right-radius: 12rpx;
|
||||
position: absolute;
|
||||
transform: scale(0.95);
|
||||
right: -8rpx;
|
||||
bottom: -6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
pages/my/personalData/index.vue
Normal file
80
pages/my/personalData/index.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<view class="personalData-v">
|
||||
<view class="notice-warp">
|
||||
<u-tabs :list="tabBars" :is-scroll="false" v-model="current" @change="tabChange" height="100">
|
||||
</u-tabs>
|
||||
</view>
|
||||
<view class="content">
|
||||
<accountData ref="accountData" v-if="current==0" :accountData="baseInfo"></accountData>
|
||||
<personalData ref="personalData" v-if="current == 1" :personalData="baseInfo"></personalData>
|
||||
<signList ref="signList" v-if="current == 2"></signList>
|
||||
<commonText ref="commonText" v-if="current == 3"></commonText>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import personalData from './components/personalData.vue';
|
||||
import accountData from './components/accountInformation.vue';
|
||||
import signList from './components/signList.vue';
|
||||
import commonText from './components/commonText.vue';
|
||||
export default {
|
||||
components: {
|
||||
personalData,
|
||||
accountData,
|
||||
signList,
|
||||
commonText
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tabBars: [{
|
||||
name: '账户信息'
|
||||
}, {
|
||||
name: '个人资料'
|
||||
}, {
|
||||
name: '个人签名'
|
||||
}, {
|
||||
name: '审批常用语'
|
||||
}],
|
||||
current: 0,
|
||||
baseInfo: {}
|
||||
};
|
||||
},
|
||||
onLoad(e) {
|
||||
this.baseInfo = JSON.parse(decodeURIComponent(e.baseInfo))
|
||||
},
|
||||
methods: {
|
||||
tabChange(index) {
|
||||
this.current = index;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.notice-warp {
|
||||
height: 100rpx;
|
||||
|
||||
.search-box {
|
||||
padding: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 120rpx;
|
||||
}
|
||||
|
||||
.personalData-v {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-bottom: 100rpx;
|
||||
|
||||
::v-deep .buttom-btn {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
32
pages/my/scanResult/index.vue
Normal file
32
pages/my/scanResult/index.vue
Normal file
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<view class="scanResult-v">
|
||||
<view class="text">
|
||||
{{result}}
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'scanResult',
|
||||
data() {
|
||||
return {
|
||||
result: '',
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
this.result = option.result;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.scanResult-v {
|
||||
height: 100%;
|
||||
padding: 0 24rpx;
|
||||
}
|
||||
</style>
|
||||
118
pages/my/settings/index.vue
Normal file
118
pages/my/settings/index.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<view class="settings-v">
|
||||
<u-cell-group class="u-p-l-20 u-p-r-20" :border="false">
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<u-cell-item :title="$t('app.my.settings.language')" @click="selectLocaleShow=true"
|
||||
:title-style="titleStyle"></u-cell-item>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<u-cell-item :title="$t('app.my.settings.userAgreement')" @click='openPage(agreement)'
|
||||
:title-style="titleStyle"></u-cell-item>
|
||||
<u-cell-item :title="$t('app.my.settings.privacyPolicy')" @click='openPage(policy)'
|
||||
:title-style="titleStyle"></u-cell-item>
|
||||
<!-- #endif -->
|
||||
<u-cell-item :title="$t('app.my.settings.changePassword')" @click="modifyPsd('/pages/my/modifyPsd/index')"
|
||||
:title-style="titleStyle">
|
||||
</u-cell-item>
|
||||
<u-cell-item :title="$t('app.my.settings.contact')" @click="modifyPsd('/pages/my/contactUs/index')"
|
||||
:title-style="titleStyle">
|
||||
</u-cell-item>
|
||||
<u-cell-item :title="$t('app.my.settings.About')" @click="modifyPsd('/pages/my/abouts/index')"
|
||||
:title-style="titleStyle" :border-bottom="false">
|
||||
</u-cell-item>
|
||||
</u-cell-group>
|
||||
<u-select v-model="selectLocaleShow" :list="localeList" mode="single-column" :default-value="defaultLocale"
|
||||
@confirm="localeConfirm"></u-select>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import resources from '@/libs/resources.js'
|
||||
import {
|
||||
useLocale
|
||||
} from '@/locale/useLocale';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
// #ifdef APP-PLUS
|
||||
agreement: resources.userAgreement,
|
||||
policy: resources.privacyPolicy,
|
||||
// #endif
|
||||
titleStyle: {
|
||||
color: '#303133'
|
||||
},
|
||||
localeList: [{
|
||||
label: '简体中文',
|
||||
value: 'zh-Hans'
|
||||
},
|
||||
{
|
||||
label: '繁体中文',
|
||||
value: 'zh-Hant'
|
||||
},
|
||||
{
|
||||
label: 'English',
|
||||
value: 'en'
|
||||
}
|
||||
],
|
||||
selectLocaleShow: false,
|
||||
defaultLocale: []
|
||||
};
|
||||
},
|
||||
onLoad() {
|
||||
this.defaultLocale = [this.localeList.findIndex(o => o.value === uni.getLocale())];
|
||||
},
|
||||
methods: {
|
||||
modifyPsd(path) {
|
||||
if (!path) return
|
||||
uni.navigateTo({
|
||||
url: path
|
||||
})
|
||||
},
|
||||
// #ifdef APP-PLUS
|
||||
openPage(url) {
|
||||
plus.runtime.openURL(url);
|
||||
},
|
||||
// #endif
|
||||
localeConfirm(e) {
|
||||
if (e[0].index === this.defaultLocale[0]) return
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const isAndroid = systemInfo.platform.toLowerCase() === 'android';
|
||||
if (isAndroid) {
|
||||
uni.showModal({
|
||||
content: '应用此设置将重启App',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
this.handleChangeLocale(e[0])
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.handleChangeLocale(e[0])
|
||||
}
|
||||
},
|
||||
handleChangeLocale(e) {
|
||||
this.defaultLocale = [e.index];
|
||||
const {
|
||||
changeLocale
|
||||
} = useLocale();
|
||||
changeLocale(e.value)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
page {
|
||||
background-color: #f0f2f6;
|
||||
}
|
||||
|
||||
:deep(.u-cell) {
|
||||
height: 112rpx;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
|
||||
.settings-v {
|
||||
background-color: #fff;
|
||||
}
|
||||
</style>
|
||||
BIN
pages/my/static/image/about-head.png
Normal file
BIN
pages/my/static/image/about-head.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
67
pages/my/subordinate/index.vue
Normal file
67
pages/my/subordinate/index.vue
Normal file
@@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<view class="tree-main">
|
||||
<ly-tree v-if="isReady" :props="props" :node-key="props.value" :load="loadNode" lazy :tree-data="treeData"
|
||||
show-node-icon :defaultExpandAll='false' />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import LyTree from './subordinateTree/ly-tree.vue'
|
||||
import {
|
||||
getSubordinate
|
||||
} from '@/api/common.js'
|
||||
let _self;
|
||||
export default {
|
||||
components: {
|
||||
LyTree
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isReady: false,
|
||||
props: {
|
||||
label: 'userName',
|
||||
isLeaf: 'isLeaf',
|
||||
value: "id",
|
||||
icon: 'avatar'
|
||||
},
|
||||
treeData: []
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
_self = this
|
||||
this.isReady = true;
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadNode(node, resolve) {
|
||||
if (node.level === 0) {
|
||||
getSubordinate(node.level).then(res => {
|
||||
let data = JSON.parse(JSON.stringify(res.data))
|
||||
data.map((o) => {
|
||||
o.avatar = _self.baseURL + o.avatar
|
||||
return data
|
||||
})
|
||||
resolve(data)
|
||||
})
|
||||
} else {
|
||||
getSubordinate(node.key).then(res => {
|
||||
let data = JSON.parse(JSON.stringify(res.data))
|
||||
data.map((o) => {
|
||||
o.avatar = _self.baseURL + o.avatar
|
||||
return data
|
||||
})
|
||||
resolve(data)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
395
pages/my/subordinate/subordinateTree/ly-tree-node.vue
Normal file
395
pages/my/subordinate/subordinateTree/ly-tree-node.vue
Normal file
@@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<view ref="node" name="LyTreeNode" v-show="node.visible" class="ly-tree-node" :class="{
|
||||
'is-expanded': expanded,
|
||||
'is-hidden': !node.visible,
|
||||
'is-checked': !node.disabled && node.checked
|
||||
}" role="treeitem" @tap.stop="handleClick">
|
||||
<view class="ly-tree-node__content" :class="{
|
||||
'is-current': node.isCurrent && highlightCurrent
|
||||
}" :style="{
|
||||
'padding-left': (node.level - 1) * indent + 'px'
|
||||
}">
|
||||
<text @tap.stop="handleExpandIconClick" :class="[
|
||||
{
|
||||
'is-leaf': node.isLeaf,
|
||||
expanded: !node.isLeaf && node.expanded
|
||||
},
|
||||
'ly-tree-node__expand-icon',
|
||||
iconClass ? iconClass : 'ly-iconfont ly-icon-caret-right'
|
||||
]">
|
||||
</text>
|
||||
|
||||
<ly-checkbox v-if="checkboxVisible || radioVisible" :type="checkboxVisible ? 'checkbox' : 'radio'"
|
||||
:checked="node.checked" :indeterminate="node.indeterminate" :disabled="!!node.disabled"
|
||||
@check="handleCheckChange(!node.checked)" />
|
||||
|
||||
<text v-if="node.loading" class="ly-tree-node__loading-icon ly-iconfont ly-icon-loading">
|
||||
</text>
|
||||
|
||||
<template v-if="node.icon && node.icon.length > 0">
|
||||
<image v-if="node.icon.indexOf('/') !== -1" class="ly-tree-node__icon" mode="widthFix" :src="node.icon"
|
||||
@error="handleImageError">
|
||||
</image>
|
||||
<text v-else class="ly-tree-node__icon" :class="node.icon">
|
||||
</text>
|
||||
</template>
|
||||
|
||||
<view class="ly-tree-node__label_box">
|
||||
<text class="ly-tree-node__label u-line-1">{{node.data.userName}}</text>
|
||||
<text style="color: #c6c6c6;"
|
||||
class="ly-tree-node__label u-line-1">{{node.data.position ? node.data.department +'/'+node.data.position : node.data.department}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="!renderAfterExpand || childNodeRendered" v-show="expanded" class="ly-tree-node__children"
|
||||
role="group">
|
||||
<ly-tree-node v-for="cNodeId in node.childNodesId" :nodeId="cNodeId"
|
||||
:render-after-expand="renderAfterExpand" :show-checkbox="showCheckbox" :show-radio="showRadio"
|
||||
:check-only-leaf="checkOnlyLeaf" :key="getNodeKey(cNodeId)" :indent="indent" :icon-class="iconClass">
|
||||
</ly-tree-node>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getNodeKey
|
||||
} from './tool/util.js';
|
||||
export default {
|
||||
name: 'LyTreeNode',
|
||||
componentName: 'LyTreeNode',
|
||||
props: {
|
||||
nodeId: [Number, String],
|
||||
renderAfterExpand: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
checkOnlyLeaf: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
showRadio: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
indent: Number,
|
||||
iconClass: String
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
node: {
|
||||
indeterminate: false,
|
||||
checked: false,
|
||||
expanded: false
|
||||
},
|
||||
expanded: false,
|
||||
childNodeRendered: false,
|
||||
oldChecked: null,
|
||||
oldIndeterminate: null,
|
||||
highlightCurrent: false
|
||||
};
|
||||
},
|
||||
|
||||
inject: ['tree'],
|
||||
|
||||
computed: {
|
||||
checkboxVisible() {
|
||||
if (this.checkOnlyLeaf) {
|
||||
return this.showCheckbox && this.node.isLeaf;
|
||||
}
|
||||
|
||||
return this.showCheckbox;
|
||||
},
|
||||
radioVisible() {
|
||||
if (this.checkOnlyLeaf) {
|
||||
return this.showRadio && this.node.isLeaf;
|
||||
}
|
||||
|
||||
return this.showRadio;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
'node.indeterminate'(val) {
|
||||
this.handleSelectChange(this.node.checked, val);
|
||||
},
|
||||
'node.checked'(val) {
|
||||
this.handleSelectChange(val, this.node.indeterminate);
|
||||
},
|
||||
'node.expanded'(val) {
|
||||
this.$nextTick(() => this.expanded = val);
|
||||
if (val) {
|
||||
this.childNodeRendered = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
getNodeKey(nodeId) {
|
||||
let node = this.tree.store.root.getChildNodes([nodeId])[0];
|
||||
return getNodeKey(this.tree.nodeKey, node.data);
|
||||
},
|
||||
|
||||
handleSelectChange(checked, indeterminate) {
|
||||
if (this.oldChecked !== checked && this.oldIndeterminate !== indeterminate) {
|
||||
|
||||
if (this.checkOnlyLeaf && !this.node.isLeaf) return;
|
||||
|
||||
if (this.checkboxVisible) {
|
||||
const allNodes = this.tree.store._getAllNodes();
|
||||
this.tree.$emit('check-change', {
|
||||
checked,
|
||||
indeterminate,
|
||||
node: this.node,
|
||||
data: this.node.data,
|
||||
checkedall: allNodes.every(item => item.checked)
|
||||
});
|
||||
} else {
|
||||
this.tree.$emit('radio-change', {
|
||||
checked,
|
||||
node: this.node,
|
||||
data: this.node.data,
|
||||
checkedall: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.expanded && this.tree.expandOnCheckNode && checked) {
|
||||
this.handleExpandIconClick();
|
||||
}
|
||||
|
||||
this.oldChecked = checked;
|
||||
this.indeterminate = indeterminate;
|
||||
},
|
||||
|
||||
handleClick() {
|
||||
this.tree.store.setCurrentNode(this.node);
|
||||
this.tree.$emit('current-change', {
|
||||
node: this.node,
|
||||
data: this.tree.store.currentNode ? this.tree.store.currentNode.data : null,
|
||||
currentNode: this.tree.store.currentNode
|
||||
});
|
||||
this.tree.currentNode = this.node;
|
||||
|
||||
if (this.tree.expandOnClickNode) {
|
||||
this.handleExpandIconClick();
|
||||
}
|
||||
|
||||
if (this.tree.checkOnClickNode && !this.node.disabled) {
|
||||
(this.checkboxVisible || this.radioVisible) && this.handleCheckChange(!this.node.checked);
|
||||
}
|
||||
|
||||
this.tree.$emit('node-click', this.node);
|
||||
},
|
||||
|
||||
handleExpandIconClick() {
|
||||
if (this.node.isLeaf) return;
|
||||
|
||||
if (this.expanded) {
|
||||
this.tree.$emit('node-collapse', this.node);
|
||||
this.node.collapse();
|
||||
} else {
|
||||
this.node.expand();
|
||||
this.tree.$emit('node-expand', this.node);
|
||||
|
||||
if (this.tree.accordion) {
|
||||
uni.$emit(`${this.tree.elId}-tree-node-expand`, this.node);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleCheckChange(checked) {
|
||||
if (this.node.disabled) return;
|
||||
|
||||
if (this.checkboxVisible) {
|
||||
this.node.setChecked(checked, !(this.tree.checkStrictly || this.checkOnlyLeaf));
|
||||
} else {
|
||||
this.node.setRadioChecked(checked);
|
||||
}
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.tree.$emit('check', {
|
||||
node: this.node,
|
||||
data: this.node.data,
|
||||
checkedNodes: this.tree.store.getCheckedNodes(),
|
||||
checkedKeys: this.tree.store.getCheckedKeys(),
|
||||
halfCheckedNodes: this.tree.store.getHalfCheckedNodes(),
|
||||
halfCheckedKeys: this.tree.store.getHalfCheckedKeys()
|
||||
});
|
||||
});
|
||||
uni.$emit('updateKey')
|
||||
},
|
||||
|
||||
handleImageError() {
|
||||
this.node.icon = this.tree.defaultNodeIcon;
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if (!this.tree) {
|
||||
throw new Error('Can not find node\'s tree.');
|
||||
}
|
||||
|
||||
this.node = this.tree.store.nodesMap[this.nodeId];
|
||||
this.highlightCurrent = this.tree.highlightCurrent;
|
||||
|
||||
if (this.node.expanded) {
|
||||
this.expanded = true;
|
||||
this.childNodeRendered = true;
|
||||
}
|
||||
|
||||
const props = this.tree.props || {};
|
||||
const childrenKey = props['children'] || 'children';
|
||||
this.$watch(`node.data.${childrenKey}`, () => {
|
||||
this.node.updateChildren();
|
||||
});
|
||||
|
||||
if (this.tree.accordion) {
|
||||
uni.$on(`${this.tree.elId}-tree-node-expand`, node => {
|
||||
if (this.node.id !== node.id && this.node.level === node.level) {
|
||||
this.node.collapse();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.$parent = null;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.ly-tree-node {
|
||||
white-space: nowrap;
|
||||
outline: 0
|
||||
}
|
||||
|
||||
.ly-tree-node__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 70rpx;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.ly-tree-node__label_box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.ly-tree-node__content.is-current {
|
||||
background-color: #F5F7FA;
|
||||
}
|
||||
|
||||
.ly-tree-node__content>.ly-tree-node__expand-icon {
|
||||
padding: 12rpx;
|
||||
}
|
||||
|
||||
.ly-tree-node__checkbox {
|
||||
display: flex;
|
||||
margin-right: 16rpx;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.ly-tree-node__checkbox>image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.ly-tree-node__expand-icon {
|
||||
color: #C0C4CC;
|
||||
font-size: 28rpx;
|
||||
-webkit-transform: rotate(0);
|
||||
transform: rotate(0);
|
||||
-webkit-transition: -webkit-transform .3s ease-in-out;
|
||||
transition: -webkit-transform .3s ease-in-out;
|
||||
transition: transform .3s ease-in-out;
|
||||
transition: transform .3s ease-in-out, -webkit-transform .3s ease-in-out
|
||||
}
|
||||
|
||||
.ly-tree-node__expand-icon.expanded {
|
||||
-webkit-transform: rotate(90deg);
|
||||
transform: rotate(90deg)
|
||||
}
|
||||
|
||||
.ly-tree-node__expand-icon.is-leaf {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.ly-tree-node__label {
|
||||
font-size: 28rpx
|
||||
}
|
||||
|
||||
.ly-tree-node__loading-icon {
|
||||
margin-right: 16rpx;
|
||||
font-size: 28rpx;
|
||||
color: #C0C4CC;
|
||||
-webkit-animation: rotating 2s linear infinite;
|
||||
animation: rotating 2s linear infinite
|
||||
}
|
||||
|
||||
.ly-tree-node>.ly-tree-node__children {
|
||||
overflow: hidden;
|
||||
background-color: transparent
|
||||
}
|
||||
|
||||
.ly-tree-node>.ly-tree-node__children.collapse-transition {
|
||||
transition: height .3s ease-in-out;
|
||||
}
|
||||
|
||||
.ly-tree-node.is-expanded>.ly-tree-node__children {
|
||||
display: block
|
||||
}
|
||||
|
||||
.ly-tree-node_collapse {
|
||||
overflow: hidden;
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
/* lyTree-end */
|
||||
|
||||
/* iconfont-start */
|
||||
@font-face {
|
||||
font-family: "ly-iconfont";
|
||||
src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAPsAAsAAAAACKwAAAOeAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDBgqFDIQPATYCJAMMCwgABCAFhG0HQBtfB8gekiSCdAwUAKgCFMA5Hj7H0PeTlABUr57PVyGqugqzSWJnNwWoWJjx/9rUr4TPL1ZSQpU2mycqwoRwIN3p+MkqMqyEW+OtMBLPSUBb8v//XtWMKTavxYIUsT/Wy1qbQzkBDOYEKGB7dVpPyVqgCnJNwvMvhZl10nMCtQbFoPVhY8ZDncJfF4grbqpQ13AqE52hWqgcOFrEQ6hWnW5VfMCD7Pfjn4WoI6nI/K0bl0MNGPBz0qcflVqYnvCA4vNDPUXGPFCIw8HgtsqiOK9SrW2smm6sVITElWlpISMdVBn8wyMJopLfXg+myZ48KCrSkvj9g37U1ItbXYke4APwXxK3N4TuehyBfmM0I3zbNdt7uk3VnjPtzX0rnIl7z7bZvb/thHohsu9QuykKo+Cws4nL7LsPmI3n2qN9B9upZEIKd4hu0NCKi0rt7fNtdl+I1N25hOJMDQK6odS123tROR7Pg8toEhDaF+kR0TYjxW6M58F5+ZNQOxmZHtE2g+IYjxjlNy/yIRQpCmrgq5R4/3jx8PvT8Ha8d3/xiLnt4EGyaDnznzRv8vpyZ+9TFHf/ntX9e59A+b6+fPHd5+dy0wYHVvHOroWbnWe879O9DnL53bN/gUHuwm28b/n8i/V3ry4E3IoXNqS6Rvs0LhJxeNVjoUkM3LKosU+0a6rh45FVvLt+2oz7Zd53b4QOy7/9snDXHbqVu+A+f8r7PnM2H8kXrWm5c8/vLu7LqRee7HW637mz3kHc5U/RCXf25d7G8tkdgEfwIpzpkknGpaMw3ww55q9Mn9OQNyua/wB/49OOWydn4eL/6roCfjx6FMmcxfJStYRKfd3UwoHiML4rF4uMSK+SvYTuNxMHrpl8yd3Q6v32cAeo/KFaowBJlQHIqo3zi3geKtRZhErVlqDWnOGn67QRKkWpwaw1AkKza5A0egFZszf8In4HFTp9h0rNUQm1NqP1lXUmgyuDBVUlNYi2gHA98FnokUreOZaac1xV1JlMMZGKEs+QdCLVrgynPhUcO0pzzYyUjDAReGSYeBl13YCEIrCpLhOWlGE+mWRD35TQAw8UawRKJVEGQrMAwekCPpaMlpTOz49FmeZwqcREX1t3Ikoo4dMTaQmpBfzhRn9R30uZXTKXKUOSmLSKEQIeYhjqKZcrcIzhMLLRrJMSrA35UF4yGMaWGhPHm733dwJq+Z/NkSJHUXemCirjgpuWrHMD1eC+mQUAAAA=') format('woff2');
|
||||
}
|
||||
|
||||
.ly-iconfont {
|
||||
font-family: "ly-iconfont" !important;
|
||||
font-size: 30rpx;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.ly-icon-caret-right:before {
|
||||
content: "\e8ee";
|
||||
}
|
||||
|
||||
.ly-icon-loading:before {
|
||||
content: "\e657";
|
||||
}
|
||||
|
||||
/* iconfont-end */
|
||||
|
||||
/* animate-start */
|
||||
@keyframes rotating {
|
||||
0% {
|
||||
-webkit-transform: rotateZ(0);
|
||||
transform: rotateZ(0)
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: rotateZ(360deg);
|
||||
transform: rotateZ(360deg)
|
||||
}
|
||||
}
|
||||
|
||||
/* animate-end */
|
||||
</style>
|
||||
620
pages/my/subordinate/subordinateTree/ly-tree.vue
Normal file
620
pages/my/subordinate/subordinateTree/ly-tree.vue
Normal file
@@ -0,0 +1,620 @@
|
||||
<template>
|
||||
<view>
|
||||
<template v-if="showLoading">
|
||||
<view class="ly-loader ly-flex-center">
|
||||
<view class="ly-loader-inner">加载中...</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<view v-if="isEmpty || !visible" class="ly-empty">
|
||||
{{emptyText}}
|
||||
</view>
|
||||
<view :key="updateKey" class="ly-tree" :class="{'is-empty': isEmpty || !visible}" role="tree"
|
||||
name="LyTreeExpand">
|
||||
<ly-tree-node v-for="nodeId in childNodesId" :nodeId="nodeId" :render-after-expand="renderAfterExpand"
|
||||
:show-checkbox="showCheckbox" :show-radio="showRadio" :check-only-leaf="checkOnlyLeaf"
|
||||
:key="getNodeKey(nodeId)" :indent="indent" :icon-class="iconClass" updateKey="hanldeUpdateKey">
|
||||
</ly-tree-node>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TreeStore from './model/tree-store.js';
|
||||
import {
|
||||
getNodeKey
|
||||
} from './tool/util.js';
|
||||
import LyTreeNode from './ly-tree-node.vue';
|
||||
|
||||
export default {
|
||||
name: 'LyTree',
|
||||
|
||||
componentName: 'LyTree',
|
||||
|
||||
components: {
|
||||
LyTreeNode
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
updateKey: new Date().getTime(), // 数据更新的时候,重新渲染树
|
||||
elId: `ly_${Math.ceil(Math.random() * 10e5).toString(36)}`,
|
||||
visible: true,
|
||||
store: {
|
||||
ready: false
|
||||
},
|
||||
currentNode: null,
|
||||
childNodesId: [],
|
||||
mathKey: 1
|
||||
};
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
tree: this
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
// 展示数据
|
||||
treeData: Array,
|
||||
|
||||
// 自主控制loading加载,避免数据还没获取到的空档出现“暂无数据”字样
|
||||
ready: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 内容为空的时候展示的文本
|
||||
emptyText: {
|
||||
type: String,
|
||||
default: '暂无数据'
|
||||
},
|
||||
|
||||
// 是否在第一次展开某个树节点后才渲染其子节点
|
||||
renderAfterExpand: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
|
||||
nodeKey: String,
|
||||
|
||||
// 在显示复选框的情况下,是否严格的遵循父子不互相关联的做法,默认为 false
|
||||
checkStrictly: Boolean,
|
||||
|
||||
// 是否默认展开所有节点
|
||||
defaultExpandAll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 切换全部展开、全部折叠
|
||||
toggleExpendAll: Boolean,
|
||||
|
||||
// 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点
|
||||
expandOnClickNode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 选中的时候展开节点
|
||||
expandOnCheckNode: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 是否在点击节点的时候选中节点,默认值为 false,即只有在点击复选框时才会选中节点
|
||||
checkOnClickNode: Boolean,
|
||||
checkDescendants: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 展开子节点的时候是否自动展开父节点
|
||||
autoExpandParent: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
// 默认勾选的节点的 key 的数组
|
||||
defaultCheckedKeys: Array,
|
||||
|
||||
// 默认展开的节点的 key 的数组
|
||||
defaultExpandedKeys: Array,
|
||||
|
||||
// 是否展开当前节点的父节点
|
||||
expandCurrentNodeParent: Boolean,
|
||||
|
||||
// 当前选中的节点
|
||||
currentNodeKey: [String, Number],
|
||||
|
||||
// 是否最后一层叶子节点才显示单选/多选框
|
||||
checkOnlyLeaf: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 节点是否可被选择
|
||||
showCheckbox: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 节点单选
|
||||
showRadio: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 配置选项
|
||||
props: {
|
||||
type: [Object, Function],
|
||||
default () {
|
||||
return {
|
||||
children: 'children', // 指定子树为节点对象的某个属性值
|
||||
label: 'label', // 指定节点标签为节点对象的某个属性值
|
||||
disabled: 'disabled' // 指定节点选择框是否禁用为节点对象的某个属性值
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
// 是否懒加载子节点,需与 load 方法结合使用
|
||||
lazy: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 是否高亮当前选中节点,默认值是 false
|
||||
highlightCurrent: Boolean,
|
||||
|
||||
// 加载子树数据的方法,仅当 lazy 属性为true 时生效
|
||||
load: Function,
|
||||
|
||||
// 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
|
||||
filterNodeMethod: Function,
|
||||
|
||||
// 搜索时是否展示匹配项的所有子节点
|
||||
childVisibleForFilterNode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 是否每次只打开一个同级树节点展开
|
||||
accordion: Boolean,
|
||||
|
||||
// 相邻级节点间的水平缩进,单位为像素
|
||||
indent: {
|
||||
type: Number,
|
||||
default: 18
|
||||
},
|
||||
|
||||
// 自定义树节点的展开图标
|
||||
iconClass: String,
|
||||
|
||||
// 是否显示节点图标,如果配置为true,需要配置props中对应的图标属性名称
|
||||
showNodeIcon: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
|
||||
// 当节点图标显示出错时,显示的默认图标
|
||||
defaultNodeIcon: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
|
||||
// 如果数据量较大,建议不要在node节点中添加parent属性,会造成性能损耗
|
||||
isInjectParentInNode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
isEmpty() {
|
||||
if (this.store.root) {
|
||||
const childNodes = this.store.root.getChildNodes(this.childNodesId);
|
||||
|
||||
return !childNodes || childNodes.length === 0 || childNodes.every(({
|
||||
visible
|
||||
}) => !visible);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
showLoading() {
|
||||
//不要删除
|
||||
const a = this.mathKey
|
||||
return !(this.store.getReady() && this.ready);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
toggleExpendAll(newVal) {
|
||||
this.store.toggleExpendAll(newVal);
|
||||
},
|
||||
defaultCheckedKeys(newVal) {
|
||||
this.store.setDefaultCheckedKey(newVal);
|
||||
},
|
||||
defaultExpandedKeys(newVal) {
|
||||
this.store.defaultExpandedKeys = newVal;
|
||||
this.store.setDefaultExpandedKeys(newVal);
|
||||
},
|
||||
checkStrictly(newVal) {
|
||||
this.store.checkStrictly = newVal || this.checkOnlyLeaf;
|
||||
},
|
||||
'store.root.childNodesId'(newVal) {
|
||||
this.childNodesId = newVal;
|
||||
},
|
||||
'store.root.visible'(newVal) {
|
||||
this.visible = newVal;
|
||||
},
|
||||
childNodesId() {
|
||||
this.$nextTick(() => {
|
||||
this.$emit('ly-tree-render-completed');
|
||||
});
|
||||
},
|
||||
treeData: {
|
||||
handler(newVal) {
|
||||
this.updateKey = new Date().getTime();
|
||||
this.store.setData(newVal);
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
/*
|
||||
* @description 对树节点进行筛选操作
|
||||
* @method filter
|
||||
* @param {all} value 在 filter-node-method 中作为第一个参数
|
||||
* @param {Object} data 搜索指定节点的节点数据,不传代表搜索所有节点,假如要搜索A节点下面的数据,那么nodeData代表treeData中A节点的数据
|
||||
*/
|
||||
filter(value, data) {
|
||||
if (!this.filterNodeMethod) throw new Error('[Tree] filterNodeMethod is required when filter');
|
||||
this.store.filter(value, data);
|
||||
this.handleUpdateKey()
|
||||
},
|
||||
|
||||
handleUpdateKey() {
|
||||
this.updateKey = new Date().getTime();
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 获取节点的唯一标识符
|
||||
* @method getNodeKey
|
||||
* @param {String, Number} nodeId
|
||||
* @return {String, Number} 匹配到的数据中的某一项数据
|
||||
*/
|
||||
getNodeKey(nodeId) {
|
||||
let node = this.store.root.getChildNodes([nodeId])[0];
|
||||
return getNodeKey(this.nodeKey, node.data);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 获取节点路径
|
||||
* @method getNodePath
|
||||
* @param {Object} data 节点数据
|
||||
* @return {Array} 路径数组
|
||||
*/
|
||||
getNodePath(data) {
|
||||
return this.store.getNodePath(data);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点所组成的数组
|
||||
* @method getCheckedNodes
|
||||
* @param {Boolean} leafOnly 是否只是叶子节点,默认false
|
||||
* @param {Boolean} includeHalfChecked 是否包含半选节点,默认false
|
||||
* @return {Array} 目前被选中的节点所组成的数组
|
||||
*/
|
||||
getCheckedNodes(leafOnly, includeHalfChecked) {
|
||||
return this.store.getCheckedNodes(leafOnly, includeHalfChecked);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前被选中的节点的 key 所组成的数组
|
||||
* @method getCheckedKeys
|
||||
* @param {Boolean} leafOnly 是否只是叶子节点,默认false,若为 true 则仅返回被选中的叶子节点的 keys
|
||||
* @param {Boolean} includeHalfChecked 是否返回indeterminate为true的节点,默认false
|
||||
* @return {Array} 目前被选中的节点所组成的数组
|
||||
*/
|
||||
getCheckedKeys(leafOnly, includeHalfChecked) {
|
||||
return this.store.getCheckedKeys(leafOnly, includeHalfChecked);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 获取当前被选中节点的 data,若没有节点被选中则返回 null
|
||||
* @method getCurrentNode
|
||||
* @return {Object} 当前被选中节点的 data,若没有节点被选中则返回 null
|
||||
*/
|
||||
getCurrentNode() {
|
||||
const currentNode = this.store.getCurrentNode();
|
||||
return currentNode ? currentNode.data : null;
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 获取当前被选中节点的 key,若没有节点被选中则返回 null
|
||||
* @method getCurrentKey
|
||||
* @return {all} 当前被选中节点的 key, 若没有节点被选中则返回 null
|
||||
*/
|
||||
getCurrentKey() {
|
||||
const currentNode = this.getCurrentNode();
|
||||
return currentNode ? currentNode[this.nodeKey] : null;
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 设置全选/取消全选
|
||||
* @method setCheckAll
|
||||
* @param {Boolean} isCheckAll 选中状态,默认为true
|
||||
*/
|
||||
setCheckAll(isCheckAll = true) {
|
||||
if (this.showRadio) throw new Error(
|
||||
'You set the "show-radio" property, so you cannot select all nodes');
|
||||
|
||||
if (!this.showCheckbox) console.warn(
|
||||
'You have not set the property "show-checkbox". Please check your settings');
|
||||
|
||||
this.store.setCheckAll(isCheckAll);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 设置目前勾选的节点
|
||||
* @method setCheckedNodes
|
||||
* @param {Array} nodes 接收勾选节点数据的数组
|
||||
* @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
|
||||
*/
|
||||
setCheckedNodes(nodes, leafOnly) {
|
||||
this.store.setCheckedNodes(nodes, leafOnly);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 通过 keys 设置目前勾选的节点
|
||||
* @method setCheckedKeys
|
||||
* @param {Array} keys 勾选节点的 key 的数组
|
||||
* @param {Boolean} leafOnly 是否只是叶子节点, 若为 true 则仅设置叶子节点的选中状态,默认值为 false
|
||||
*/
|
||||
setCheckedKeys(keys, leafOnly) {
|
||||
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCheckedKeys');
|
||||
this.store.setCheckedKeys(keys, leafOnly);
|
||||
this.handleUpdateKey()
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 通过 key / data 设置某个节点的勾选状态
|
||||
* @method setChecked
|
||||
* @param {all} data 勾选节点的 key 或者 data
|
||||
* @param {Boolean} checked 节点是否选中
|
||||
* @param {Boolean} deep 是否设置子节点 ,默认为 false
|
||||
*/
|
||||
setChecked(data, checked, deep) {
|
||||
this.store.setChecked(data, checked, deep);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点所组成的数组
|
||||
* @method getHalfCheckedNodes
|
||||
* @return {Array} 目前半选中的节点所组成的数组
|
||||
*/
|
||||
getHalfCheckedNodes() {
|
||||
return this.store.getHalfCheckedNodes();
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 若节点可被选择(即 show-checkbox 为 true),则返回目前半选中的节点的 key 所组成的数组
|
||||
* @method getHalfCheckedKeys
|
||||
* @return {Array} 目前半选中的节点的 key 所组成的数组
|
||||
*/
|
||||
getHalfCheckedKeys() {
|
||||
return this.store.getHalfCheckedKeys();
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 通过 node 设置某个节点的当前选中状态
|
||||
* @method setCurrentNode
|
||||
* @param {Object} node 待被选节点的 node
|
||||
*/
|
||||
setCurrentNode(node) {
|
||||
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentNode');
|
||||
this.store.setUserCurrentNode(node);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 通过 key 设置某个节点的当前选中状态
|
||||
* @method setCurrentKey
|
||||
* @param {all} key 待被选节点的 key,若为 null 则取消当前高亮的节点
|
||||
*/
|
||||
setCurrentKey(key) {
|
||||
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in setCurrentKey');
|
||||
this.store.setCurrentNodeKey(key);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 根据 data 或者 key 拿到 Tree 组件中的 node
|
||||
* @method getNode
|
||||
* @param {all} data 要获得 node 的 key 或者 data
|
||||
*/
|
||||
getNode(data) {
|
||||
return this.store.getNode(data);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 删除 Tree 中的一个节点
|
||||
* @method remove
|
||||
* @param {all} data 要删除的节点的 data 或者 node
|
||||
*/
|
||||
remove(data) {
|
||||
this.store.remove(data);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 为 Tree 中的一个节点追加一个子节点
|
||||
* @method append
|
||||
* @param {Object} data 要追加的子节点的 data
|
||||
* @param {Object} parentNode 子节点的 parent 的 data、key 或者 node
|
||||
*/
|
||||
append(data, parentNode) {
|
||||
this.store.append(data, parentNode);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 为 Tree 的一个节点的前面增加一个节点
|
||||
* @method insertBefore
|
||||
* @param {Object} data 要增加的节点的 data
|
||||
* @param {all} refNode 要增加的节点的后一个节点的 data、key 或者 node
|
||||
*/
|
||||
insertBefore(data, refNode) {
|
||||
this.store.insertBefore(data, refNode);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 为 Tree 的一个节点的后面增加一个节点
|
||||
* @method insertAfter
|
||||
* @param {Object} data 要增加的节点的 data
|
||||
* @param {all} refNode 要增加的节点的前一个节点的 data、key 或者 node
|
||||
*/
|
||||
insertAfter(data, refNode) {
|
||||
this.store.insertAfter(data, refNode);
|
||||
},
|
||||
|
||||
/*
|
||||
* @description 通过 keys 设置节点子元素
|
||||
* @method updateKeyChildren
|
||||
* @param {String, Number} key 节点 key
|
||||
* @param {Object} data 节点数据的数组
|
||||
*/
|
||||
updateKeyChildren(key, data) {
|
||||
if (!this.nodeKey) throw new Error('[Tree] nodeKey is required in updateKeyChild');
|
||||
this.store.updateChildren(key, data);
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.isTree = true;
|
||||
|
||||
let props = this.props;
|
||||
if (typeof this.props === 'function') props = this.props();
|
||||
if (typeof props !== 'object') throw new Error('props must be of object type.');
|
||||
|
||||
this.store = new TreeStore({
|
||||
key: this.nodeKey,
|
||||
data: this.treeData,
|
||||
lazy: this.lazy,
|
||||
props: props,
|
||||
load: this.load,
|
||||
showCheckbox: this.showCheckbox,
|
||||
showRadio: this.showRadio,
|
||||
currentNodeKey: this.currentNodeKey,
|
||||
checkStrictly: this.checkStrictly || this.checkOnlyLeaf,
|
||||
checkDescendants: this.checkDescendants,
|
||||
expandOnCheckNode: this.expandOnCheckNode,
|
||||
defaultCheckedKeys: this.defaultCheckedKeys,
|
||||
defaultExpandedKeys: this.defaultExpandedKeys,
|
||||
expandCurrentNodeParent: this.expandCurrentNodeParent,
|
||||
autoExpandParent: this.autoExpandParent,
|
||||
defaultExpandAll: this.defaultExpandAll,
|
||||
filterNodeMethod: this.filterNodeMethod,
|
||||
childVisibleForFilterNode: this.childVisibleForFilterNode,
|
||||
showNodeIcon: this.showNodeIcon,
|
||||
isInjectParentInNode: this.isInjectParentInNode
|
||||
});
|
||||
this.childNodesId = this.store.root.childNodesId;
|
||||
|
||||
uni.$on(`updateKey`, () => {
|
||||
this.handleUpdateKey()
|
||||
this.mathKey++
|
||||
});
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (this.accordion) {
|
||||
uni.$off(`${this.elId}-tree-node-expand`)
|
||||
}
|
||||
uni.$off('updateKey')
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.ly-tree {
|
||||
position: relative;
|
||||
cursor: default;
|
||||
background: #FFF;
|
||||
color: #606266;
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.ly-tree.is-empty {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
/* lyEmpty-start */
|
||||
.ly-empty {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 100rpx;
|
||||
}
|
||||
|
||||
/* lyEmpty-end */
|
||||
|
||||
/* lyLoader-start */
|
||||
.ly-loader {
|
||||
margin-top: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.ly-loader-inner,
|
||||
.ly-loader-inner:before,
|
||||
.ly-loader-inner:after {
|
||||
background: #efefef;
|
||||
animation: load 1s infinite ease-in-out;
|
||||
width: .5em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.ly-loader-inner:before,
|
||||
.ly-loader-inner:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
content: '';
|
||||
}
|
||||
|
||||
.ly-loader-inner:before {
|
||||
left: -1em;
|
||||
}
|
||||
|
||||
.ly-loader-inner {
|
||||
text-indent: -9999em;
|
||||
position: relative;
|
||||
font-size: 22rpx;
|
||||
animation-delay: 0.16s;
|
||||
}
|
||||
|
||||
.ly-loader-inner:after {
|
||||
left: 1em;
|
||||
animation-delay: 0.32s;
|
||||
}
|
||||
|
||||
/* lyLoader-end */
|
||||
|
||||
@keyframes load {
|
||||
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
box-shadow: 0 0 #efefef;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
40% {
|
||||
box-shadow: 0 -1.5em #efefef;
|
||||
height: 1.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
538
pages/my/subordinate/subordinateTree/model/node.js
Normal file
538
pages/my/subordinate/subordinateTree/model/node.js
Normal file
@@ -0,0 +1,538 @@
|
||||
import {
|
||||
markNodeData,
|
||||
objectAssign,
|
||||
arrayFindIndex,
|
||||
getChildState,
|
||||
reInitChecked,
|
||||
getPropertyFromData,
|
||||
isNull,
|
||||
NODE_KEY
|
||||
} from '../tool/util';
|
||||
|
||||
const getStore = function(store) {
|
||||
let thisStore = store;
|
||||
|
||||
return function() {
|
||||
return thisStore;
|
||||
}
|
||||
}
|
||||
|
||||
let nodeIdSeed = 0;
|
||||
|
||||
export default class Node {
|
||||
constructor(options) {
|
||||
this.time = new Date().getTime();
|
||||
this.id = nodeIdSeed++;
|
||||
this.text = null;
|
||||
this.checked = false;
|
||||
this.indeterminate = false;
|
||||
this.data = null;
|
||||
this.expanded = false;
|
||||
this.parentId = null;
|
||||
this.visible = true;
|
||||
this.isCurrent = false;
|
||||
|
||||
for (let name in options) {
|
||||
if (options.hasOwnProperty(name)) {
|
||||
if (name === 'store') {
|
||||
this.store = getStore(options[name]);
|
||||
} else {
|
||||
this[name] = options[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.store()) {
|
||||
throw new Error('[Node]store is required!');
|
||||
}
|
||||
|
||||
// internal
|
||||
this.level = 0;
|
||||
this.loaded = false;
|
||||
this.childNodesId = [];
|
||||
this.loading = false;
|
||||
this.label = getPropertyFromData(this, 'label');
|
||||
this.key = this._getKey();
|
||||
this.disabled = getPropertyFromData(this, 'disabled');
|
||||
this.nextSibling = null;
|
||||
this.previousSibling = null;
|
||||
this.icon = '';
|
||||
|
||||
this._handleParentAndLevel();
|
||||
this._handleProps();
|
||||
this._handleExpand();
|
||||
this._handleCurrent();
|
||||
|
||||
if (this.store().lazy) {
|
||||
this.store()._initDefaultCheckedNode(this);
|
||||
}
|
||||
|
||||
this.updateLeafState();
|
||||
}
|
||||
|
||||
_getKey() {
|
||||
if (!this.data || Array.isArray(this.data)) return null;
|
||||
|
||||
if (typeof this.data === 'object') {
|
||||
const nodeKey = this.store().key;
|
||||
const key = this.data[nodeKey];
|
||||
|
||||
if (typeof key === 'undefined') {
|
||||
throw new Error(`您配置的node-key为"${nodeKey}",但数据中并未找到对应"${nodeKey}"属性的值,请检查node-key的配置是否合理`)
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
throw new Error('不合法的data数据');
|
||||
}
|
||||
|
||||
_handleParentAndLevel() {
|
||||
if (this.parentId !== null) {
|
||||
let parent = this.getParent(this.parentId);
|
||||
|
||||
if (this.store().isInjectParentInNode) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
// 由于这里做了修改,默认第一个对象不会被注册到nodesMap中,所以找不到parent会报错,所以默认parent的level是0
|
||||
if (!parent) {
|
||||
parent = {
|
||||
level: 0
|
||||
}
|
||||
} else {
|
||||
const parentChildNodes = parent.getChildNodes(parent.childNodesId);
|
||||
const index = parent.childNodesId.indexOf(this.key);
|
||||
this.nextSibling = index > -1 ? parentChildNodes[index + 1] : null;
|
||||
this.previousSibling = index > 0 ? parentChildNodes[index - 1] : null;
|
||||
}
|
||||
this.level = parent.level + 1;
|
||||
}
|
||||
}
|
||||
|
||||
_handleProps() {
|
||||
const props = this.store().props;
|
||||
|
||||
if (this.store().showNodeIcon) {
|
||||
if (props && typeof props.icon !== 'undefined') {
|
||||
this.icon = getPropertyFromData(this, 'icon');
|
||||
} else {
|
||||
console.warn('请配置props属性中的"icon"字段')
|
||||
}
|
||||
}
|
||||
|
||||
this.store().registerNode(this);
|
||||
|
||||
if (props && typeof props.isLeaf !== 'undefined') {
|
||||
const isLeaf = getPropertyFromData(this, 'isLeaf');
|
||||
if (typeof isLeaf === 'boolean') {
|
||||
this.isLeafByUser = isLeaf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleExpand() {
|
||||
if (this.store().lazy !== true && this.data) {
|
||||
this.setData(this.data);
|
||||
|
||||
if (this.store().defaultExpandAll) {
|
||||
this.expanded = true;
|
||||
}
|
||||
} else if (this.level > 0 && this.store().lazy && this.store().defaultExpandAll) {
|
||||
this.expand();
|
||||
}
|
||||
|
||||
if (!Array.isArray(this.data)) {
|
||||
markNodeData(this, this.data);
|
||||
}
|
||||
|
||||
if (!this.data) return;
|
||||
|
||||
const defaultExpandedKeys = this.store().defaultExpandedKeys;
|
||||
const key = this.store().key;
|
||||
if (key && defaultExpandedKeys && defaultExpandedKeys.indexOf(this.key) !== -1) {
|
||||
this.expand(null, this.store().autoExpandparent);
|
||||
}
|
||||
}
|
||||
|
||||
_handleCurrent() {
|
||||
const key = this.store().key;
|
||||
|
||||
if (key && this.store().currentNodeKey !== undefined && this.key === this.store().currentNodeKey) {
|
||||
this.store().currentNode = this;
|
||||
this.store().currentNode.isCurrent = true;
|
||||
}
|
||||
}
|
||||
|
||||
destroyStore() {
|
||||
getStore(null)
|
||||
}
|
||||
|
||||
setData(data) {
|
||||
if (!Array.isArray(data)) {
|
||||
markNodeData(this, data);
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.childNodesId = [];
|
||||
|
||||
let children;
|
||||
if (this.level === 0 && Array.isArray(this.data)) {
|
||||
children = this.data;
|
||||
} else {
|
||||
children = getPropertyFromData(this, 'children') || [];
|
||||
}
|
||||
|
||||
for (let i = 0, j = children.length; i < j; i++) {
|
||||
this.insertChild({
|
||||
data: children[i]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
contains(target, deep = true) {
|
||||
const walk = function(parent) {
|
||||
const children = parent.getChildNodes(parent.childNodesId) || [];
|
||||
let result = false;
|
||||
for (let i = 0, j = children.length; i < j; i++) {
|
||||
const child = children[i];
|
||||
if (child === target || (deep && walk(child))) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
return walk(this);
|
||||
}
|
||||
|
||||
remove() {
|
||||
if (this.parentId !== null) {
|
||||
const parent = this.getParent(this.parentId);
|
||||
parent.removeChild(this);
|
||||
}
|
||||
}
|
||||
|
||||
insertChild(child, index, batch) {
|
||||
if (!child) throw new Error('insertChild error: child is required.');
|
||||
|
||||
if (!(child instanceof Node)) {
|
||||
if (!batch) {
|
||||
const children = this.getChildren(true);
|
||||
if (children.indexOf(child.data) === -1) {
|
||||
if (typeof index === 'undefined' || index < 0) {
|
||||
children.push(child.data);
|
||||
} else {
|
||||
children.splice(index, 0, child.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
objectAssign(child, {
|
||||
parentId: isNull(this.key) ? '' : this.key,
|
||||
store: this.store()
|
||||
});
|
||||
child = new Node(child);
|
||||
}
|
||||
|
||||
child.level = this.level + 1;
|
||||
|
||||
if (typeof index === 'undefined' || index < 0) {
|
||||
this.childNodesId.push(child.key);
|
||||
} else {
|
||||
this.childNodesId.splice(index, 0, child.key);
|
||||
}
|
||||
|
||||
this.updateLeafState();
|
||||
}
|
||||
|
||||
insertBefore(child, ref) {
|
||||
let index;
|
||||
if (ref) {
|
||||
index = this.childNodesId.indexOf(ref.id);
|
||||
}
|
||||
this.insertChild(child, index);
|
||||
}
|
||||
|
||||
insertAfter(child, ref) {
|
||||
let index;
|
||||
if (ref) {
|
||||
index = this.childNodesId.indexOf(ref.id);
|
||||
if (index !== -1) index += 1;
|
||||
}
|
||||
this.insertChild(child, index);
|
||||
}
|
||||
|
||||
removeChild(child) {
|
||||
const children = this.getChildren() || [];
|
||||
const dataIndex = children.indexOf(child.data);
|
||||
if (dataIndex > -1) {
|
||||
children.splice(dataIndex, 1);
|
||||
}
|
||||
|
||||
const index = this.childNodesId.indexOf(child.key);
|
||||
|
||||
if (index > -1) {
|
||||
this.store() && this.store().deregisterNode(child);
|
||||
child.parentId = null;
|
||||
this.childNodesId.splice(index, 1);
|
||||
}
|
||||
|
||||
this.updateLeafState();
|
||||
}
|
||||
|
||||
removeChildByData(data) {
|
||||
let targetNode = null;
|
||||
|
||||
for (let i = 0; i < this.childNodesId.length; i++) {
|
||||
let node = this.getChildNodes(this.childNodesId);
|
||||
if (node[i].data === data) {
|
||||
targetNode = node[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetNode) {
|
||||
this.removeChild(targetNode);
|
||||
}
|
||||
}
|
||||
|
||||
// 为了避免APP端parent嵌套结构导致报错,这里parent需要从nodesMap中获取
|
||||
getParent(parentId) {
|
||||
try {
|
||||
if (!parentId.toString()) return null;
|
||||
return this.store().nodesMap[parentId];
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 为了避免APP端childNodes嵌套结构导致报错,这里childNodes需要从nodesMap中获取
|
||||
getChildNodes(childNodesId) {
|
||||
let childNodes = [];
|
||||
if (childNodesId.length === 0) return childNodes;
|
||||
childNodesId.forEach((key) => {
|
||||
childNodes.push(this.store().nodesMap[key]);
|
||||
})
|
||||
return childNodes;
|
||||
}
|
||||
|
||||
expand(callback, expandparent) {
|
||||
const done = () => {
|
||||
if (expandparent) {
|
||||
let parent = this.getParent(this.parentId);
|
||||
while (parent && parent.level > 0) {
|
||||
parent.expanded = true;
|
||||
parent = this.getParent(parent.parentId);
|
||||
}
|
||||
}
|
||||
this.expanded = true;
|
||||
if (callback) callback();
|
||||
};
|
||||
|
||||
if (this.shouldLoadData()) {
|
||||
this.loadData(function(data) {
|
||||
if (Array.isArray(data)) {
|
||||
if (this.checked) {
|
||||
this.setChecked(true, true);
|
||||
} else if (!this.store().checkStrictly) {
|
||||
reInitChecked(this);
|
||||
}
|
||||
done();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
doCreateChildren(array, defaultProps = {}) {
|
||||
array.forEach((item) => {
|
||||
this.insertChild(objectAssign({
|
||||
data: item
|
||||
}, defaultProps), undefined, true);
|
||||
});
|
||||
}
|
||||
|
||||
collapse() {
|
||||
this.expanded = false;
|
||||
}
|
||||
|
||||
shouldLoadData() {
|
||||
return this.store().lazy === true && this.store().load && !this.loaded;
|
||||
}
|
||||
|
||||
updateLeafState() {
|
||||
if (this.store().lazy === true && this.loaded !== true && typeof this.isLeafByUser !== 'undefined') {
|
||||
this.isLeaf = this.isLeafByUser;
|
||||
return;
|
||||
}
|
||||
const childNodesId = this.childNodesId;
|
||||
if (!this.store().lazy || (this.store().lazy === true && this.loaded === true)) {
|
||||
this.isLeaf = !childNodesId || childNodesId.length === 0;
|
||||
return;
|
||||
}
|
||||
this.isLeaf = false;
|
||||
}
|
||||
|
||||
setChecked(value, deep, recursion, passValue) {
|
||||
this.indeterminate = value === 'half';
|
||||
this.checked = value === true;
|
||||
|
||||
if (this.checked && this.store().expandOnCheckNode) {
|
||||
this.expand(null, true)
|
||||
}
|
||||
|
||||
if (this.store().checkStrictly) return;
|
||||
if (this.store().showRadio) return;
|
||||
|
||||
if (!(this.shouldLoadData() && !this.store().checkDescendants)) {
|
||||
let childNodes = this.getChildNodes(this.childNodesId);
|
||||
let {
|
||||
all,
|
||||
allWithoutDisable
|
||||
} = getChildState(childNodes);
|
||||
|
||||
if (!this.isLeaf && (!all && allWithoutDisable)) {
|
||||
this.checked = false;
|
||||
value = false;
|
||||
}
|
||||
|
||||
const handleDescendants = () => {
|
||||
if (deep) {
|
||||
let childNodes = this.getChildNodes(this.childNodesId)
|
||||
for (let i = 0, j = childNodes.length; i < j; i++) {
|
||||
const child = childNodes[i];
|
||||
passValue = passValue || value !== false;
|
||||
const isCheck = child.disabled ? child.checked : passValue;
|
||||
child.setChecked(isCheck, deep, true, passValue);
|
||||
}
|
||||
const {
|
||||
half,
|
||||
all
|
||||
} = getChildState(childNodes);
|
||||
|
||||
if (!all) {
|
||||
this.checked = all;
|
||||
this.indeterminate = half;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.shouldLoadData()) {
|
||||
this.loadData(() => {
|
||||
handleDescendants();
|
||||
reInitChecked(this);
|
||||
}, {
|
||||
checked: value !== false
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
handleDescendants();
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.parentId) return;
|
||||
|
||||
let parent = this.getParent(this.parentId);
|
||||
if (parent && parent.level === 0) return;
|
||||
|
||||
if (!recursion) {
|
||||
reInitChecked(parent);
|
||||
}
|
||||
}
|
||||
|
||||
setRadioChecked(value) {
|
||||
const allNodes = this.store()._getAllNodes().sort((a, b) => b.level - a.level);
|
||||
allNodes.forEach(node => node.setChecked(false, false));
|
||||
this.checked = value === true;
|
||||
}
|
||||
|
||||
getChildren(forceInit = false) {
|
||||
if (this.level === 0) return this.data;
|
||||
const data = this.data;
|
||||
if (!data) return null;
|
||||
|
||||
const props = this.store().props;
|
||||
let children = 'children';
|
||||
if (props) {
|
||||
children = props.children || 'children';
|
||||
}
|
||||
|
||||
if (data[children] === undefined) {
|
||||
data[children] = null;
|
||||
}
|
||||
|
||||
if (forceInit && !data[children]) {
|
||||
data[children] = [];
|
||||
}
|
||||
|
||||
return data[children];
|
||||
}
|
||||
|
||||
updateChildren() {
|
||||
let childNodes = this.getChildNodes(this.childNodesId);
|
||||
const newData = this.getChildren() || [];
|
||||
const oldData = childNodes.map((node) => node.data);
|
||||
|
||||
const newDataMap = {};
|
||||
const newNodes = [];
|
||||
|
||||
newData.forEach((item, index) => {
|
||||
const key = item[NODE_KEY];
|
||||
const isNodeExists = !!key && arrayFindIndex(oldData, data => data[NODE_KEY] === key) >= 0;
|
||||
if (isNodeExists) {
|
||||
newDataMap[key] = {
|
||||
index,
|
||||
data: item
|
||||
};
|
||||
} else {
|
||||
newNodes.push({
|
||||
index,
|
||||
data: item
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.store().lazy) {
|
||||
oldData.forEach((item) => {
|
||||
if (!newDataMap[item[NODE_KEY]]) this.removeChildByData(item);
|
||||
});
|
||||
}
|
||||
|
||||
newNodes.forEach(({
|
||||
index,
|
||||
data
|
||||
}) => {
|
||||
this.insertChild({
|
||||
data
|
||||
}, index);
|
||||
});
|
||||
|
||||
this.updateLeafState();
|
||||
}
|
||||
|
||||
loadData(callback, defaultProps = {}) {
|
||||
if (this.store().lazy === true &&
|
||||
this.store().load && !this.loaded &&
|
||||
(!this.loading || Object.keys(defaultProps).length)
|
||||
) {
|
||||
this.loading = true;
|
||||
|
||||
const resolve = (children) => {
|
||||
this.loaded = true;
|
||||
this.loading = false;
|
||||
this.childNodesId = [];
|
||||
this.doCreateChildren(children, defaultProps);
|
||||
this.updateLeafState();
|
||||
|
||||
callback && callback.call(this, children);
|
||||
};
|
||||
|
||||
this.store().load(this, resolve);
|
||||
} else {
|
||||
callback && callback.call(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
428
pages/my/subordinate/subordinateTree/model/tree-store.js
Normal file
428
pages/my/subordinate/subordinateTree/model/tree-store.js
Normal file
@@ -0,0 +1,428 @@
|
||||
import Node from './node';
|
||||
import {
|
||||
getNodeKey,
|
||||
getPropertyFromData
|
||||
} from '../tool/util';
|
||||
|
||||
export default class TreeStore {
|
||||
constructor(options) {
|
||||
this.ready = false;
|
||||
this.currentNode = null;
|
||||
this.currentNodeKey = null;
|
||||
|
||||
Object.assign(this, options);
|
||||
|
||||
if (!this.key) {
|
||||
throw new Error('[Tree] nodeKey is required');
|
||||
}
|
||||
|
||||
this.nodesMap = {};
|
||||
this.root = new Node({
|
||||
data: this.data,
|
||||
store: this
|
||||
});
|
||||
|
||||
if (this.lazy && this.load) {
|
||||
const loadFn = this.load;
|
||||
loadFn(this.root, (data) => {
|
||||
this.root.doCreateChildren(data);
|
||||
this._initDefaultCheckedNodes();
|
||||
this.ready = true;
|
||||
uni.$emit('updateKey') //加了异步才会展示数据
|
||||
});
|
||||
} else {
|
||||
this._initDefaultCheckedNodes();
|
||||
this.ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
getReady() {
|
||||
return this.ready
|
||||
}
|
||||
filter(value, data) {
|
||||
const filterNodeMethod = this.filterNodeMethod;
|
||||
const lazy = this.lazy;
|
||||
const _self = this;
|
||||
const traverse = function(node) {
|
||||
const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(
|
||||
node.childNodesId);
|
||||
|
||||
childNodes.forEach((child) => {
|
||||
if (data && typeof data === 'object') {
|
||||
let nodePath = _self.getNodePath(child.data);
|
||||
if (!nodePath.some(pathItem => pathItem[_self.key] === data[_self.key])) {
|
||||
child.visible = false;
|
||||
traverse(child);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (_self.childVisibleForFilterNode) {
|
||||
let parent = child.getParent(child.parentId);
|
||||
child.visible = filterNodeMethod.call(child, value, child.data, child) || (parent &&
|
||||
parent.visible);
|
||||
} else {
|
||||
child.visible = filterNodeMethod.call(child, value, child.data, child);
|
||||
}
|
||||
|
||||
traverse(child);
|
||||
});
|
||||
|
||||
if (!node.visible && childNodes.length) {
|
||||
let allHidden = true;
|
||||
allHidden = !childNodes.some(child => child.visible);
|
||||
|
||||
if (node.root) {
|
||||
node.root.visible = allHidden === false;
|
||||
} else {
|
||||
node.visible = allHidden === false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!value) return;
|
||||
|
||||
if (node.visible && !node.isLeaf && !lazy) node.expand();
|
||||
};
|
||||
|
||||
traverse(this);
|
||||
}
|
||||
|
||||
setData(newVal) {
|
||||
const instanceChanged = newVal !== this.root.data;
|
||||
if (instanceChanged) {
|
||||
this.root.setData(newVal);
|
||||
this._initDefaultCheckedNodes();
|
||||
} else {
|
||||
this.root.updateChildren();
|
||||
}
|
||||
}
|
||||
|
||||
getNode(data) {
|
||||
if (data instanceof Node) return data;
|
||||
const key = typeof data !== 'object' ? data : getNodeKey(this.key, data);
|
||||
if (!key) return null;
|
||||
return this.nodesMap[key] || null;
|
||||
}
|
||||
|
||||
insertBefore(data, refData) {
|
||||
const refNode = this.getNode(refData);
|
||||
let parent = refNode.getParent(refNode.parentId);
|
||||
parent.insertBefore({
|
||||
data
|
||||
}, refNode);
|
||||
}
|
||||
|
||||
insertAfter(data, refData) {
|
||||
const refNode = this.getNode(refData);
|
||||
let parent = refNode.getParent(refNode.parentId);
|
||||
parent.insertAfter({
|
||||
data
|
||||
}, refNode);
|
||||
}
|
||||
|
||||
remove(data) {
|
||||
const node = this.getNode(data);
|
||||
|
||||
if (node && node.parentId !== null) {
|
||||
let parent = node.getParent(node.parentId);
|
||||
if (node === this.currentNode) {
|
||||
this.currentNode = null;
|
||||
}
|
||||
parent.removeChild(node);
|
||||
}
|
||||
}
|
||||
|
||||
append(data, parentData) {
|
||||
const parentNode = parentData ? this.getNode(parentData) : this.root;
|
||||
|
||||
if (parentNode) {
|
||||
parentNode.insertChild({
|
||||
data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_initDefaultCheckedNodes() {
|
||||
const defaultCheckedKeys = this.defaultCheckedKeys || [];
|
||||
const nodesMap = this.nodesMap;
|
||||
let checkedKeyfromData = [];
|
||||
let totalCheckedKeys = []
|
||||
|
||||
for (let key in nodesMap) {
|
||||
let checked = getPropertyFromData(nodesMap[key], 'checked') || false;
|
||||
checked && checkedKeyfromData.push(key);
|
||||
}
|
||||
|
||||
totalCheckedKeys = Array.from(new Set([...defaultCheckedKeys, ...checkedKeyfromData]));
|
||||
totalCheckedKeys.forEach((checkedKey) => {
|
||||
const node = nodesMap[checkedKey];
|
||||
|
||||
if (node) {
|
||||
node.setChecked(true, !this.checkStrictly);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_initDefaultCheckedNode(node) {
|
||||
const defaultCheckedKeys = this.defaultCheckedKeys || [];
|
||||
|
||||
if (defaultCheckedKeys.indexOf(node.key) !== -1) {
|
||||
node.setChecked(true, !this.checkStrictly);
|
||||
}
|
||||
}
|
||||
|
||||
toggleExpendAll(isExpandAll) {
|
||||
const allNodes = this._getAllNodes();
|
||||
|
||||
allNodes.forEach(item => {
|
||||
const node = this.getNode(item.key);
|
||||
|
||||
if (node) isExpandAll ? node.expand() : node.collapse();
|
||||
});
|
||||
}
|
||||
|
||||
setCheckAll(isCkeckAll) {
|
||||
const allNodes = this._getAllNodes();
|
||||
|
||||
allNodes.forEach(item => {
|
||||
item.setChecked(isCkeckAll, false);
|
||||
});
|
||||
}
|
||||
|
||||
setDefaultCheckedKey(newVal) {
|
||||
if (newVal !== this.defaultCheckedKeys) {
|
||||
this.defaultCheckedKeys = newVal;
|
||||
this._initDefaultCheckedNodes();
|
||||
}
|
||||
}
|
||||
|
||||
registerNode(node) {
|
||||
|
||||
const key = this.key;
|
||||
if (!key || !node || !node.data) return;
|
||||
|
||||
const nodeKey = node.key;
|
||||
if (nodeKey !== undefined) this.nodesMap[node.key] = node;
|
||||
}
|
||||
|
||||
deregisterNode(node) {
|
||||
const key = this.key;
|
||||
if (!key || !node || !node.data) return;
|
||||
|
||||
let childNodes = node.getChildNodes(node.childNodesId);
|
||||
childNodes.forEach(child => {
|
||||
this.deregisterNode(child);
|
||||
});
|
||||
|
||||
delete this.nodesMap[node.key];
|
||||
}
|
||||
|
||||
getNodePath(data) {
|
||||
if (!this.key) throw new Error('[Tree] nodeKey is required in getNodePath');
|
||||
const node = this.getNode(data);
|
||||
if (!node) return [];
|
||||
|
||||
const path = [node.data];
|
||||
let parent = node.getParent(node.parentId);
|
||||
while (parent && parent !== this.root) {
|
||||
path.push(parent.data);
|
||||
parent = parent.getParent(parent.parentId);
|
||||
}
|
||||
return path.reverse();
|
||||
}
|
||||
|
||||
getCheckedNodes(leafOnly = false, includeHalfChecked = false) {
|
||||
const checkedNodes = [];
|
||||
const traverse = function(node) {
|
||||
const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(
|
||||
node.childNodesId);
|
||||
|
||||
childNodes.forEach((child) => {
|
||||
if ((child.checked || (includeHalfChecked && child.indeterminate)) && (!leafOnly || (
|
||||
leafOnly && child.isLeaf))) {
|
||||
checkedNodes.push(child.data);
|
||||
}
|
||||
|
||||
traverse(child);
|
||||
});
|
||||
};
|
||||
|
||||
traverse(this);
|
||||
|
||||
return checkedNodes;
|
||||
}
|
||||
|
||||
getCheckedKeys(leafOnly = false, includeHalfChecked = false) {
|
||||
return this.getCheckedNodes(leafOnly, includeHalfChecked).map((data) => (data || {})[this.key]);
|
||||
}
|
||||
|
||||
getHalfCheckedNodes() {
|
||||
const nodes = [];
|
||||
const traverse = function(node) {
|
||||
const childNodes = node.root ? node.root.getChildNodes(node.root.childNodesId) : node.getChildNodes(
|
||||
node.childNodesId);
|
||||
|
||||
childNodes.forEach((child) => {
|
||||
if (child.indeterminate) {
|
||||
nodes.push(child.data);
|
||||
}
|
||||
|
||||
traverse(child);
|
||||
});
|
||||
};
|
||||
|
||||
traverse(this);
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
getHalfCheckedKeys() {
|
||||
return this.getHalfCheckedNodes().map((data) => (data || {})[this.key]);
|
||||
}
|
||||
|
||||
_getAllNodes() {
|
||||
const allNodes = [];
|
||||
const nodesMap = this.nodesMap;
|
||||
for (let nodeKey in nodesMap) {
|
||||
if (nodesMap.hasOwnProperty(nodeKey)) {
|
||||
allNodes.push(nodesMap[nodeKey]);
|
||||
}
|
||||
}
|
||||
|
||||
return allNodes;
|
||||
}
|
||||
|
||||
updateChildren(key, data) {
|
||||
const node = this.nodesMap[key];
|
||||
if (!node) return;
|
||||
const childNodes = node.getChildNodes(node.childNodesId);
|
||||
for (let i = childNodes.length - 1; i >= 0; i--) {
|
||||
const child = childNodes[i];
|
||||
this.remove(child.data);
|
||||
}
|
||||
for (let i = 0, j = data.length; i < j; i++) {
|
||||
const child = data[i];
|
||||
this.append(child, node.data);
|
||||
}
|
||||
}
|
||||
|
||||
_setCheckedKeys(key, leafOnly = false, checkedKeys) {
|
||||
const allNodes = this._getAllNodes().sort((a, b) => b.level - a.level);
|
||||
const cache = Object.create(null);
|
||||
const keys = Object.keys(checkedKeys);
|
||||
allNodes.forEach(node => node.setChecked(false, false));
|
||||
for (let i = 0, j = allNodes.length; i < j; i++) {
|
||||
const node = allNodes[i];
|
||||
let nodeKey = node.data[key];
|
||||
|
||||
if (typeof nodeKey === 'undefined') continue;
|
||||
|
||||
nodeKey = nodeKey.toString();
|
||||
let checked = keys.indexOf(nodeKey) > -1;
|
||||
if (!checked) {
|
||||
if (node.checked && !cache[nodeKey]) {
|
||||
node.setChecked(false, false);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = node.getParent(node.parentId);
|
||||
while (parent && parent.level > 0) {
|
||||
cache[parent.data[key]] = true;
|
||||
parent = parent.getParent(parent.parentId);
|
||||
}
|
||||
|
||||
if (node.isLeaf || this.checkStrictly) {
|
||||
node.setChecked(true, false);
|
||||
continue;
|
||||
}
|
||||
node.setChecked(true, true);
|
||||
|
||||
if (leafOnly) {
|
||||
node.setChecked(false, false);
|
||||
const traverse = function(node) {
|
||||
const childNodes = node.getChildNodes(node.childNodesId);
|
||||
childNodes.forEach((child) => {
|
||||
if (!child.isLeaf) {
|
||||
child.setChecked(false, false);
|
||||
}
|
||||
traverse(child);
|
||||
});
|
||||
};
|
||||
traverse(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCheckedNodes(array, leafOnly = false) {
|
||||
const key = this.key;
|
||||
const checkedKeys = {};
|
||||
array.forEach((item) => {
|
||||
checkedKeys[(item || {})[key]] = true;
|
||||
});
|
||||
|
||||
this._setCheckedKeys(key, leafOnly, checkedKeys);
|
||||
}
|
||||
|
||||
setCheckedKeys(keys, leafOnly = false) {
|
||||
this.defaultCheckedKeys = keys;
|
||||
const key = this.key;
|
||||
const checkedKeys = {};
|
||||
keys.forEach((key) => {
|
||||
checkedKeys[key] = true;
|
||||
});
|
||||
|
||||
this._setCheckedKeys(key, leafOnly, checkedKeys);
|
||||
}
|
||||
|
||||
setDefaultExpandedKeys(keys) {
|
||||
keys = keys || [];
|
||||
this.defaultExpandedKeys = keys;
|
||||
|
||||
keys.forEach((key) => {
|
||||
const node = this.getNode(key);
|
||||
if (node) node.expand(null, this.autoExpandParent);
|
||||
});
|
||||
}
|
||||
|
||||
setChecked(data, checked, deep) {
|
||||
const node = this.getNode(data);
|
||||
|
||||
if (node) {
|
||||
node.setChecked(!!checked, deep);
|
||||
}
|
||||
}
|
||||
|
||||
getCurrentNode() {
|
||||
return this.currentNode;
|
||||
}
|
||||
|
||||
setCurrentNode(currentNode) {
|
||||
const prevCurrentNode = this.currentNode;
|
||||
if (prevCurrentNode) {
|
||||
prevCurrentNode.isCurrent = false;
|
||||
}
|
||||
this.currentNode = currentNode;
|
||||
this.currentNode.isCurrent = true;
|
||||
|
||||
this.expandCurrentNodeParent && this.currentNode.expand(null, true)
|
||||
}
|
||||
|
||||
setUserCurrentNode(node) {
|
||||
const key = node[this.key];
|
||||
const currNode = this.nodesMap[key];
|
||||
this.setCurrentNode(currNode);
|
||||
}
|
||||
|
||||
setCurrentNodeKey(key) {
|
||||
if (key === null || key === undefined) {
|
||||
this.currentNode && (this.currentNode.isCurrent = false);
|
||||
this.currentNode = null;
|
||||
return;
|
||||
}
|
||||
const node = this.getNode(key);
|
||||
if (node) {
|
||||
this.setCurrentNode(node);
|
||||
}
|
||||
}
|
||||
};
|
||||
115
pages/my/subordinate/subordinateTree/tool/util.js
Normal file
115
pages/my/subordinate/subordinateTree/tool/util.js
Normal file
@@ -0,0 +1,115 @@
|
||||
export const NODE_KEY = '$treeNodeId';
|
||||
|
||||
export const markNodeData = function(node, data) {
|
||||
if (!data || data[NODE_KEY]) return;
|
||||
Object.defineProperty(data, NODE_KEY, {
|
||||
value: node.id,
|
||||
enumerable: false,
|
||||
configurable: false,
|
||||
writable: false
|
||||
});
|
||||
};
|
||||
|
||||
export const getNodeKey = function(key, data) {
|
||||
if (!data) return null;
|
||||
if (!key) return data[NODE_KEY];
|
||||
return data[key];
|
||||
};
|
||||
|
||||
export const objectAssign = function(target) {
|
||||
for (let i = 1, j = arguments.length; i < j; i++) {
|
||||
let source = arguments[i] || {};
|
||||
for (let prop in source) {
|
||||
if (source.hasOwnProperty(prop)) {
|
||||
let value = source[prop];
|
||||
if (value !== undefined) {
|
||||
target[prop] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
// TODO: use native Array.find, Array.findIndex when IE support is dropped
|
||||
export const arrayFindIndex = function(arr, pred) {
|
||||
for (let i = 0; i !== arr.length; ++i) {
|
||||
if (pred(arr[i])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
export const getChildState = function(node) {
|
||||
let all = true;
|
||||
let none = true;
|
||||
let allWithoutDisable = true;
|
||||
for (let i = 0, j = node.length; i < j; i++) {
|
||||
const n = node[i];
|
||||
if (n.checked !== true || n.indeterminate) {
|
||||
all = false;
|
||||
if (!n.disabled) {
|
||||
allWithoutDisable = false;
|
||||
}
|
||||
}
|
||||
if (n.checked !== false || n.indeterminate) {
|
||||
none = false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
all,
|
||||
none,
|
||||
allWithoutDisable,
|
||||
half: !all && !none
|
||||
};
|
||||
};
|
||||
|
||||
export const reInitChecked = function(node) {
|
||||
if (!node || node.childNodesId.length === 0) return;
|
||||
|
||||
let childNodes = node.getChildNodes(node.childNodesId);
|
||||
const {
|
||||
all,
|
||||
none,
|
||||
half
|
||||
} = getChildState(childNodes);
|
||||
if (all) {
|
||||
node.checked = true;
|
||||
node.indeterminate = false;
|
||||
} else if (half) {
|
||||
node.checked = false;
|
||||
node.indeterminate = true;
|
||||
} else if (none) {
|
||||
node.checked = false;
|
||||
node.indeterminate = false;
|
||||
}
|
||||
|
||||
let parent = node.getParent(node.parentId);
|
||||
if (!parent || parent.level === 0) return;
|
||||
|
||||
if (!node.store().checkStrictly) {
|
||||
reInitChecked(parent);
|
||||
}
|
||||
};
|
||||
|
||||
export const getPropertyFromData = function(node, prop) {
|
||||
const props = node.store().props;
|
||||
const data = node.data || {};
|
||||
const config = props[prop];
|
||||
|
||||
if (typeof config === 'function') {
|
||||
return config(data, node);
|
||||
} else if (typeof config === 'string') {
|
||||
return data[config];
|
||||
} else if (typeof config === 'undefined') {
|
||||
const dataProp = data[prop];
|
||||
return dataProp === undefined ? '' : dataProp;
|
||||
}
|
||||
};
|
||||
|
||||
export const isNull = function(v) {
|
||||
return v === undefined || v === null || v === '';
|
||||
}
|
||||
Reference in New Issue
Block a user