初始提交

This commit is contained in:
2026-01-04 11:09:06 +08:00
commit 8fa31df250
1326 changed files with 213907 additions and 0 deletions

View File

@@ -0,0 +1,293 @@
<template>
<view class="allApp-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">
</u-search>
</view>
</view>
<mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
:up="upOption" :bottombar="false" :sticky="false">
<view>
<view class="usualList">
<view class=" caption u-m-b-20">常用流程</view>
<view class="u-flex u-flex-wrap">
<view class="item u-flex-col u-col-center" v-for="(item,i) in usualList" :key="i">
<text class="u-font-40 item-icon" :class="item.icon"
:style="{'background':item.iconBackground||'#008cff'}" @click="Jump(item)" />
<text class="u-font-24 u-line-1 item-text">{{item.fullName}}</text>
</view>
</view>
</view>
<u-sticky>
<CommonTabs :list="categoryList" @change="change" :current="current" ref="CommonTabs" isBoxShadow>
</CommonTabs>
</u-sticky>
<view class="allList u-m-t-20" v-if="allList.length">
<view v-for="(item,i) in allList" :key="i">
<view class="u-flex childList-item ">
<text class="u-font-40 item-icon" :class="item.icon"
:style="{'background':item.iconBackground||'#008cff'}" @click="Jump(item)" />
<text class="item-text u-m-l-28 u-m-r-28 u-line-2">{{item.fullName}}</text>
<view class="btnBox">
<u-button :custom-style="customStyle" @click="handelAdd(item)"
v-if="!item.isCommonFlow">添加
</u-button>
<u-button :custom-style="customStyle" type="error" @click="handelDel(item)" v-else>
移除
</u-button>
</view>
</view>
</view>
</view>
<JnpfEmpty v-else />
</view>
</mescroll-uni>
</view>
</template>
<script>
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
import CommonTabs from '@/components/CommonTabs'
import {
getFlowList,
getDataList,
getFlowUsualList,
setCommonFlow,
delUsual
} from '@/api/apply/apply.js'
import resources from '@/libs/resources.js'
export default {
mixins: [MescrollMixin], // 使用mixin
components: {
CommonTabs
},
props: {
categoryList: {
type: Array,
default () {
return [];
}
},
},
data() {
return {
usualList: [],
current: 0,
customStyle: {
width: "128rpx",
fontSize: "24rpx",
height: '60rpx'
},
downOption: {
use: true,
auto: true
},
upOption: {
page: {
num: 0,
size: 20,
time: null
},
empty: {
use: false,
icon: resources.message.nodata,
tip: this.$t('common.noData'),
fixed: true,
top: "860rpx",
},
textNoMore: this.$t('app.apply.noMoreData'),
},
category: '',
allList: [],
type: '1',
fullName: '',
keyword: ''
}
},
onLoad() {
this.userInfo = uni.getStorageSync('userInfo') || {}
this.init()
},
methods: {
search() {
this.searchTimer && clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.list = [];
this.menuList = [];
this.mescroll.resetUpScroll();
}, 300);
},
init() {
this.getFlowUsualList()
},
getFlowUsualList() {
let query = {
category: 'commonFlow',
flowType: 0,
systemId: this.userInfo.systemId
}
getFlowUsualList(query).then(res => {
this.usualList = res.data.list.map(o => {
const objectData = o.objectData ? JSON.parse(o.objectData) : {}
return {
...o,
...objectData
}
})
})
},
upCallback(page) {
let query = {
currentPage: page.num,
pageSize: page.size,
category: this.category == 0 ? '' : this.category,
flowType: 0,
keyword: this.keyword,
systemId: this.userInfo.systemId
}
getFlowList(query, {
load: page.num == 1
}).then(res => {
this.mescroll.endSuccess(res.data.list.length);
if (page.num == 1) this.allList = [];
const list = res.data.list || [];
this.allList = this.allList.concat(list);
}).catch(() => {
this.mescroll.endSuccess(0);
this.mescroll.endErr();
})
},
Jump(item) {
const config = {
id: "",
flowId: item.id,
opType: "-1",
};
uni.navigateTo({
url: "/pages/workFlow/flowBefore/index?config=" +
this.jnpf.base64.encode(JSON.stringify(config))
});
},
handelAdd(item) {
setCommonFlow(item.id).then(res => {
this.usualList.push(item)
item.isCommonFlow = true
uni.$emit('updateUsualList')
uni.showToast({
title: res.msg
})
})
},
handelDel(item) {
setCommonFlow(item.id).then(res => {
item.isCommonFlow = false
this.getFlowUsualList()
uni.$emit('updateUsualList')
uni.showToast({
title: res.msg
})
})
},
change(index) {
this.current = index;
this.keyword = ""
this.category = !this.categoryList[index].id ? '' : this.categoryList[index].id
this.allList = [];
this.mescroll.resetUpScroll()
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.allApp-v {
.notice-warp {
height: 114rpx;
.search-box {
padding: 20rpx;
}
}
.caption {
font-size: 36rpx;
line-height: 80rpx;
padding: 0 32rpx;
.tip {
margin-left: 20rpx;
font-size: 24rpx;
color: #c6c6c6;
}
}
.tabs_box {
width: 100%;
.sticky {
width: 750rpx;
height: 120rpx;
color: #fff;
padding-right: 32rpx;
}
}
.usualList {
margin-top: 4.2rem;
margin-bottom: 20rpx;
background-color: #FFFFFF;
.item {
margin-bottom: 32rpx;
width: 25%;
.item-icon {
width: 88rpx;
height: 88rpx;
margin-bottom: 8rpx;
line-height: 88rpx;
text-align: center;
border-radius: 30rpx;
color: #fff;
font-size: 40rpx;
}
.item-text {
width: 100%;
text-align: center;
padding: 0 16rpx;
}
}
}
.allList {
padding: 0rpx 32rpx 28rpx;
background-color: #FFFFFF;
.childList-item {
align-items: center;
padding: 28rpx 0 0 0;
.item-text {
width: calc(100% - 216rpx);
}
.item-icon {
width: 88rpx;
height: 88rpx;
line-height: 88rpx;
text-align: center;
border-radius: 30rpx;
color: #FFFFFF;
flex-shrink: 0;
font-size: 40rpx;
}
}
}
}
</style>

View File

@@ -0,0 +1,165 @@
<template>
<view class="viewData-v">
<view class="notice-warp">
<view class="search-box">
<u-search v-model="keyword" height="72" :show-action="false" @change="search" bg-color="#f0f2f6"
shape="square">
</u-search>
</view>
</view>
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :sticky="true"
:down="downOption" :up="upOption" top="60">
<view class="u-flex-col tableList">
<view class="u-flex list-card" v-for="(item,index) in list" :key="index">
<view class="u-flex-col fieldContent u-m-l-10">
<view v-for="(column,c) in onLoadData.columnOptions" :key="c" class="fieldList u-line-1 u-flex">
<view class="val" v-if="column.ifShow">{{column.label+':'}} {{item[column.value]}}</view>
</view>
</view>
</view>
</view>
</mescroll-body>
</view>
</template>
<script>
import {
getPopSelect
} from '@/api/common.js'
import resources from '@/libs/resources.js'
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
export default {
mixins: [MescrollMixin],
data() {
return {
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: "300rpx",
},
textNoMore: this.$t('app.apply.noMoreData'),
},
list: [],
columnOptions: '',
onLoadData: {},
keyword: '',
listQuery: {
keyword: ''
}
}
},
onLoad(e) {
this.onLoadData = JSON.parse(decodeURIComponent(e.data));
this.columnOptions = this.onLoadData.columnOptions.map(o => o.value).join(',')
},
methods: {
upCallback(page) {
const paramList = this.onLoadData.templateJson
let query = {
...this.listQuery,
currentPage: page.num,
pageSize: 20,
interfaceId: this.onLoadData.interfaceId,
columnOptions: this.columnOptions,
paramList
}
getPopSelect(this.onLoadData.interfaceId, query, {
load: page.num == 1
}).then(res => {
this.mescroll.endSuccess(res.data.list.length);
if (page.num == 1) this.list = [];
this.list = this.list.concat(res.data.list);
}).catch(() => {
this.mescroll.endErr();
})
},
search() {
// 节流,避免输入过快多次请求
this.searchTimer && clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.list = [];
this.listQuery.keyword = this.keyword
this.listQuery.currentPage = 1
this.mescroll.resetUpScroll();
}, 300)
},
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.viewData-v {
width: 100%;
height: 100%;
padding-bottom: 106rpx;
.notice-warp {
height: 3.5rem;
}
.tableList {
padding: 0 20rpx;
.list-card {
background-color: #fff;
width: 100%;
border-radius: 8rpx;
margin-top: 20rpx;
padding: 20rpx 20rpx;
.fieldContent {
width: 100%;
margin-top: -14rpx;
.fieldList {
// width: 752rpx;
.key {
width: 136rpx;
margin-right: 10rpx;
text-align: right;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
line-height: 60rpx;
}
.val {
flex: 0.85;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
}
.nodata {
margin-top: 258rpx;
justify-content: center;
align-items: center;
image {
width: 280rpx;
height: 215rpx;
}
}
}
</style>

View File

@@ -0,0 +1,291 @@
<template>
<view class="candidateForm-v">
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :sticky="true"
:down="downOption" :up="upOption" :bottombar="false">
<view class="treeSelect-search search-box_sticky">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" @change="search" bg-color="#f0f2f6" shape="square">
</u-search>
<view class="alreadySelect">
<view class="alreadySelect__box u-flex-col">
<view class="alreadySelect_hd u-flex">
<view>{{$t('component.jnpf.common.selected')}}</view>
<view v-if="multiple" @click="setCheckAll" style="color: #2979ff;">
{{$t('component.jnpf.common.clearAll')}}
</view>
</view>
<view class="select__box u-flex-col">
<scroll-view scroll-y="true" style="max-height: 200px;">
<view class="u-flex select__list">
<view class="u-selectTag u-flex" v-for="(list,index) in selectList" :key="index">
<view class="avatar">
<u-avatar :src="baseURL+list.headIcon" mode="circle" size="mini">
</u-avatar>
</view>
<view class="u-font-24 content">
<view class="nameSty u-flex">
<view class="nameUp">
{{list.fullName}}
</view>
<u-icon name="close" class="close" @click='delSelect(index)'>
</u-icon>
</view>
<view class="organizeSty">{{list.organize}}</view>
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</view>
<view class="listTitle">全部数据</view>
</view>
<view class="mescroll_body">
<view style="" class="lists_box">
<view class="list-cell-txt u-border-bottom" v-for="(list,index) in list" :key="index"
@click="onSelect(list)">
<view class="avatar">
<u-avatar :src="baseURL+list.headIcon" mode="circle" size="default"></u-avatar>
</view>
<view class="u-font-30 content">
<view class="nameSty">{{list.fullName}}</view>
<view class="organizeSty">{{list.organize}}</view>
</view>
</view>
</view>
</view>
</mescroll-body>
<!-- 底部按钮 -->
<view class="flowBefore-actions">
<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>
</view>
</view>
</template>
<script>
import {
CandidateUser
} from '@/api/workFlow/flowBefore'
import resources from '@/libs/resources.js'
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
export default {
mixins: [MescrollMixin],
data() {
return {
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: "300rpx",
},
textNoMore: this.$t('app.apply.noMoreData'),
},
onLoadData: {},
list: [],
show: false,
keyword: '',
selectList: [],
multiple: true
}
},
onLoad(e) {
this.show = true
this.onLoadData = JSON.parse(decodeURIComponent(e.data));
this.selectList = this.onLoadData.selectList
this.delegateUser = this.onLoadData.delegateUser
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
upCallback(page) {
let query = {
currentPage: page.num,
pageSize: page.size,
keyword: this.keyword,
...this.onLoadData.formData,
nodeCode: this.onLoadData.nodeCode,
delegateUser: this.delegateUser
}
CandidateUser(this.onLoadData.taskId || 0, query, {
load: page.num == 1
}).then(res => {
this.mescroll.endSuccess(res.data.list.length);
if (page.num == 1) this.list = [];
const list = res.data.list;
this.list = this.list.concat(list);
}).catch(() => {
this.mescroll.endErr();
})
},
search() {
// 节流,避免输入过快多次请求
this.searchTimer && clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.list = [];
this.mescroll.resetUpScroll();
}, 300)
},
onSelect(list) {
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)
this.selectList = this.selectList.map(o => ({
nodeCode: this.onLoadData.nodeCode,
...o
}))
},
delSelect(index) {
this.selectList.splice(index, 1);
},
close() {
this.list = []
this.$emit('input', false);
},
getResult(type) {
uni.$emit(type, this.selectList, this.onLoadData.nodeCode);
uni.navigateBack()
},
setCheckAll() {
this.selectList = []
}
}
}
</script>
<style lang="scss" scoped>
.candidateForm-v {
padding-bottom: 88rpx;
.treeSelect-search {
padding: 20rpx 30rpx 20rpx;
.alreadySelect {
width: 100%;
padding: 30rpx 0rpx 0;
border-bottom: 1rpx solid #c0c4cc;
.alreadySelect__box {
.alreadySelect_hd {
width: 100%;
height: 60rpx;
justify-content: space-between;
}
.select__box {
width: 100%;
justify-content: center;
.select__list {
justify-content: flex-start;
flex-wrap: wrap;
.u-selectTag {
width: 310rpx;
border: 1px solid #2194fa;
background-color: #e8f4fe;
line-height: 40rpx;
margin: 10rpx;
padding-left: 10rpx;
align-items: center;
border-radius: 8rpx;
.content {
width: 74%;
margin-left: 10rpx;
.nameSty {
color: #353535;
flex: 1;
display: flex;
.nameUp {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex: 1;
}
.close {
width: 26px;
padding-right: 8rpx;
justify-content: flex-end;
color: #2194fa;
flex-shrink: 0;
}
}
.organizeSty {
color: #a0a1a1;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.u-size-default {
padding: 6rpx 12rpx;
}
}
}
}
}
.listTitle {
// height: 100rpx;
padding: 22rpx 0;
font-size: 36rpx;
}
}
.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: 24px;
background-color: #fff;
.content {
width: 85%;
margin-left: 15rpx;
.nameSty {}
.organizeSty {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,400 @@
<template>
<view class="jnpf-file">
<view class="jnpf-file-box" :style="{textAlign:align}">
<view v-if="!detailed &&!disabled " class="jnpf-file-box-line">
<!-- #ifndef APP-HARMONY -->
<CommentLsjUpload :ref="lsjUpload" :childId="childId" :width="width" :height="height" :option="option"
:size="fileSize" :formats="getFormats" :instantly="instantly" @uploadEnd="onuploadEnd"
:lsjUpload="lsjUpload" v-if="!disabled" :currentCount="currentCount">
<view class="icon-ym icon-ym-comment-file" size="mini"></view>
</CommentLsjUpload>
<view class="icon-ym icon-ym-comment-file" size="mini" v-else @click="onCountOver"></view>
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<view @click="chooseFile">
<view class="icon-ym icon-ym-comment-file" size="mini"></view>
</view>
<!-- #endif -->
</view>
<view class="icon-ym icon-ym-comment-file" size="mini" v-if="disabled" @click="onCountOver"></view>
<view class="tipText u-p-l-20">
{{tipText}}
</view>
</view>
</view>
</template>
<script>
import CommentLsjUpload from './lsj-upload/lsj-upload.vue'
import {
getDownloadUrl
} from '@/api/common'
import jnpf from '@/utils/jnpf'
const units = {
KB: 1024,
MB: 1024 * 1024,
GB: 1024 * 1024 * 1024,
};
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
export default {
components: {
CommentLsjUpload
},
name: 'jnpf-upload-img',
inheritAttrs: false,
props: {
modelValue: {
type: Array,
default: () => ([])
},
limit: {
type: [Number, String],
default: 9
},
fileSize: {
type: Number,
default: 10
},
sizeUnit: {
type: String,
default: 'MB'
},
accept: {
type: String,
default: ''
},
pathType: {
type: String,
default: 'defaultPath'
},
tipText: {
type: String,
default: ''
},
isAccount: {
type: Number,
default: 0
},
folder: {
type: String,
default: ''
},
vModel: {
type: String,
default: ''
},
detailed: {
type: Boolean,
default: false
},
align: {
type: String,
default: 'right'
},
currentCount: {
type: Number,
default: 0
}
},
data() {
return {
percent: '',
fileList: [],
// 上传接口参数
option: {},
params: {
pathType: this.pathType,
isAccount: this.isAccount,
folder: this.folder
},
// 选择文件后是否立即自动上传true=选择后立即上传
instantly: true,
size: 30,
list: [],
deletable: false,
childId: 'upload' + this.$u.guid(3, false, 2),
lsjUpload: 'lsjUpload' + this.$u.guid(3, false, 2),
width: '48rpx',
height: '48rpx',
disabled: false
}
},
computed: {
baseURL() {
return this.define.baseURL
},
comUploadUrl() {
return this.define.comUploadUrl
},
getFormats() {
let formats = this.accept
formats = formats.replace("image/*", 'png,jpg,jpeg,bmp,gif,webp,psd,svg,tiff')
formats = formats.replace("video/*", 'avi,wmv,mpg,mpeg,mov,rm,ram,swf,flv,mp4,wma,rm,rmvb,flv,mpg,mkv')
formats = formats.replace("audio/*", 'mp3,wav,aif,midi,m4a')
return formats
},
},
created() {
const token = uni.getStorageSync('token')
this.option = {
url: this.baseURL + '/api/file/Uploader/annex',
name: 'file',
header: {
'Authorization': token,
'uid': '27682',
'client': 'app',
'accountid': 'DP',
},
data: this.params
}
},
watch: {
modelValue: {
handler(val) {
this.fileList = JSON.parse(JSON.stringify(val));
},
immediate: true
}
},
methods: {
// 鸿蒙上传附件
chooseFile() {
if (this.limit === this.fileList.length) return this.toast(`只允许上传${this.limit}个文件`);
uni.chooseFile({
count: 1, //默认100
success: (res) => {
const tempFilePaths = res.tempFilePaths;
const token = uni.getStorageSync('token')
const file = res.tempFiles[0]
// 限制文件大小
if (file.size > units[this.sizeUnit] * Math.abs(this.fileSize)) {
this.toast(`文件大小超过${this.fileSize}${this.sizeUnit}`)
return;
}
uni.uploadFile({
url: this.baseURL + '/api/file/Uploader/annex',
filePath: tempFilePaths[0],
name: 'file',
formData: {
...this.params,
fileName: file.name
},
header: {
'Authorization': token,
'uid': '27682',
'client': 'app',
'accountid': 'DP',
},
success: (response) => {
let item = JSON.parse(response.data)
this.fileList.push({
name: res.tempFiles[0].name,
fileId: item.data.name,
url: item.data.url,
fileExtension: item.data.fileExtension,
fileSize: item.data.fileSize
})
this.$emit('update:modelValue', this.fileList)
this.$emit('change', this.fileList)
this.$forceUpdate();
}
});
}
});
},
onCountOver() {
uni.showToast({
title: `最多可以上传${this.limit}个文件`,
icon: 'none'
});
return;
},
// 某文件上传结束回调(成功失败都回调)
onuploadEnd(item) {
if (this.currentCount >= this.limit) {
// this.disabled = true;
return this.$u.toast('最多可以上传' + this.limit + '个文件')
}
if (item['responseText']) {
let response = JSON.parse(item.responseText)
let count = this.fileList.length
if (count >= this.limit) {
// this.disabled = true;
return this.$u.toast('最多可以上传' + this.limit + '个文件')
}
if (response.code != 200) return this.$u.toast(response.msg)
this.fileList.push({
name: item.name,
fileId: response.data.name,
url: response.data.url,
fileExtension: response.data.fileExtension,
fileSize: response.data.fileSize
})
this.$emit('update:modelValue', this.fileList)
this.$emit('change', this.fileList)
}
this.$forceUpdate();
},
downLoad(item) {
if (item.fileExtension && imgTypeList.includes(item.fileExtension)) return this.previewImage(item)
// #ifdef MP
this.previewFile(item)
// #endif
// #ifndef MP
getDownloadUrl('annex', item.fileId).then(res => {
const fileUrl = this.baseURL + res.data.url + '&name=' + item.name;
// #ifdef H5
window.location.href = fileUrl;
// #endif
// #ifdef APP-PLUS
this.downloadFile(res.data.url);
// #endif
})
// #endif
},
// 移除某个文件
delFile(files) {
this.fileList = files
// if(this.fileList.length >= this.limit) {
// this.disabled = true
// }else{
// this.disabled = false
// }
},
previewFile(item) {
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx']
let url = item.url
let fileType = url.split('.')[1]
if (fileTypes.includes(fileType)) {
uni.downloadFile({
url: this.baseURL + url,
success: (res) => {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: encodeURI(filePath),
showMenu: true,
fileType: fileType,
success: (res) => {
console.log('打开文档成功');
},
fail(err) {
console.log('小程序', err);
}
});
}
});
} else {
this.$u.toast(
'该文件类型无法打开'
)
}
},
previewImage(item) {
if (!item.url) return
const url = jnpf.getAuthImgUrl(item.url)
uni.previewImage({
urls: [url],
current: url,
success: () => {},
fail: () => {
uni.showToast({
title: '预览图片失败',
icon: 'none'
});
}
});
},
downloadFile(url) {
uni.downloadFile({
url: this.baseURL + url,
success: res => {
if (res.statusCode === 200) {
uni.saveFile({
tempFilePath: res.tempFilePath,
success: red => {
uni.showToast({
icon: 'none',
mask: true,
title: '文件已保存:' + red.savedFilePath, //保存路径
duration: 3000,
});
setTimeout(() => {
uni.openDocument({
filePath: red.savedFilePath,
success: ress => {},
fail(err) {}
});
}, 500)
}
});
}
}
});
},
}
}
</script>
<style lang="scss" scoped>
.icon-ym {
font-size: 48rpx;
}
.jnpf-file {
width: 100%;
.jnpf-file-box {
.jnpf-file-box-line {
height: 70rpx;
align-items: center;
display: flex;
}
.tipText {
color: #606266;
word-break: break-all;
line-height: 48rpx;
}
.jnpf-file-item {
justify-content: space-between;
flex-direction: row;
.jnpf-file-item-txt {
width: 230rpx;
flex: auto;
}
.showLeft {
text-align: left;
}
.closeBox {
height: 60rpx;
align-items: flex-end;
justify-content: space-evenly;
flex: 0.2;
.closeTxt {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
background-color: #fa3534;
color: #FFFFFF;
font-size: 20rpx;
align-items: center;
justify-content: center;
line-height: 36rpx;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,432 @@
export class LsjFile {
constructor(data) {
this.dom = null;
// files.type = waiting等待上传|| loading上传中|| success成功 || fail失败
this.files = new Map();
this.debug = data.debug || false;
this.id = data.id;
this.width = data.width;
this.height = data.height;
this.option = data.option;
this.instantly = data.instantly;
this.prohibited = data.prohibited;
this.onchange = data.onchange;
this.onprogress = data.onprogress;
this.uploadHandle = this._uploadHandle;
// #ifdef MP-WEIXIN
this.uploadHandle = this._uploadHandleWX;
// #endif
}
/**
* 创建File节点
* @param {string}path webview地址
*/
create(path) {
if (!this.dom) {
// #ifdef H5
let dom = document.createElement('input');
dom.type = 'file'
dom.value = ''
dom.style.height = this.height
dom.style.width = this.width
dom.style.position = 'absolute'
dom.style.top = 0
dom.style.left = 0
dom.style.right = 0
dom.style.bottom = 0
dom.style.opacity = 0
dom.style.zIndex = 900
dom.accept = this.prohibited.accept;
if (this.prohibited.multiple) {
dom.multiple = 'multiple';
}
dom.onchange = event => {
for (let file of event.target.files) {
if (this.files.size >= this.prohibited.count) {
this.toast(`最多可以上传${this.prohibited.count}个文件`);
this.dom.value = '';
break;
}
this.addFile(file);
}
this._uploadAfter();
this.dom.value = '';
};
this.dom = dom;
// #endif
// #ifdef APP-PLUS
let styles = {
top: '-200px',
left: 0,
width: '1px',
height: '200px',
background: 'transparent'
};
let extras = {
debug: this.debug,
instantly: this.instantly,
prohibited: this.prohibited,
}
this.dom = plus.webview.create(path, this.id, styles, extras);
this.setData(this.option);
this._overrideUrlLoading();
// #endif
return this.dom;
}
}
/**
* 设置上传参数
* @param {object|string}name 上传参数,支持a.b 和 a[b]
*/
setData() {
let [name, value = ''] = arguments;
if (typeof name === 'object') {
Object.assign(this.option, name);
} else {
this._setValue(this.option, name, value);
}
this.debug && console.log(JSON.stringify(this.option));
// #ifdef APP-PLUS
this.dom.evalJS(`vm.setData('${JSON.stringify(this.option)}')`);
// #endif
}
/**
* 上传
* @param {string}name 文件名称
*/
async upload(name = '') {
if (!this.option.url) {
throw Error('未设置上传地址');
}
// #ifndef APP-PLUS
if (name && this.files.has(name)) {
await this.uploadHandle(this.files.get(name));
} else {
for (let item of this.files.values()) {
if (item.type === 'waiting' || item.type === 'fail') {
await this.uploadHandle(item);
}
}
}
// #endif
// #ifdef APP-PLUS
this.dom && this.dom.evalJS(`vm.upload('${name}')`);
// #endif
}
// 选择文件change
addFile(file, isCallChange) {
let name = file.name;
this.debug && console.log('文件名称', name, '大小', file.size);
if (file) {
// 限制文件格式
let path = '';
let suffix = name.substring(name.lastIndexOf(".") + 1).toLowerCase();
let formats = this.prohibited.formats.toLowerCase();
// #ifndef MP-WEIXIN
path = URL.createObjectURL(file);
// #endif
// #ifdef MP-WEIXIN
path = file.path;
// #endif
if (formats && !formats.includes(suffix)) {
this.toast(`不支持上传${suffix.toUpperCase()}格式文件`);
return false;
}
// 限制文件大小
if (file.size > 1024 * 1024 * Math.abs(this.prohibited.size)) {
this.toast(`文件大小超过${this.prohibited.size}MB`)
return false;
}
this.files.set(file.name, {
file,
path,
name: file.name,
size: file.size,
progress: 0,
type: 'waiting'
});
return true;
}
}
/**
* 移除文件
* @param {string}name 不传name默认移除所有文件传入name移除指定name的文件
*/
clear(name = '') {
// #ifdef APP-PLUS
this.dom && this.dom.evalJS(`vm.clear('${name}')`);
// #endif
if (!name) {
this.files.clear();
} else {
this.files.delete(name);
}
return this.onchange(this.files);
}
/**
* 提示框
* @param {string}msg 轻提示内容
*/
toast(msg) {
uni.showToast({
title: msg,
icon: 'none'
});
}
/**
* 微信小程序选择文件
* @param {number}count 可选择文件数量
*/
chooseMessageFile(type, count) {
wx.chooseMessageFile({
count: count,
type: type,
success: ({
tempFiles
}) => {
for (let file of tempFiles) {
this.addFile(file);
}
this._uploadAfter();
},
fail: () => {
this.toast(`打开失败`);
}
})
}
_copyObject(obj) {
if (typeof obj !== "undefined") {
return JSON.parse(JSON.stringify(obj));
} else {
return obj;
}
}
/**
* 自动根据字符串路径设置对象中的值 支持.和[]
* @param {Object} dataObj 数据源
* @param {String} name 支持a.b 和 a[b]
* @param {String} value 值
* setValue(dataObj, name, value);
*/
_setValue(dataObj, name, value) {
// 通过正则表达式 查找路径数据
let dataValue;
if (typeof value === "object") {
dataValue = this._copyObject(value);
} else {
dataValue = value;
}
let regExp = new RegExp("([\\w$]+)|\\[(:\\d)\\]", "g");
const patten = name.match(regExp);
// 遍历路径 逐级查找 最后一级用于直接赋值
for (let i = 0; i < patten.length - 1; i++) {
let keyName = patten[i];
if (typeof dataObj[keyName] !== "object") dataObj[keyName] = {};
dataObj = dataObj[keyName];
}
// 最后一级
dataObj[patten[patten.length - 1]] = dataValue;
this.debug && console.log('参数更新后', JSON.stringify(this.option));
}
_uploadAfter() {
this.onchange(this.files);
setTimeout(() => {
this.instantly && this.upload();
}, 1000)
}
_overrideUrlLoading() {
this.dom.overrideUrlLoading({
mode: 'reject'
}, e => {
let {
retype,
item,
files,
end
} = this._getRequest(
e.url
);
let _this = this;
switch (retype) {
case 'updateOption':
this.dom.evalJS(`vm.setData('${JSON.stringify(_this.option)}')`);
break
case 'change':
try {
_this.files = new Map([..._this.files, ...JSON.parse(unescape(files))]);
} catch (e) {
return console.error('出错了,请检查代码')
}
_this.onchange(_this.files);
break
case 'progress':
try {
item = JSON.parse(unescape(item));
} catch (e) {
return console.error('出错了,请检查代码')
}
_this._changeFilesItem(item, end);
break
default:
break
}
})
}
_getRequest(url) {
let theRequest = new Object()
let index = url.indexOf('?')
if (index != -1) {
let str = url.substring(index + 1)
let strs = str.split('&')
for (let i = 0; i < strs.length; i++) {
theRequest[strs[i].split('=')[0]] = unescape(strs[i].split('=')[1])
}
}
return theRequest
}
_changeFilesItem(item, end = false) {
this.debug && console.log('onprogress', JSON.stringify(item));
this.onprogress(item, end);
this.files.set(item.name, item);
}
_uploadHandle(item) {
item.type = 'loading';
delete item.responseText;
return new Promise((resolve, reject) => {
this.debug && console.log('option', JSON.stringify(this.option));
let {
url,
name,
method = 'POST',
header,
formData,
data
} = this.option;
let form = new FormData();
for (let keys in formData) {
form.append(keys, formData[keys])
}
for (let keys in data) {
form.append(keys, data[keys])
}
form.append(name, item.file);
let xmlRequest = new XMLHttpRequest();
xmlRequest.open(method, url, true);
for (let keys in header) {
xmlRequest.setRequestHeader(keys, header[keys])
}
xmlRequest.upload.addEventListener(
'progress',
event => {
if (event.lengthComputable) {
let progress = Math.ceil((event.loaded * 100) / event.total)
if (progress <= 100) {
item.progress = progress;
this._changeFilesItem(item);
}
}
},
false
);
xmlRequest.ontimeout = () => {
console.error('请求超时')
item.type = 'fail';
this._changeFilesItem(item, true);
return resolve(false);
}
xmlRequest.onreadystatechange = ev => {
if (xmlRequest.readyState == 4) {
if (xmlRequest.status == 200) {
this.debug && console.log('上传完成:' + xmlRequest.responseText)
item['responseText'] = xmlRequest.responseText;
item.type = 'success';
this._changeFilesItem(item, true);
return resolve(true);
} else if (xmlRequest.status == 0) {
console.error(
'status = 0 :请检查请求头Content-Type与服务端是否匹配服务端已正确开启跨域并且nginx未拦截阻止请求')
}
console.error('--ERROR--status = ' + xmlRequest.status)
item.type = 'fail';
this._changeFilesItem(item, true);
return resolve(false);
}
}
xmlRequest.send(form)
});
}
_uploadHandleWX(item) {
item.type = 'loading';
delete item.responseText;
return new Promise((resolve, reject) => {
this.debug && console.log('option', JSON.stringify(this.option));
let form = {
filePath: item.file.path,
...this.option,
formData: this.option.data || {},
};
form['fail'] = ({
errMsg = ''
}) => {
console.error('--ERROR--' + errMsg)
item.type = 'fail';
this._changeFilesItem(item, true);
return resolve(false);
}
form['success'] = res => {
if (res.statusCode == 200) {
this.debug && console.log('上传完成,微信端返回不一定是字符串根据接口返回格式判断是否需要JSON.parse' + res.data)
item['responseText'] = res.data;
item.type = 'success';
this._changeFilesItem(item, true);
return resolve(true);
}
item.type = 'fail';
this._changeFilesItem(item, true);
return resolve(false);
}
let xmlRequest = uni.uploadFile(form);
xmlRequest.onProgressUpdate(({
progress = 0
}) => {
if (progress <= 100) {
item.progress = progress;
this._changeFilesItem(item);
}
})
});
}
}

View File

@@ -0,0 +1,399 @@
<template>
<view class="lsj-file" :style="[getStyles]">
<view ref="lsj" class="hFile" :style="[getStyles]" @click="onClick">
<slot>
<view class="defview" :style="[getStyles]">附件上传</view>
</slot>
</view>
</view>
</template>
<script>
// 查看文档https://ext.dcloud.net.cn/plugin?id=5459
import {
LsjFile
} from './LsjFile.js'
export default {
name: 'comment-Lsj-upload',
props: {
currentCount: {
type: Number,
default: 0
},
// 打印日志
debug: {
type: Boolean,
default: false
},
// 自动上传
instantly: {
type: Boolean,
default: false
},
// 上传接口参数设置
option: {
type: Object,
default: () => {}
},
// 文件大小上限
size: {
type: Number,
default: 10
},
// 文件选择个数上限,超出后不触发点击
count: {
type: Number,
default: 2000
},
// 是否允许多选文件
multiple: {
type: Boolean,
default: true
},
// 允许上传的文件格式(多个以逗号隔开)
formats: {
type: String,
default: ''
},
// input file选择限制
accept: {
type: String,
default: ''
},
// 微信选择文件类型
//all=从所有文件选择,
//video=只能选择视频文件,
//image=只能选择图片文件,
//file=可以选择除了图片和视频之外的其它的文件
wxFileType: {
type: String,
default: 'all'
},
// webviewID需唯一不同窗口也不要同Id
childId: {
type: String,
default: 'lsjUpload'
},
// 文件选择触发面宽度
width: {
type: String,
default: '100%'
},
// 文件选择触发面高度
height: {
type: String,
default: '80rpx'
},
// top,left,bottom,right仅position=absolute时才需要传入
top: {
type: [String, Number],
default: ''
},
left: {
type: [String, Number],
default: ''
},
bottom: {
type: [String, Number],
default: ''
},
right: {
type: [String, Number],
default: ''
},
// nvue不支持跟随窗口滚动
position: {
type: String,
// #ifdef APP-NVUE
default: 'absolute',
// #endif
// #ifndef APP-NVUE
default: 'static',
// #endif
},
},
data() {
return {
}
},
watch: {
option(v) {
// #ifdef APP-PLUS
this.lsjFile && this.show();
// #endif
}
},
updated() {
// #ifdef APP-PLUS
if (this.isShow) {
this.lsjFile && this.show();
}
// #endif
},
computed: {
getStyles() {
let styles = {
width: this.width,
height: this.height
}
if (this.position == 'absolute') {
styles['top'] = this.top
styles['bottom'] = this.bottom
styles['left'] = this.left
styles['right'] = this.right
styles['position'] = 'fixed'
}
return styles
}
},
mounted() {
this._size = 0;
let WEBID = this.childId + new Date().getTime();
this.lsjFile = new LsjFile({
id: WEBID,
debug: this.debug,
width: this.width,
height: this.height,
option: this.option,
instantly: this.instantly,
// 限制条件
prohibited: {
// 大小
size: this.size,
// 允许上传的格式
formats: this.formats,
// 限制选择的格式
accept: this.accept,
count: this.count,
// 是否多选
multiple: this.multiple,
},
onchange: this.onchange,
onprogress: this.onprogress,
});
this.create();
// 需判断是否当前页显示
uni.$on('lsjShow', this.show);
},
beforeDestroy() {
uni.$off('lsjShow', this.show);
// #ifdef APP-PLUS
this.lsjFile.dom.close();
// #endif
},
methods: {
setFiles(array) {
if (array instanceof Map) {
for (let [key, item] of array) {
item['progress'] = 100;
item['type'] = 'success';
this.lsjFile.files.set(key, item);
}
} else if (Array.isArray(array)) {
array.forEach(item => {
if (item.name) {
item['progress'] = 100;
item['type'] = 'success';
this.lsjFile.files.set(item.name, item);
}
});
}
this.onchange(this.lsjFile.files);
},
setData() {
this.lsjFile && this.lsjFile.setData(...arguments);
},
getDomStyles(callback) {
// #ifndef APP-NVUE
let view = uni
.createSelectorQuery()
.in(this)
.select('.lsj-file')
view.fields({
size: true,
rect: true
},
({
height,
width,
top,
left,
right,
bottom
}) => {
uni.createSelectorQuery()
.selectViewport()
.scrollOffset(({
scrollTop
}) => {
return callback({
top: parseInt(top) + parseInt(scrollTop) + 'px',
left: parseInt(left) + 'px',
width: parseInt(width) + 'px',
height: parseInt(height) + 'px'
})
})
.exec()
}
).exec()
// #endif
// #ifdef APP-NVUE
const dom = weex.requireModule('dom')
dom.getComponentRect(this.$refs.lsj, ({
size: {
height,
width,
top,
left,
right,
bottom
}
}) => {
return callback({
top: parseInt(top) + 'px',
left: parseInt(left) + 'px',
width: parseInt(width) + 'px',
height: parseInt(height) + 'px',
right: parseInt(right) + 'px',
bottom: parseInt(bottom) + 'px'
})
})
// #endif
},
show() {
if (this._size && (this._size >= this.count)) {
return;
}
this.isShow = true;
// #ifdef APP-PLUS
this.lsjFile && this.getDomStyles(styles => {
this.lsjFile.dom.setStyle(styles)
});
// #endif
// #ifdef H5
this.lsjFile.dom.style.display = 'inline'
// #endif
},
hide() {
this.isShow = false;
// #ifdef APP-PLUS
this.lsjFile && this.lsjFile.dom.setStyle({
top: '-100px',
left: '0px',
width: '1px',
height: '100px',
});
// #endif
// #ifdef H5
this.lsjFile.dom.style.display = 'none'
// #endif
},
/**
* 手动提交上传
* @param {string}name 文件名称不传则上传所有type等于waiting和fail的文件
*/
upload(name) {
this.lsjFile && this.lsjFile.upload(name);
},
/**
* @returns {Map} 已选择的文件Map集
*/
onchange(files) {
// this.$emit('change',files);
this._size = files.size;
return files.size >= this.count ? this.hide() : this.show();
},
/**
* @returns {object} 当前上传中的对象
*/
onprogress(item, end = false) {
this.$emit('progress', item);
if (end) {
setTimeout(() => {
this.$emit('uploadEnd', item);
}, 0);
}
},
/**
* 移除组件内缓存的某条数据
* @param {string}name 文件名称,不指定默认清除所有文件
*/
clear(name) {
this.lsjFile.clear(name);
},
// 创建选择器
create() {
// 若iOS端服务端处理不了跨域就将hybrid目录内的html放到服务端去并将此处path改成服务器上的地址
let path = '/uni_modules/lsj-upload/hybrid/html/uploadFile.html?sourceflag=comment';
let dom = this.lsjFile.create(path);
// #ifdef H5
this.$refs.lsj.$el.appendChild(dom);
// #endif
// #ifndef APP-PLUS
this.show();
// #endif
// #ifdef APP-PLUS
dom.setStyle({
position: this.position
});
dom.loadURL(path);
setTimeout(() => {
// #ifdef APP-NVUE
plus.webview.currentWebview().append(dom);
// #endif
// #ifndef APP-NVUE
this.$root.$scope.$getAppWebview().append(dom);
// #endif
this.show();
}, 300)
// #endif
},
// 点击选择附件
onClick() {
/*if (this.currentCount >= 9) {
this.toast(`最多可以上传9个文件`);
return;
}*/
// #ifdef MP-WEIXIN
if (!this.isShow) {
return;
}
let count = this.count - this._size;
this.lsjFile.chooseMessageFile(this.wxFileType, count);
// #endif
},
toast(msg) {
uni.showToast({
title: msg,
icon: 'none'
});
}
}
}
</script>
<style scoped>
.lsj-file {
display: inline-block;
}
.defview {
background-color: #007aff;
color: #fff;
border-radius: 10rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 28rpx;
}
.hFile {
position: relative;
overflow: hidden;
}
</style>

View File

@@ -0,0 +1,340 @@
<template>
<u-popup class="jnpf-tree-select-popup" :maskCloseAble="maskCloseAble" mode="right" v-model="showPopup"
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" width="100%">
<view class="jnpf-tree-select-body">
<view class="jnpf-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="jnpf-tree-select-search">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" @change="search()" bg-color="#f0f2f6" shape="square">
</u-search>
</view>
<view class="jnpf-tree-selected">
<view class="jnpf-tree-selected-head">
<view>{{$t('component.jnpf.common.selected')}}</view>
<view v-if="multiple" class="clear-btn" @click="cleanAll">
{{$t('component.jnpf.common.clearAll')}}
</view>
</view>
<view class="jnpf-tree-selected-box">
<scroll-view scroll-y="true" style="max-height: 240rpx;">
<view class="jnpf-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="jnpf-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="selectType !== 'all'">全部数据</view>
<view class="jnpf-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>
<JnpfEmpty v-if="list.length<1"></JnpfEmpty>
</view>
</scroll-view>
</view>
<!-- 底部按钮 -->
<view class="jnpf-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 {
getTaskUserList
} from '@/api/workFlow/flowBefore'
export default {
name: "comment-tree-select",
props: {
taskId: {
type: String,
default: ''
},
selectType: {
type: String,
default: 'all'
},
clearable: {
type: Boolean,
default: false
},
query: {
type: Object,
default: () => ({})
},
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: '确认'
}
},
data() {
return {
triggered: false,
selectList: [],
keyword: '',
current: 0,
list: [],
pagination: {
currentPage: 1,
pageSize: 20
},
total: 0,
height: 0,
showPopup: false
};
},
watch: {
// 在select弹起的时候重新初始化所有数据
modelValue: {
immediate: true,
handler(val) {
this.showPopup = val
if (val) setTimeout(() => this.getInfoList(), 10);
}
},
},
computed: {
baseURL() {
return this.define.baseURL
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
},
realProps() {
return {
...defaultProps,
...this.props
}
}
},
created() {
setTimeout(() => {
this.triggered = true;
}, 1000)
},
methods: {
handleScrollToLower() {
this.getInfoList()
},
getInfoList() {
this.pagination.keyword = this.keyword
getTaskUserList(this.taskId, this.pagination).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++
})
},
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)
},
search() {
this.searchTimer && clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.pagination = {
currentPage: 1,
pageSize: 20
}
this.pagination.keyword = this.keyword
getTaskUserList(this.taskId, this.pagination).then(res => {
const list = res.data.list;
this.list = list
this.pagination = res.data.pagination
this.total = this.pagination.total
})
}, 300)
},
delSelect(index) {
this.selectList.splice(index, 1);
},
cleanAll() {
this.selectList = [];
},
handleConfirm() {
this.keyword = '';
this.$emit('confirm', this.selectList);
this.close();
},
close() {
this.$emit('close');
}
}
};
</script>
<style scoped lang="scss">
.jnpf-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;
}
.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
}
}
}
}
</style>

View File

@@ -0,0 +1,680 @@
<template>
<view class="comment-v">
<view class="comment_inner">
<view class="text_box">
<div class="u-input-wrapper">
<u-input :type="textarea.type" v-model="dataForm.text" placeholder="请输入" :height='textarea.height'
ref="textRef" :focus="textFocus" :border='textarea.border' :maxlength="textarea.maxlength"
:auto-height="textarea.autoHeight" class="text_input" @input="handleContentChange" />
<div class="remainingCharacters">{{ dataForm.text.length }}/{{textarea.maxlength}}</div>
</div>
</view>
<view class="box" :style="{ bottom: popupOpenBottom + 'rpx'}">
<scroll-view :scroll-y="true" style="height: 550rpx;" class="scroll_view" @click="hideDrawer">
<view class="comment-area">
<view class="img_box">
<view class="u-preview-wrap" v-for="(item, index) in dataForm.imgList" :key="index">
<view class="u-delete-icon" @tap.stop="deleteItem(index)">
<u-icon class="u-icon" name="close" size="20" color="#ffffff"></u-icon>
</view>
<image class="u-preview-image" :src="jnpf.getAuthImgUrl(item.thumbUrl||item.url)"
mode="aspectFill" @tap.stop="doPreviewImage(index)"></image>
</view>
</view>
<view v-for='(item,index) in dataForm.file' :key="index"
class="jnpf-file-item u-type-primary u-flex u-line-1" @tap='downLoad(item)'>
<view class="jnpf-file-item-txt u-line-1">{{item.name}}</view>
<view class="closeBox u-flex-col" @click.stop="delFile(index)">
<text class="icon-ym icon-ym-nav-close closeTxt u-flex"></text>
</view>
</view>
</view>
</scroll-view>
<view class="btn_box">
<view class="u-flex">
<view class="btn_item">
<view class="icon-ym icon-ym-roll-call" size="mini" @click="openSelectUser()">
</view>
</view>
<view class="btn_item">
<view class="icon-ym icon-ym-comment-img"
v-if="dataForm.imgList && dataForm.imgList.length >= 9"
@click="clickImgUploadOverCount">
</view>
<u-upload :custom-btn="true" :action="comUploadUrl+type" :header="uploadHeaders"
ref="uUpload" :max-size="10*1024*1024" :max-count="9" :show-upload-list="false"
:show-progress="false" @on-success="onImgSuccess" @on-change="onImgChange"
:show-tips="false" @on-oversize="onImgOversize">
<template #addBtn>
<view class="slot-btn" hover-class="slot-btn__hover" hover-stay-time="150">
<view class="icon-ym icon-ym-comment-img"></view>
</view>
</template>
</u-upload>
</view>
<view class="btn_item">
<CommentFile v-model="dataForm.file" :limit="9" :fileSize="10"
:currentCount="dataForm.file?dataForm.file.length:0" ref="commentFile" />
</view>
<view class="btn_item">
<view class="icon-ym icon-ym-emoji" size="mini" @click="chooseEmoji"></view>
</view>
</view>
<view class="btn_item">
<u-button type="primary" @click="handleClick" :disabled="submitDisabled"
size="medium">发送</u-button>
</view>
</view>
</view>
</view>
<view class="popup-layer u-border-top" :class="popupLayerClass" @touchmove.stop.prevent="discard">
<swiper class="emoji-swiper" indicator-dots="true" duration="150" v-show="showEmoji">
<swiper-item v-for="(page,pid) in emojiTree" :key="pid">
<view v-for="(em,eid) in page" :key="eid" @click="addEmoji(em)" class="emoji-item">
<image mode="widthFix" :src="getEmojiUrl(em.url)" class="emoji-item-img"></image>
</view>
</swiper-item>
</swiper>
</view>
<FlowUserModal v-model="flowUserModalShow" :taskId="taskId" :selectType="'custom'" :multiple="true"
ref="flowUserModal" @confirm="handleSelectUser" @close="closeFlowUserModal" />
</view>
</template>
<script>
import {
getDownloadUrl
} from '@/api/common'
import CommentFile from './comment-file/index.vue'
import FlowUserModal from './comment-user-select/index.vue'
import {
emojiList,
emojiTree,
imagesMap
} from '../flowBefore/emoji'
import jnpf from '@/utils/jnpf'
export default {
components: {
CommentFile,
FlowUserModal
},
data() {
return {
dataForm: {
text: '',
file: [],
imgList: [],
},
type: 'annexpic',
uploadHeaders: {
Authorization: this.token
},
token: '',
list: [],
textarea: {
type: 'textarea',
border: true,
height: 440,
autoHeight: false,
maxlength: 500
},
taskId: null,
replyId: null,
submitDisabled: true,
selectUserType: '',
selectionStart: 0,
flowUserModalShow: false,
textFocus: true,
popupLayerClass: '',
popupOpenBottom: 20,
showEmoji: false,
emojiList,
emojiTree,
};
},
computed: {
baseURL() {
return this.define.baseURL
},
comUploadUrl() {
return this.define.comUploadUrl
},
},
onReady() {
},
onLoad(e) {
this.uploadHeaders.Authorization = uni.getStorageSync('token')
let data = JSON.parse(decodeURIComponent(e.data))
this.taskId = data.taskId
if (data.replyId) {
this.replyId = data.replyId
uni.setNavigationBarTitle({
title: '回复评论'
});
}
},
watch: {
dataForm: {
handler: function(val) {
this.setSubmitDisabled()
},
deep: true,
immediate: true
},
},
methods: {
clickImgUploadOverCount() {
uni.showToast({
title: '最多可以上传9张图片',
icon: 'none'
});
return false;
},
discard() {
return;
},
chooseEmoji() {
if (this.showEmoji) return this.hideDrawer()
this.showEmoji = true;
this.openDrawer();
},
addEmoji(em) {
this.dataForm.text += em.alt;
},
getEmojiUrl(url) {
return imagesMap[url.replace('.', '')]
},
openDrawer() {
this.popupLayerClass = 'showLayer';
setTimeout(() => {
this.popupOpenBottom = 315;
}, 150);
},
hideDrawer() {
this.popupLayerClass = '';
setTimeout(() => {
this.showEmoji = false;
this.popupOpenBottom = 20;
}, 50);
},
getFocus() {
this.textFocus = false
setTimeout(() => {
this.textFocus = true
}, 50);
},
setSubmitDisabled() {
this.submitDisabled = !this.dataForm.text
},
openSelectUser() {
this.selectUserType = 'btn';
this.selectionStart = -1;
this.flowUserModalShow = true
},
closeFlowUserModal() {
this.flowUserModalShow = false
},
handleContentChange(value) {
if (!value || !value.endsWith("@")) {
return;
}
this.selectUserType = 'input';
this.selectionStart = value.length;
this.flowUserModalShow = true
},
handleSelectUser(data) {
if (!data.length || !data.length) return;
let addContent = this.selectUserType === 'btn' ? '@' : '';
for (let i = 0; i < data.length; i++) {
let str = (i > 0 ? '@' : '') + `{${data[i].fullName}}`;
addContent += str;
}
if (this.selectionStart === -1) {
this.dataForm.text += addContent;
this.textFocus = false
this.getFocus();
} else {
let oldValue = this.dataForm.text;
let rangeIndex = this.selectionStart + addContent.length;
this.dataForm.text = oldValue.slice(0, this.selectionStart) + addContent + oldValue.slice(this
.selectionStart);
this.textFocus = false
this.getFocus();
}
},
handleClick() {
const query = {
text: this.dataForm.text,
file: JSON.stringify(this.dataForm.file),
image: JSON.stringify(this.dataForm.imgList),
replyId: this.replyId
}
uni.$emit('comment', query);
uni.navigateBack();
},
//文件下载
downLoad(item) {
// #ifdef MP
this.previewFile(item)
// #endif
// #ifndef MP
getDownloadUrl('annex', item.fileId).then(res => {
const fileUrl = this.baseURL + res.data.url + '&name=' + item.name;
// #ifdef H5
window.location.href = fileUrl;
// #endif
// #ifdef APP-PLUS
this.downloadFile(res.data.url);
// #endif
})
// #endif
},
previewFile(item) {
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx']
let url = item.url
let fileType = url.split('.')[1]
if (fileTypes.includes(fileType)) {
uni.downloadFile({
url: this.baseURL + url,
success: (res) => {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: encodeURI(filePath),
showMenu: true,
fileType: fileType,
success: (res) => {},
fail(err) {}
});
}
});
} else {
this.$u.toast(
'该文件类型无法打开'
)
}
},
//文件删除
delFile(index) {
uni.showModal({
title: '提示',
content: '是否删除该文件?',
success: res => {
if (res.confirm) {
this.dataForm.file.splice(index, 1)
this.$refs.commentFile.delFile(this.dataForm.file);
} else if (res.cancel) {}
}
});
},
downloadFile(url) {
uni.downloadFile({
url: this.baseURL + url,
success: res => {
if (res.statusCode === 200) {
const filePath = res.tempFilePath;
uni.openDocument({
filePath: escape(filePath),
success: ress => {},
fail(err) {}
});
}
}
});
},
onImgSuccess(data, index, lists, name) {
if (data.code == 200) {
this.dataForm.imgList.push({
name: lists[index].file.name,
fileId: data.data.name,
url: data.data.url,
thumbUrl: data.data.thumbUrl,
})
// this.$emit('input', this.fileList)
} else {
lists.splice(index, 1)
this.$u.toast(data.msg)
}
},
onImgChange(res, index, lists, name) {
const isTopLimit = lists.length > 9;
if (isTopLimit) {
uni.showToast({
title: '最多可以上传9张图片',
icon: 'none'
});
return false
}
const isRightSize = lists[index].file.size < 10 * 1024 * 1024;
if (!isRightSize) {
uni.showToast({
title: '图片大小超过10MB',
icon: 'none'
});
return false;
}
/*
let isAccept = new RegExp('image/!*').test(file.type);
if (!isAccept) {
this.$message({ message: '请上传图片', type: 'error', duration: 1000 })
return
}
return isRightSize && isAccept;*/
},
onImgOversize(res, index, lists, name) {
uni.showToast({
title: '图片大小超过10MB',
icon: 'none'
});
return false;
},
doPreviewImage(index = 0) {
const images = this.dataForm.imgList.map(item => jnpf.getAuthImgUrl(item.url,false));
uni.previewImage({
urls: images,
current: images[index],
success: () => {},
fail: () => {
uni.showToast({
title: '预览图片失败',
icon: 'none'
});
}
});
},
deleteItem(index) {
uni.showModal({
title: '提示',
content: '是否删除该图片?',
success: res => {
if (res.confirm) {
this.$refs.uUpload.remove(index);
this.dataForm.imgList.splice(index, 1)
// this.$emit('input', this.fileList)
uni.showToast({
title: '移除成功',
icon: 'none'
});
}
}
});
}
}
}
</script>
<style lang="scss">
page {
height: 100%;
}
.comment-v {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
margin: 0 auto;
// .flowBefore-actions{
// width: 90%;
// left: 50%;
// transform: translateX(-50%);
// bottom: 20rpx;
// .buttom-btn{
// border-radius: 10rpx;
// }
// }
.uni-textarea-compute {
height: 470rpx !important;
}
.comment_inner {
display: flex;
flex-direction: column;
background-color: #FFFFFF;
height: 100%;
padding: 0 30rpx;
.text_box {
flex: 0.35;
.u-input-wrapper {
border: 1px solid #E7E7E7;
margin-top: 10px;
}
.remainingCharacters {
width: 99%;
text-align: right;
}
.text_input {
border: 0px;
}
// .input_textarea{
// height: 470rpx !important;
// }
}
.box {
width: 100%;
display: flex;
flex-direction: column;
position: absolute;
.scroll_view {
.comment-area {
height: 550rpx;
display: flex;
flex-direction: column;
align-items: flex-end;
justify-content: flex-end;
margin-bottom: 28rpx;
.img_box {
width: 100%;
display: flex;
flex-direction: row;
flex-wrap: wrap;
.u-preview-wrap {
width: 110rpx;
height: 110rpx;
overflow: hidden;
margin: 10rpx;
background: rgb(244, 245, 246);
position: relative;
border-radius: 10rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
.u-preview-image {
display: block;
width: 100%;
height: 100%;
border-radius: 10rpx;
}
.u-delete-icon {
position: absolute;
top: 10rpx;
right: 10rpx;
z-index: 10;
background-color: $u-type-error;
border-radius: 100rpx;
width: 34rpx;
height: 34rpx;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.u-icon {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
}
}
.jnpf-file-item {
width: 100%;
justify-content: space-between;
flex-direction: row;
.jnpf-file-item-txt {
flex: 1;
}
.closeBox {
height: 60rpx;
justify-content: space-evenly;
flex: 0.2;
.closeTxt {
width: 34rpx;
height: 34rpx;
border-radius: 50%;
background-color: #909194;
color: #FFFFFF;
font-size: 22rpx;
align-items: center;
justify-content: center;
}
}
}
}
}
.btn_box {
width: 100%;
display: flex;
flex-direction: row;
// justify-content: flex-start;
align-items: center;
.btn_item {
margin-right: 30rpx;
margin-left: 10rpx;
.icon-ym {
font-size: 48rpx;
}
}
.btn_item:last-child {
margin-left: auto;
margin-right: 50rpx;
}
.submit_item {
background-color: red;
}
.slot-btn {
.img_icon {
width: 80rpx;
height: 80rpx;
text-align: center;
line-height: 80rpx;
&:before {
content: "\e987";
font-size: 60rpx;
color: #666666;
}
}
}
.file_icon {
width: 80rpx;
height: 80rpx;
text-align: center;
line-height: 80rpx;
&:before {
font-size: 60rpx;
color: #666666;
}
}
}
}
}
}
.popup-layer {
&.showLayer {
transform: translate3d(0, -42vw, 0);
}
transition: all .15s linear;
width: 100%;
height: 42vw;
padding: 20rpx 2%;
background-color: #f2f2f2;
position: fixed;
z-index: 20;
top: 100%;
.emoji-swiper {
height: 40vw;
swiper-item {
display: flex;
align-content: flex-start;
flex-wrap: wrap;
.emoji-item {
width: 12vw;
height: 12vw;
display: flex;
justify-content: center;
align-items: center;
.emoji-item-img {
width: 8.4vw;
height: 8.4vw;
}
}
}
}
.more-layer {
width: 100%;
height: 42vw;
.list {
width: 100%;
display: flex;
flex-wrap: wrap;
.box {
width: 18vw;
height: 18vw;
border-radius: 20rpx;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
margin: 0 3vw 2vw 3vw;
.icon {
font-size: 70rpx;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,196 @@
<template>
<view class="candidateForm-v">
<u-popup mode="left" :popup="false" v-model="showPopup" length="auto" @close="close" width="100%">
<view class="jnpf-wrap jnpf-wrap-form">
<u-form :model="candidateForm" ref="candidateForm" :errorType="['toast']" label-position="left"
label-width="150" label-align="left">
<u-form-item label="分支选择" prop="branch" v-if="candidateType == 1" required>
<JnpfSelect v-model="candidateForm.branchList" @change="branchChange" placeholder="请选择审批分支"
:options="branchList" :multiple="true" :props='props' />
</u-form-item>
<u-form-item label-width="250" v-for="(item,index) in candidateForm.list" :key="index"
:label="item.label">
<u-input type="select" :select-open="item.selectShow" v-model="item.value"
@click="openSelect(item)" placeholder="请选择审批候选人">
</u-input>
</u-form-item>
</u-form>
<view class="buttom-actions">
<u-button class="buttom-btn" @click="cancel">取消</u-button>
<u-button class="buttom-btn" type="primary" @click="submit">确定</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
props: {
modelValue: {
type: Boolean,
default: false
},
formData: {
type: Object,
default: () => ({})
},
taskId: {
type: String,
default: ""
}
},
data() {
return {
showPopup: false,
candidateForm: {
list: [],
branchList: []
},
nodeId: '',
selectId: {},
rules: {},
selectList: [],
selectVal: {},
isCandidate: false,
props: {
label: 'nodeName',
value: 'nodeId'
},
candidateType: 1,
branchList: [],
candidateList: []
};
},
watch: {
modelValue: {
immediate: true,
handler(val) {
this.showPopup = val
}
},
},
created() {
uni.$on('confirm', (data, id) => {
this.selectConfirm(data, id)
})
},
onUnload() {
uni.$off('confirm')
},
methods: {
branchChange(e) {
this.candidateForm.branchList = e;
this.init()
},
init(candidateType, branchList, candidateList) {
this.candidateType = candidateType;
this.branchList = branchList;
this.candidateList = candidateList
if (this.candidateType == 1) {
let list = [];
for (let i = 0; i < this.candidateForm.branchList.length; i++) {
inner: for (let j = 0; j < this.branchList.length; j++) {
let o = this.branchList[j]
if (o.isCandidates) this.isCandidate = o.isCandidates
if (this.candidateForm.branchList[i] === o.nodeId && o.isCandidates) {
list.push({
...o,
label: o.nodeName + '审批人',
value: '',
selectShow: false
})
break inner
}
}
this.candidateForm.list = list
}
} else {
if (Array.isArray(this.candidateList) && this.candidateList.length) {
this.isCandidate = true
this.candidateForm.list = this.candidateList.map(o => ({
...o,
label: o.nodeName + '审批人',
value: '',
selectShow: false
}))
}
}
},
openSelect(item) {
this.selectList = []
for (let o in this.selectVal) {
if (o === item.nodeId) this.selectList = this.selectVal[o]
}
item.formData = this.formData
item.taskId = this.taskId
item.selectList = !item.value ? [] : this.selectList
uni.navigateTo({
url: '/pages/workFlow/candiDateUserSelect/index?data=' + encodeURIComponent(JSON.stringify(
item))
})
},
selectConfirm(e, id) {
let selectData = e;
let selectVal = [];
let val = [];
let selectId = [];
let nodeId = '';
if (!selectData.length) {
delete this.selectVal[id]
delete this.selectId[id]
for (let i = 0; i < this.candidateForm.list.length; i++) {
if (id === this.candidateForm.list[i].nodeId) {
this.candidateForm.list[i].value = ""
}
}
}
for (let i = 0; i < this.candidateForm.list.length; i++) {
for (let o = 0; o < selectData.length; o++) {
if (selectData[o].nodeId === this.candidateForm.list[i].nodeId) {
nodeId = selectData[o].nodeId
val.push(selectData[o].userName)
selectVal.push(selectData[o])
this.candidateForm.list[i].value = val.join(',')
selectId.push(selectData[o].userId)
this.$set(this.selectId, selectData[o].nodeId, selectId)
}
}
}
this.$set(this.selectVal, nodeId, selectVal)
},
submit() {
const query = {
candidateType: this.candidateType,
branchList: this.candidateForm.branchList
}
if (this.isCandidate) {
query.candidateList = this.selectId
for (let rules of this.candidateForm.list) {
if (!rules.value) return this.$u.toast(
`${rules.nodeName}不能为空`
)
}
}
this.$emit('submitCandidate', query);
},
cancel() {
this.close()
},
close() {
this.$emit('input', false);
}
}
};
</script>
<style lang="scss" scoped>
.candidateForm-v {
.jnpf-wrap,
.jnpf-wrap-form {
padding: 0;
}
}
</style>

View File

@@ -0,0 +1,92 @@
<template>
<view class="dataForm-v">
<u-popup mode="left" :popup="false" v-model="show" length="auto" @close="close" width="100%">
<view class="diyTitle u-flex">
<uni-icons type="back" size="27" class="uni-btn-icon" @click="black"></uni-icons>
<view class="txt">异常处理</view>
</view>
<view class="jnpf-wrap-form u-p-l-20 u-p-r-20">
<u-form :model="errorDataForm" ref="errorDataForm" :errorType="['toast']" label-position="left"
:label-width="250" label-align="left">
<u-form-item prop="errorDataForm" v-for="(item,index) in list" :key="index" :label="item.nodeName"
required>
<JnpfUserSelect v-model="errorDataForm.errorRuleUserList[item.nodeCode]" :multiple="true"
placeholder="请选择异常处理人" />
</u-form-item>
</u-form>
<view class="buttom-actions">
<u-button class="buttom-btn" @click="cancel">取消</u-button>
<u-button class="buttom-btn" type="primary" @click="submit">确定</u-button>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
props: {
// 通过双向绑定控制组件的弹出与收起
},
data() {
return {
errorDataForm: {
errorRuleUserList: {},
},
list: [],
show: false,
query: {}
};
},
methods: {
init(list, query) {
this.show = true
this.list = list
this.list.map(o => {
this.$set(this.errorDataForm.errorRuleUserList, o.nodeCode, [])
})
this.query = {
...query
}
},
submit() {
const hasEmptyUserList = Object.keys(this.errorDataForm.errorRuleUserList).some(rules => {
return !this.errorDataForm.errorRuleUserList[rules].length;
});
if (hasEmptyUserList) return this.$u.toast('异常处理人员不能为空');
const query = {
errorRuleUserList: this.errorDataForm.errorRuleUserList,
...this.query
}
this.$emit('submitErrorForm', query);
},
cancel() {
this.close()
},
black() {
this.close()
},
close() {
this.show = false
}
}
};
</script>
<style lang="scss" scoped>
.dataForm-v {
.diyTitle {
height: 80rpx;
padding: 14rpx 6rpx;
text-align: center;
justify-content: flex-start;
.uniui-back {
font-size: 27px;
font-weight: lighter;
}
.txt {
flex: 0.95;
}
}
}
</style>

View File

@@ -0,0 +1,280 @@
<template>
<view class="record-v">
<view class="discuss_box" v-if="commentList.length">
<view class="u-flex-col discuss_list" v-for="(item,index) in commentList" :key="index">
<view class="u-flex discuss_txt">
<view class="discuss_txt_left u-flex">
<u-avatar :src="baseURL+item.creatorUserHeadIcon"></u-avatar>
<span class="uName">
<span class="comment-header-color">{{item.creatorUser}}</span>
<span v-if="item.replyUser">
<span class="replyText comment-content-color">回复</span>
<span class="replyText comment-header-color">{{ item.replyUser }}</span>
<span class="replyText"> <span class="icon-ym icon-ym-chat"
@click="openReplyText(item.replyText)"></span></span>
</span>
</span>
</view>
</view>
<view class="u-flex-col discuss_content">
<view class="msg-text">
<view v-for="(item2,j) in item.text" :key="j">
<text class="txt comment-content-color" v-if="item2.type=='text'">{{item2.content}}</text>
<image class="msg-text-emoji" :src="item2.content" v-if="item2.type=='emjio'" />
</view>
</view>
<JnpfUploadImg v-model="item.image" disabled detailed align='left' v-if="item.isDel != 2" />
<view v-for='(file,f) in item.file' :key="f" class="jnpf-file-item u-type-primary u-flex u-line-1"
@click="openFile(file)">
<view class="u-line-1" style="margin-bottom: 10rpx;">
{{file.name}}
</view>
</view>
</view>
<view class="u-flex discuss_txt time_button">
<text
class="discuss_txt_left u-flex comment-creator-time">{{$u.timeFormat(item.creatorTime,'yyyy-mm-dd hh:MM:ss')}}</text>
<view>
<text v-if="item.isDel == 1" class="del" @click.stop="delComment(item.id, index)">删除</text>
<text v-if="item.isDel != 2" class="reply" @click.stop="handleReply(item.id)">回复</text>
</view>
</view>
<u-divider half-width="100%" :margin-top="32" :margin-bottom="32" :use-slot="false"
v-if="index != commentList.length-1" />
</view>
</view>
<JnpfEmpty v-else />
</view>
</template>
<script>
import {
getCommentList,
delComment
} from '@/api/workFlow/flowEngine'
import {
getDownloadUrl
} from '@/api/common'
import {
emojiList,
imagesMap
} from '../../flowBefore/emoji'
export default {
props: {
taskId: {
type: [String, Number],
default: ''
},
},
data() {
return {
emojiList: emojiList,
commentList: []
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
getCommentList() {
let query = {
currentPage: 1,
pageSize: 100000,
sort: 'desc',
sidx: '',
taskId: this.taskId
}
getCommentList(query).then(res => {
this.commentList = [];
const list = res.data.list.map((o) => {
o.image = JSON.parse(o.image)
o.file = JSON.parse(o.file)
o.text = this.replaceEmoji(o.text)
return o
})
this.commentList = this.commentList.concat(list);
}).catch((err) => {})
},
openReplyText(replyText) {
uni.showModal({
content: replyText,
showCancel: false,
success: res => {}
})
},
replaceEmoji(str) { //替换表情符号为图片
if (!str) return ''
let replacedStr = str.replace(/\[([^(\]|\[)]*)\]/g, item => 'jnpfjnpf' + item + 'jnpfjnpf');
let strArr = replacedStr.split(/jnpfjnpfjnpfjnpf|jnpfjnpf/g)
strArr = strArr.filter(o => o)
let contentList = []
for (let i = 0; i < strArr.length; i++) {
let item = {
content: strArr[i],
type: 'emjio'
}
if (/\[([^(\]|\[)]*)\]/.test(strArr[i])) {
let content = ''
for (let j = 0; j < this.emojiList.length; j++) {
let row = this.emojiList[j];
if (row.alt == strArr[i]) {
content = this.getEmojiUrl(row.url)
break
}
}
item = {
content: content,
type: 'emjio'
}
} else {
item = {
content: strArr[i],
type: 'text'
}
}
contentList.push(item)
}
return contentList
},
getEmojiUrl(url) {
return imagesMap[url.replace('.', '')]
},
delComment(id, i) {
uni.showModal({
title: '提示',
content: '确定删除?',
success: (res) => {
if (res.confirm) {
delComment(id).then(res => {
this.getCommentList()
this.commentList.splice(i, 1)
})
}
}
})
},
handleReply(id) {
this.$emit('handleReply', id)
},
openFile(item) {
// #ifdef MP
this.previewFile(item)
// #endif
// #ifdef H5 || APP
getDownloadUrl('annex', item.fileId).then(res => {
// #ifdef H5
window.location.href = this.baseURL + res.data.url + '&name=' + item.name;
// #endif
// #ifdef APP
uni.downloadFile({
url: this.baseURL + res.data.url + '&name=' + item.name,
success: function(res) {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: filePath,
showMenu: true,
success: function(res) {}
});
}
});
// #endif
})
// #endif
},
previewFile(item) {
let url = item.url
uni.downloadFile({
url: this.baseURL + url,
success: (res) => {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: encodeURI(filePath),
success: (res) => {}
});
}
});
}
// 流程评论end
}
}
</script>
<style lang="scss">
.record-v {
height: 100%;
background-color: #fff;
.msg-text {
border-radius: 4rpx 30rpx 30rpx 30rpx;
padding: 16rpx 32rpx;
display: flex;
align-items: center;
font-size: 14px;
line-height: 17px;
font-family: PingFang SC;
word-break: break-word;
flex-wrap: wrap;
background-color: #fff;
.msg-text-emoji {
vertical-align: top;
width: 48rpx;
height: 48rpx;
display: inline-block;
}
}
.discuss_box {
padding: 20rpx;
.discuss_list {
.time_button {
padding-left: 110rpx;
margin-top: 20rpx;
padding-right: 10rpx;
}
.discuss_txt {
width: 100%;
justify-content: space-between;
.discuss_txt_left {
.uName {
margin-left: 8px;
font-size: 14px;
font-family: PingFang SC;
line-height: 17rpx;
.replyText {
margin-left: 8px;
}
}
}
.del {
color: red;
margin-left: 20px;
}
.reply {
margin-left: 20px;
}
}
.discuss_content {
font-size: 12px;
padding-left: 70rpx;
.img_box {
margin: 40rpx 0;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,125 @@
<template>
<uni-popup ref="taskLogPoup" background-color="#fff" border-radius="8rpx 8rpx 0 0" :is-mask-click="false">
<view class="timeLine-popup-content u-flex-col">
<view class="u-flex head-title">
<text class="text">任务流程</text>
<text class="text icon-ym icon-ym-fail" @click="popupClose"></text>
</view>
<view class="content" v-for="item,index in taskLogList" :key="index" v-if="taskLogList.length">
<view class="u-flex-col content-info-process">
<view class="info-process-item">
<view class="u-flex u-m-t-20 u-m-b-20 process-item-head">
<view class="left">
<text>触发时间:</text>
<text>{{item.startTime?$u.timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss'):''}}</text>
</view>
<u-tag :text="item.isAsync == 1 ? '异步' : '同步'" size="mini"
:type="item.isAsync == 1 ? 'error' : 'primary'" />
</view>
<view class="process-list u-m-t-14">
<view class="u-flex list u-p-l-12 u-p-r-12" v-for="(child,c) in item.recordList" :key="c">
<view class="list-left">
<u-icon :name="child.status === 0 ? 'checkmark-circle-fill' : 'close-circle-fill'"
:color="child.status === 0 ? '#52c41a' : '#f4420a' "></u-icon>
<text class="u-m-l-8">{{child.nodeName}}</text>
</view>
<text class="u-m-l-8 r-txt" v-if="child.status !== 0"
@click="jumpErrorPage(child)">查看异常</text>
</view>
</view>
</view>
</view>
</view>
<JnpfEmpty v-else />
</view>
</uni-popup>
</template>
<script>
export default {
props: {
taskLogList: {
type: Array,
default: () => []
}
},
data() {
return {}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
jumpErrorPage(e) {
let data = {
errorData: e.errorData,
errorTip: e.errorTip
}
uni.navigateTo({
url: '/pages/workFlow/flowBefore/logError?data=' + this.jnpf.base64.encode(JSON.stringify(
data),
"UTF-8"),
})
},
open() {
this.$nextTick(() => {
this.$refs.taskLogPoup.open('bottom')
})
},
popupClose() {
this.$refs.taskLogPoup.close()
}
}
}
</script>
<style lang="scss" scoped>
.timeLine-popup-content {
padding: 20rpx;
height: 1200rpx;
overflow-y: scroll;
.head-title {
justify-content: space-between;
color: #333333;
/* #ifdef APP-PLUS */
padding: 20rpx 0;
/* #endif */
height: 80rpx;
}
.content {
.content-info-process {
.info-process-item {
border-bottom: 1rpx solid #d7d7d7;
padding-bottom: 26rpx;
.process-item-head {
justify-content: space-between;
}
.process-list {
background-color: #f1f4f8;
border-radius: 8rpx;
.list {
height: 68rpx;
align-items: center;
border-bottom: 1rpx solid #e7e9ec;
justify-content: space-between;
.r-txt {
color: #0000ff;
}
&:last-child {
border-bottom: none
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,300 @@
<template>
<view class="u-p-l-20 u-p-r-20 u-p-t-20">
<u-time-line>
<u-time-line-item nodeTop="2" class="u-p-b-20" v-for="item,index in progressList" :key="index">
<template v-slot:node>
<view class="u-node" :style="{ background: getTimeLineTagColor(item.nodeStatus) }"></view>
</template>
<template v-slot:content>
<view class="u-font-24 content">
<view class="u-order-title u-flex u-m-b-8">
<view class="u-line-1 name" v-if="['start', 'end', 'outside'].includes(item.nodeType)">
{{item.nodeName}}
</view>
<view class="u-line-1 name" v-else>
{{item.nodeName + getCounterSignContent(item.counterSign, item.assigneeType)}}
</view>
<view class="u-m-l-10">
<!-- 流程状态 -->
<u-tag :text="getNodeStatusContent(item.nodeStatus)" mode="light" size="mini"
shape="circle" :type="getNodeStatusColor(item.nodeStatus)"
v-if="item.nodeType !=='end' && getNodeStatusContent(item.nodeStatus)" />
<text
class="u-m-l-10">{{item.startTime ? $u.timeFormat(item.startTime, 'mm-dd hh:MM'):''}}</text>
</view>
</view>
<view class="u-flex avatar-box" v-if="item.nodeStatus == 1">
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
class="avatar"></u-avatar>
<text class="u-m-l-8">发起人{{taskInfo.creatorUser}}</text>
</view>
<view class="u-flex-col approver-list" v-if="item.nodeStatus != 1 && item.approver?.length">
<view class="u-flex approver-item" @click="openApprover(item)">
<view class="u-flex approver-list-l">
<view class="u-flex-col approver-list-l-box" v-for="child,i in item.approver"
:key="i">
<u-avatar :src="baseURL + child.headIcon" size="mini" mode="circle"
class="avatar"></u-avatar>
<u-tag :text="useDefine.getFlowStateContent(child.handleType)" mode="light"
v-if="useDefine.getFlowStateContent(child.handleType)" class="tag"
size="mini"
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
color="#fff" />
<text class="u-m-t-20 u-line-1 approver-user-name">
{{child.userName}}
</text>
</view>
</view>
<view class="u-m-l-20 approver-list-r u-flex">
<view class="approver-list-r-box">{{item.approverCount}}</view>
<text class="icon-ym icon-ym-right u-m-r-12"></text>
</view>
</view>
<view class="bottom-block" @click="openTaskLogModal(item)" v-if="item.showTaskFlow">
<u-tag text="任务流程" type="info" mode="dark" class="u-m-l-10" />
<text class="icon-ym icon-ym-right u-m-r-12"></text>
</view>
</view>
<view class="u-flex outside-sign" v-if="item.nodeType == 'outside'">
<view>数据传递{{ getOutsideState(item.outSideStatus)}}</view>
<view v-if="!item.outSideStatus">
<text class="u-m-l-8 r-txt btn-link" @click="handleShowErrorModal(item)">查看异常</text >
<text class="u-m-l-8 r-txt btn-link danger" @click="handleRetry(item)" v-if="item.isRetry">重试</text >
</view>
</view>
</view>
</template>
</u-time-line-item>
</u-time-line>
</view>
<TimeLinePopup :popupTitle="popupTitle" :recordList="recordList" ref="TimeLinePopup">
</TimeLinePopup>
<TaskLogModal ref="TaskLogModal" :taskLogList="taskLogList" />
</template>
<script>
import {
recordList,
taskList,
retryOutside
} from '@/api/workFlow/flowBefore'
import fileList from './fileList.vue'
import TimeLinePopup from './TimeLinePopup.vue'
import TaskLogModal from './TaskLogModal.vue'
import {
useDefineSetting
} from '@/utils/useDefineSetting';
export default {
components: {
fileList,
TimeLinePopup,
TaskLogModal
},
props: {
progressList: {
type: Array,
default: () => []
},
taskInfo: {
type: Object,
default: () => {}
},
},
data() {
return {
useDefine: useDefineSetting(),
recordList: [],
popupTitle: '',
taskLogList: []
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
getCounterSignContent(counterSign, assigneeType) {
if (assigneeType == 10) return '(逐级审批)';
return counterSign == 0 ? '(或签)' : counterSign == 1 ? '(会签)' : '(依次审批)';
},
getNodeStatusColor(status) {
return status == 1 || status == 2 ? 'success' : status == 3 ? 'error' : 'primary';
},
getNodeStatusContent(status) {
const list = ['', '已提交', '已通过', '已拒绝', '审批中', '已退回', '已撤回', '等待中', '办理中'];
return list[status] || '';
},
/* 任务流程 */
openTaskLogModal(item) {
let data = {
taskId: this.taskInfo.id,
nodeCode: item.nodeId
}
taskList(data).then(res => {
this.taskLogList = res.data || []
this.$nextTick(() => {
this.$refs.TaskLogModal.open()
})
})
},
openApprover(item) {
if (item.nodeType === "start" || item.nodeType === "end") return
this.popupTitle = item.nodeName + this.getCounterSignContent(item.counterSign, item.assigneeType)
this.nodeId = item.nodeId
this.getRecordList()
},
getTimeLineTagColor(status) {
return status == 1 || status == 2 ? '#08AF28' : status != 3 ? '#0177FF' : '#ed6f6f';
},
getRecordList() {
let data = {
taskId: this.taskInfo.id,
nodeId: this.nodeId
}
recordList(data).then(res => {
let list = res.data || []
list.map(o => {
o.fileList = JSON.parse(o.fileList)
})
this.recordList = list
this.$nextTick(() => {
this.$refs.TimeLinePopup.open()
})
})
},
getOutsideState(state) {
return state ? '成功' : '失败';
},
handleShowErrorModal(e) {
let data = {
errorData: e.errorData,
errorTip: e.errorTip
}
uni.navigateTo({
url: '/pages/workFlow/flowBefore/logError?data=' + this.jnpf.base64.encode(JSON.stringify(
data),
"UTF-8"),
})
},
handleRetry(e) {
retryOutside(e.nodeId).then((res) => {
uni.showToast({
title: res.msg,
complete: () => {
uni.$emit('refresh')
},
})
})
}
}
}
</script>
<style lang="scss">
.u-node {
width: 20rpx;
height: 20rpx;
border-radius: 50%;
}
.active {
background: #02a7f0;
}
.content {
.u-order-title {
.name {
flex: 1;
}
}
.avatar-box {
::v-deep .u-avatar {
// width: 2rem !important;
// height: 2rem !important;
}
}
.outside-sign {
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 16px;
cursor: pointer;
background-color: #F5F5F5;
.btn-link {
background: none;
border: none;
color: #007AFF;
padding: 0;
font-size: 28rpx;
line-height: 1;
}
.danger {
color: #ff0000;
padding-left: 12px;
}
}
.approver-list {
border-radius: 8rpx;
min-height: 140rpx;
background-color: #F5F5F5;
.approver-item {
.approver-list-l {
flex: 1;
.approver-list-l-box {
width: 120rpx;
align-items: center;
position: relative;
.tag {
position: absolute;
top: 50rpx;
}
.approver-user-name {
color: #303133;
width: 120rpx;
text-align: center;
}
}
}
.approver-list-r {
height: 4.375rem;
.approver-list-r-box {
width: 24px;
height: 24px;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background-color: #fff;
}
}
}
.bottom-block {
height: 80rpx;
display: flex;
align-items: center;
justify-content: space-between;
border-top: 1rpx solid #e5e5e5;
:deep(.u-tag) {
padding: 8rpx;
background-color: rgb(165, 168, 172);
}
}
}
}
</style>

View File

@@ -0,0 +1,173 @@
<template>
<uni-popup ref="flowStepPopup" background-color="#fff" border-radius="8rpx 8rpx 0 0" :is-mask-click="false">
<view class="timeLine-popup-content u-flex-col">
<view class="u-flex head-title">
<text class="text">{{popupTitle}}</text>
<text class="text icon-ym icon-ym-fail" @click="popupClose"></text>
</view>
<view class="content" v-for="item,index in recordList" :key="index" v-if="recordList.length">
<!--头部 -->
<view class="u-flex u-m-t-20 content-info">
<view class="u-flex content-info-left">
<u-avatar :src="baseURL + item.headIcon" size="mini" mode="circle" class="avatar"></u-avatar>
<view class="u-m-l-10 name-box">
<p>{{item.userName}}</p>
<p class="name">
{{item.handleTime?$u.timeFormat(item.handleTime, 'yyyy-mm-dd hh:MM:ss'):''}}
</p>
</view>
</view>
<u-tag :text="useDefine.getFlowStateContent(item.handleType)"
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))"
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))" color="#fff"
size="mini" shape="circle" />
<view class="content-info-right u-line-1" v-if="item.handleUserName">
<u-icon name="arrow-rightward" color="#1890ff" class="u-m-l-10 u-m-r-10"></u-icon>
<text class="u-font-24 txt u-line-1">{{item.handleUserName}}</text>
</view>
</view>
<view class="content-info-bottom" v-if="item.signImg || item.fileList?.length || item.handleOpinion">
<text class="u-line-2" v-if="item.handleOpinion">{{item.handleOpinion}}</text>
<fileList :fileList="item.fileList" v-if="item?.fileList?.length"></fileList>
<view class="u-flex sign-box" v-if="item.signImg">
<view class="sign-title">签名</view>
<JnpfSign v-model="item.signImg" align="left" detailed />
</view>
<view class="approvalField" v-for="(field,index) in item.approvalField" :key="index">
<text>{{field.fieldName+''}}</text>
<text>{{field.value}}</text>
</view>
</view>
</view>
<JnpfEmpty v-else />
</view>
</uni-popup>
</template>
<script>
import fileList from './fileList.vue'
import {
useDefineSetting
} from '@/utils/useDefineSetting';
export default {
components: {
fileList
},
props: {
recordList: {
type: Array,
default: () => []
},
popupTitle: {
type: String,
default: ''
}
},
data() {
return {
useDefine: useDefineSetting(),
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
open() {
this.$nextTick(() => {
this.$refs.flowStepPopup.open('bottom')
})
},
popupClose() {
this.$refs.flowStepPopup.close()
}
}
}
</script>
<style lang="scss" scoped>
.timeLine-popup-content {
padding: 20rpx;
height: 1200rpx;
overflow-y: scroll;
.head-title {
justify-content: space-between;
color: #333333;
/* #ifdef APP-PLUS */
padding: 20rpx 0;
/* #endif */
}
.content {
.content-info {
height: 100rpx;
.content-info-left {
flex: 1;
.name-box {
.name {
color: #606266;
}
}
}
.content-info-right {
max-width: 228rpx;
.txt {
color: #303133;
}
}
}
.content-info-bottom {
background-color: #F5F5F5;
padding: 20rpx;
border-radius: 8rpx;
color: #303133;
.approvalField {
padding: 10rpx 0;
}
.sign-box {
height: 88rpx;
.sign-title {
width: 120rpx;
}
}
}
.content-info-process {
.info-process-item {
border-bottom: 1rpx solid #d7d7d7;
padding-bottom: 26rpx;
.process-item-head {
justify-content: space-between;
}
.process-list {
background-color: #f1f4f8;
border-radius: 8rpx;
.list {
height: 68rpx;
align-items: center;
border-bottom: 1rpx solid #e7e9ec;
justify-content: space-between;
.r-txt {
color: #0000ff;
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,125 @@
<template>
<view class="" style="padding: 20rpx 0;">
<view v-for='(item,index) in fileList' :key="index"
class="jnpf-file-item u-type-primary u-flex u-line-1 u-m-t-10" @tap='downLoad(item)'>
<view class="jnpf-file-item-txt u-line-1">{{item.name}}</view>
</view>
</view>
</template>
<script>
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
import {
getDownloadUrl
} from '@/api/common'
import jnpf from '@/utils/jnpf'
export default {
props: {
fileList: {
type: Array,
default: () => []
}
},
data() {
return {
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
downLoad(item) {
if (item.fileExtension && imgTypeList.includes(item.fileExtension)) return this.previewImage(item)
// #ifdef MP
this.previewFile(item)
// #endif
// #ifndef MP
getDownloadUrl('annex', item.fileId).then(res => {
const fileUrl = this.baseURL + res.data.url + '&name=' + item.name;
// #ifdef H5
window.location.href = fileUrl;
// #endif
// #ifdef APP-PLUS
this.downloadFile(res.data.url);
// #endif
})
// #endif
},
previewFile(item) {
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx']
let url = item.url
let fileType = url.split('.')[1]
if (fileTypes.includes(fileType)) {
uni.downloadFile({
url: this.baseURL + url,
success: (res) => {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: encodeURI(filePath),
showMenu: true,
fileType: fileType,
success: (res) => {
console.log('打开文档成功');
},
fail(err) {
console.log('小程序', err);
}
});
}
});
} else {
this.$u.toast(
'该文件类型无法打开'
)
}
},
previewImage(item) {
if (!item.url) return
const url = jnpf.getAuthImgUrl(item.url)
uni.previewImage({
urls: [url],
current: url,
success: () => {},
fail: () => {
uni.showToast({
title: '预览图片失败',
icon: 'none'
});
}
});
},
downloadFile(url) {
uni.downloadFile({
url: this.baseURL + url,
success: res => {
if (res.statusCode === 200) {
uni.saveFile({
tempFilePath: res.tempFilePath,
success: red => {
uni.showToast({
icon: 'none',
mask: true,
title: '文件已保存:' + red.savedFilePath, //保存路径
duration: 3000,
});
setTimeout(() => {
uni.openDocument({
filePath: red.savedFilePath,
success: ress => {},
fail(err) {}
});
}, 500)
}
});
}
}
});
},
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,228 @@
<template>
<view class="flowStep u-m-t-20">
<view class="tabsContent">
<u-tabs :list="getList" :current="current" @change="change"></u-tabs>
<view class="time-line-box" v-if="opType != '-1'">
<TimeLine :progressList="progressList" :taskInfo="taskInfo" v-if="tabId == 0"></TimeLine>
<ProcessComments ref="ProcessComments" v-if="tabId == 1" :taskId="taskId" @handleReply="handleReply">
</ProcessComments>
<view class="u-p-l-20 u-p-r-20" v-if="tabId == 2">
<dataLog :dataLogList="dataLogList"></dataLog>
</view>
<assistantMsg v-if="tabId == 3" :auxiliaryInfo="auxiliaryInfo" :formData="formData"></assistantMsg>
</view>
</view>
</view>
</template>
<script>
import TimeLine from './TimeLine.vue'
import dataLog from '@/components/dataLog'
import ProcessComments from './ProcessComments.vue'
import assistantMsg from '@/components/assistantMsg'
export default {
components: {
ProcessComments,
TimeLine,
dataLog,
assistantMsg
},
props: {
dataLogList: {
type: Array,
default: () => []
},
progressList: {
type: Array,
default: () => []
},
commentList: {
type: Array,
default: () => []
},
taskInfo: {
type: Object,
default: () => {}
},
formData: {
type: Object,
default: () => {}
},
auxiliaryInfo: {
type: Array,
default: () => []
},
taskId: {
type: [String, Number],
default: ''
},
hasComment: {
default: false
},
dataLog: {
default: false
},
formID: {
type: [String, Number],
default: ''
},
opType: {
type: [String, Number],
default: ''
},
},
data() {
return {
current: 0,
value: '',
popupTitle: '',
nodeId: '',
tabId: 0
}
},
computed: {
baseURL() {
return this.define.baseURL
},
getList() {
const list = [];
const OP_TYPE_INVALID = '-1';
if (this.opType !== OP_TYPE_INVALID) {
list.push({
name: '流转',
id: 0
});
if (this.hasComment) {
list.push({
name: '评论',
id: 1
});
};
if (this.dataLog) {
list.push({
name: '修改记录',
id: 2
})
}
}
if (this.auxiliaryInfo.length) {
list.push({
name: '辅助信息',
id: 3
});
}
return list
},
},
methods: {
handleReply(id) {
this.$emit('handleReply', id)
},
popupClose() {
this.$refs.flowStepPopup.close()
},
change(e) {
this.current = e
this.tabId = this.getList[e].id
if (this.current === 1) {
this.$nextTick(() => {
this.$refs.ProcessComments.getCommentList()
})
}
}
}
}
</script>
<style lang="scss">
.flowStep {
width: 100%;
.tabsContent {
.time-line-box {
background-color: #fff;
}
.bottom-btn {
background-color: #fff;
width: 100%;
height: 88rpx;
padding: 0 20rpx;
color: #7f7f7f;
.btn {
background-color: #f2f2f2;
border-radius: 8rpx;
height: 60rpx;
padding-left: 10rpx;
width: 100%;
line-height: 60rpx;
}
}
}
}
.timeLine {
width: 100%;
height: 100%;
padding: 20rpx;
.u-time-axis-item {
.u-time-axis-node {
top: 0 !important;
}
}
.timeLine-right {
padding-left: 0;
padding-right: 40rpx !important;
&::before {
left: 670rpx !important;
}
.u-time-axis-item {
.u-time-axis-node {
left: 670rpx !important;
}
}
}
.timeLine-dot {
width: 28rpx;
height: 28rpx;
border: 4rpx solid #FFFFFF;
box-shadow: 0 6rpx 12rpx rgba(2, 7, 28, 0.16);
border-radius: 50%;
}
.timeLine-content {
padding: 0 10rpx;
.timeLine-title {
font-size: 30rpx;
line-height: 36rpx;
.name {
margin-bottom: 6rpx;
}
}
.timeLine-title2 {
margin-top: 6rpx;
background: rgba(255, 255, 255, 0.39);
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.1);
padding: 10rpx 20rpx;
border-radius: 4px;
}
}
.timeLine-desc {
margin-top: 10rpx;
font-size: 26rpx;
line-height: 36rpx;
color: #909399;
margin-bottom: 10rpx;
}
}
</style>

View File

@@ -0,0 +1,126 @@
<template>
<view class="" style="padding: 20rpx 0;">
<view v-for='(item,index) in fileList' :key="index"
class="jnpf-file-item u-type-primary u-flex u-line-1 u-m-t-10" @tap='downLoad(item)'>
<view class="jnpf-file-item-txt u-line-1">{{item.name}}</view>
</view>
</view>
</template>
<script>
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
import {
getDownloadUrl
} from '@/api/common'
import jnpf from '@/utils/jnpf'
export default {
props: {
fileList: {
type: Array,
default: () => []
}
},
data() {
return {
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
downLoad(item) {
if (item.fileExtension && imgTypeList.includes(item.fileExtension)) return this.previewImage(item)
// #ifdef MP
this.previewFile(item)
// #endif
// #ifndef MP
getDownloadUrl('annex', item.fileId).then(res => {
const fileUrl = this.baseURL + res.data.url + '&name=' + item.name;
// #ifdef H5
window.location.href = fileUrl;
// #endif
// #ifdef APP-PLUS
this.downloadFile(res.data.url);
// #endif
})
// #endif
},
previewFile(item) {
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx']
let url = item.url
let fileType = url.split('.')[1]
if (fileTypes.includes(fileType)) {
uni.downloadFile({
url: this.baseURL + url,
success: (res) => {
var filePath = res.tempFilePath;
uni.openDocument({
filePath: encodeURI(filePath),
showMenu: true,
fileType: fileType,
success: (res) => {
console.log('打开文档成功');
},
fail(err) {
console.log('小程序', err);
}
});
}
});
} else {
this.$u.toast(
'该文件类型无法打开'
)
}
},
previewImage(item) {
if (!item.url) return
const url = jnpf.getAuthImgUrl(item.url)
uni.previewImage({
urls: [url],
current: url,
success: () => {},
fail: () => {
uni.showToast({
title: '预览图片失败',
icon: 'none'
});
}
});
},
downloadFile(url) {
uni.downloadFile({
url: this.baseURL + url,
success: res => {
if (res.statusCode === 200) {
uni.saveFile({
tempFilePath: res.tempFilePath,
success: red => {
uni.showToast({
icon: 'none',
mask: true,
title: '文件已保存:' + red.savedFilePath, //保存路径
duration: 3000,
});
setTimeout(() => {
uni.openDocument({
filePath: red.savedFilePath,
success: ress => {},
fail(err) {}
});
}, 500)
}
});
}
}
});
},
}
}
</script>
<style>
</style>

View File

@@ -0,0 +1,150 @@
<template>
<view class="flowBefore-actions" :style="{'height': '88rpx'}"
v-if="hasComment || (actionList.length || rightBtnList.length) ">
<CustomButton v-if="showMoreBtn" btnText="更多" btnType="more" iconName="more-dot-fill" size="28"
@handleBtn="showAction = $event" :btnLoading="loading" class="u-flex-col buttom-btn-left-inner" />
<template v-if="opType != 2">
<CustomButton v-if="showSaveBtn" :btnText="saveBtnText || '暂存'" customIcon
class="u-flex-col buttom-btn-left-inner" btnIcon="icon-ym icon-ym-extend-save" size="28"
@handleBtn="handleBtn('save')" btnType="save" :btnLoading="loading" />
<CustomButton v-if="showRejectBtn" btnText="拒绝" iconName="minus-circle" size="28"
@handleBtn="handleBtn('reject')" btnType="reject" :btnLoading="loading"
class="u-flex-col buttom-btn-left-inner" />
</template>
<view class="rightBtn u-flex">
<u-button class="buttom-btn" @click.stop="handleBtn('comment')" v-if="!rightBtnList.length"
:loading="loading">
评论
</u-button>
<template v-if="opType != 2">
<u-button v-for="btn,index in rightBtnList" :key="index" class="buttom-btn"
@click.stop="handleBtn(btn.id)" :style="{'width':btn.width}"
:class="{'delegate-submit-btn':btn.id === 'delegateSubmit'}" :loading="loading" :type="btn?.type">
{{ btn.fullName}}
</u-button>
</template>
<template class="" v-else>
<u-button v-for="btn,index in todoBtnList" :key="index" class="buttom-btn"
@click.stop="handleBtn(btn.id)" :style="{'width':btn.width}" :loading="loading" :type="btn?.type">
{{ btn.fullName}}
</u-button>
</template>
</view>
<u-action-sheet v-model="showAction" :list="actionList" @click="handleAction" />
</view>
</template>
<script>
import CustomButton from '@/components/CustomButton'
export default {
components: {
CustomButton
},
props: {
actionList: {
type: Array,
default: () => ([])
},
rightBtnList: {
type: Array,
default: () => ([])
},
todoBtnList: {
type: Array,
default: () => ([])
},
btnInfo: {
type: Object,
default: () => ({})
},
opType: {
type: [String, Number],
default: ''
},
saveBtnText: {
type: String,
default: ''
},
btnLoading: {
type: Boolean,
default: false
},
hideSaveBtn: {
type: Boolean,
default: false
},
hasSignFor: {
type: Boolean,
default: false
},
hasComment: {
type: Boolean,
default: false
},
isProcessing: {
type: [String, Number],
default: ''
}
},
computed: {
loading() {
return this.btnLoading
},
showMoreBtn() {
if (this.actionList.length) return true
return false
},
showRejectBtn() {
if (this.btnInfo?.hasRejectBtn && !this.isProcessing && this.opType != 2) return true
return false
},
showSaveBtn() {
if (this.btnInfo?.hasSaveBtn && !this.hideSaveBtn) return true
return false
}
},
data() {
return {
showAction: false
}
},
methods: {
handleBtn(type, item) {
if (!this.loading) this.$emit('handleBtn', type, item)
},
//更多按钮
handleAction(index) {
this.handleBtn(this.actionList[index].id, this.actionList[index])
}
}
}
</script>
<style lang="scss">
.flowBefore-actions {
.poup-btn {
background-color: #fff;
width: 100%;
height: 88rpx;
padding: 0 20rpx;
color: #7f7f7f;
border-bottom: 1rpx solid #d7d7d7;
.btn {
background-color: #f2f2f2;
border-radius: 8rpx;
height: 60rpx;
padding-left: 10rpx;
width: 100%;
line-height: 60rpx;
}
}
}
.delegate-submit-btn {
background-color: #50abf6;
button {
background-color: #50abf6 !important;
}
}
</style>

View File

@@ -0,0 +1,372 @@
<template>
<view class="flowStep u-m-t-20">
<view class="tabsContent">
<u-tabs :list="list" :current="current" @change="change"></u-tabs>
<view class="u-p-l-20 u-p-r-20 time-line-box">
<view class="u-p-l-20 u-p-r-20 u-p-t-20" v-if="current == 0">
<TimeLine :progressList="progressList" :taskInfo="taskInfo"></TimeLine>
<!-- <u-time-line>
<u-time-line-item nodeTop="2" class="u-p-b-20" v-for="item,index in progressList" :key="index">
<template v-slot:node>
<view class="u-node"></view>
</template>
<template v-slot:content>
<view class="u-font-24 content">
<view class="u-order-title u-flex u-m-b-8">
<view class="u-line-1 name">
{{item.nodeName + getCounterSignContent(item.counterSign)}}
</view>
<view class="u-m-l-10">
<u-tag :text="getNodeStatusContent(item.nodeStatus)" mode="light"
size="mini" shape="circle"
:type="getNodeStatusColor(item.nodeStatus)" />
<text
class="u-m-l-10">{{item.startTime?$u.timeFormat(item.startTime, 'mm-dd hh:MM'):''}}</text>
</view>
</view>
<view class="u-flex avatar-box" v-if="item.nodeStatus == 1">
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
class="avatar"></u-avatar>
<text class="u-m-l-8">发起人{{taskInfo.creatorUser}}</text>
</view>
<view class="u-flex approver-list" v-else>
<view class="u-flex approver-list-l">
<view class="u-flex-col approver-list-l-box"
v-for="child,i in item.approver" :key="i">
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
class="avatar"></u-avatar>
<u-tag :text="useDefine.getFlowStateContent(child.handleType)"
mode="light" v-if="useDefine.getFlowStateContent(child.handleType)"
class="tag" size="mini"
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
color="#fff" />
<text class="u-m-t-20 u-line-1 approver-user-name">
{{child.userName}}
</text>
</view>
</view>
<view class="u-m-l-20 approver-list-r u-flex" @click="openApprover(item)">
<view class="approver-list-r-box">{{item.approverCount}}</view>
<text class="icon-ym icon-ym-right u-m-r-12"></text>
</view>
</view>
</view>
</template>
</u-time-line-item>
</u-time-line> -->
</view>
<view class="u-p-l-20 u-p-r-20" v-if="current == 1">
2
</view>
</view>
</view>
</view>
<uni-popup ref="flowStepPopup" background-color="#fff" border-radius="8rpx 8rpx 0 0" :is-mask-click="false">
<view class="flow-popup-content" style="height: 1200rpx;">
<view class="u-flex head-title">
<text class="text">{{popupTitle}}</text>
<text class="text icon-ym icon-ym-fail" @click="popupClose"></text>
</view>
<view class="" v-for="item,index in recordList" :key="index" v-if="recordList.length">
<view class="u-flex u-m-t-20" style="height: 100rpx;">
<view class="u-flex" style="flex: 1;">
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
class="avatar"></u-avatar>
<view class="u-m-l-10">
<p>{{item.userName}}</p>
<p style="color: #606266;">
{{item.handleTime?$u.timeFormat(item.handleTime, 'yyyy-mm-dd hh:MM:ss'):''}}
</p>
</view>
</view>
<u-tag :text="useDefine.getFlowStateContent(item.handleType)"
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))"
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))" color="#fff"
size="mini" shape="circle" />
<view class="" v-if="item.handleUserName">
<u-icon name="arrow-rightward" color="#1890ff" class="u-m-l-10 u-m-r-10"></u-icon>
<text class="u-font-24" style="color: #303133;">{{item.handleUserName}}</text>
</view>
</view>
<view class="" style="background-color: #F5F5F5;padding: 20rpx;border-radius: 8rpx;color: #303133;"
v-if="item.signImg || item.fileList.length || item.handleOpinion">
<text class="u-line-2" v-if="item.handleOpinion">{{item.handleOpinion}}</text>
<fileList :fileList="item.fileList" v-if="item.fileList.length"></fileList>
<view class="u-flex" style="height: 88rpx;" v-if="item.signImg">
<view style="width: 60px;">签名</view>
<JnpfSign v-model="item.signImg" align="left" detailed />
</view>
</view>
</view>
<JnpfEmpty v-else />
</view>
</uni-popup>
</template>
<script>
import fileList from './fileList.vue'
import TimeLine from './TimeLine.vue'
import {
useDefineSetting
} from '@/utils/useDefineSetting';
import {
recordList
} from '@/api/workFlow/flowBefore'
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
export default {
components: {
fileList,
TimeLine
},
props: {
progressList: {
type: Array,
default: () => []
},
taskInfo: {
type: Object,
default: () => {}
}
},
data() {
return {
list: [{
name: '流转'
}, {
name: '评论'
}],
current: 0,
value: '',
popupTitle: '',
recordList: [],
nodeId: '',
useDefine: useDefineSetting()
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
methods: {
openApprover(item) {
this.popupTitle = item.nodeName + this.getCounterSignContent(item.counterSign, item.assigneeType)
this.nodeId = item.nodeId
this.$nextTick(() => {
this.$refs.flowStepPopup.open('bottom')
this.getRecordList()
})
},
getRecordList() {
let data = {
taskId: this.taskInfo.id,
nodeId: this.nodeId
}
recordList(data).then(res => {
let list = res.data || []
list.map(o => {
o.fileList = JSON.parse(o.fileList)
})
this.recordList = list
})
},
getCounterSignContent(counterSign, assigneeType) {
if (assigneeType == 10) return '(逐级审批)';
return counterSign == 0 ? '(或签)' : counterSign == 1 ? '(会签)' : '(依次审批)';
},
popupClose() {
this.$refs.flowStepPopup.close()
},
getNodeStatusContent(status) {
const list = ['', '已提交', '已通过', '已拒绝', '审批中'];
return list[status] || '';
},
getNodeStatusColor(status) {
return status == 1 || status == 2 ? 'success' : status == 3 ? 'error' : 'primary';
},
change(e) {
this.current = e
}
}
}
</script>
<style lang="scss">
.flow-popup-content {
padding: 20rpx;
.head-title {
justify-content: space-between;
color: #333333;
// border-bottom: 1rpx solid #f0f0f0;
}
}
.flowStep {
width: 100%;
.tabsContent {
.time-line-box {
background-color: #fff;
.u-node {
background: #19be6b;
width: 20rpx;
height: 20rpx;
border-radius: 50%;
}
.active {
background: #02a7f0;
}
.content {
.u-order-title {
.name {
flex: 1;
}
}
.avatar-box {
::v-deep .u-avatar {
width: 2rem !important;
height: 2rem !important;
}
}
.approver-list {
border-radius: 8rpx;
height: 140rpx;
background-color: #F5F5F5;
.approver-list-l {
flex: 1;
.approver-list-l-box {
width: 120rpx;
align-items: center;
position: relative;
.tag {
position: absolute;
top: 50rpx;
}
.approver-user-name {
color: #303133;
width: 120rpx;
text-align: center;
}
}
}
.approver-list-r {
height: 4.375rem;
.approver-list-r-box {
width: 24px;
height: 24px;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12px;
background-color: #fff;
}
}
}
}
}
.bottom-btn {
background-color: #fff;
width: 100%;
height: 88rpx;
padding: 0 20rpx;
color: #7f7f7f;
.btn {
background-color: #f2f2f2;
border-radius: 8rpx;
height: 60rpx;
padding-left: 10rpx;
width: 100%;
line-height: 60rpx;
}
}
}
}
::v-deep .u-time-axis-item {
margin-bottom: 0 !important;
}
.timeLine {
width: 100%;
height: 100%;
padding: 20rpx;
.u-time-axis-item {
.u-time-axis-node {
top: 0 !important;
}
}
.timeLine-right {
padding-left: 0;
padding-right: 40rpx !important;
&::before {
left: 670rpx !important;
}
.u-time-axis-item {
.u-time-axis-node {
left: 670rpx !important;
}
}
}
.timeLine-dot {
width: 28rpx;
height: 28rpx;
border: 4rpx solid #FFFFFF;
box-shadow: 0 6rpx 12rpx rgba(2, 7, 28, 0.16);
border-radius: 50%;
}
.timeLine-content {
padding: 0 10rpx;
.timeLine-title {
font-size: 30rpx;
line-height: 36rpx;
.name {
margin-bottom: 6rpx;
}
}
.timeLine-title2 {
margin-top: 6rpx;
background: rgba(255, 255, 255, 0.39);
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.1);
padding: 10rpx 20rpx;
border-radius: 4px;
}
}
.timeLine-desc {
margin-top: 10rpx;
font-size: 26rpx;
line-height: 36rpx;
color: #909399;
margin-bottom: 10rpx;
}
}
</style>

View File

@@ -0,0 +1,203 @@
<template>
<u-popup class="jnpf-select" :maskCloseAble="maskCloseAble" mode="bottom" v-model="showPopup"
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close">
<view class="u-select">
<view class="u-select__header" @touchmove.stop.prevent="">
<view class="u-select__header__cancel u-select__header__btn" :style="{ color: cancelColor }"
hover-class="u-hover-class" :hover-stay-time="150" @tap="close()"
style="width: 60rpx;text-align: center;">
<text v-if="cancelBtn">{{cancelText}}</text>
</view>
<view class="u-select__header__title" style="flex: 1;text-align: center;">
{{title}}
</view>
<view class="u-select__header__confirm u-select__header__btn" :style="{ color: confirmColor }"
style="width: 60rpx;text-align: center;" hover-class="u-hover-class" :hover-stay-time="150"
@touchmove.stop="" @tap.stop="handleConfirm()">
<text v-if="confirmBtn">{{confirmText}}</text>
</view>
</view>
<view class="u-select__body u-select__body__multiple">
<scroll-view :scroll-y="true" style="height: 100%">
<view class="u-flex u-p-l-20 u-p-r-20" style="height: 100rpx;border-bottom: 1rpx solid #f0f2f6;"
@click="radioGroupChange('add')">
<text class="u-m-r-20 u-font-28 icon-ym icon-ym-add-folder"></text>
<text>新建文件夹</text>
</view>
<view class="u-flex u-p-l-20 u-p-r-20 uploadFileBtn" @click="radioGroupChange('up')">
<JnpfUploadFileComment ref="lsjUpload" height="100rpx" childId="upload" :size="10"
:parentId="parentId" @callback="onCallback">
</JnpfUploadFileComment>
</view>
</scroll-view>
</view>
</view>
</u-popup>
</template>
<script>
export default {
props: {
height: {
type: [Number, String],
default: ''
},
cancelBtn: {
type: Boolean,
default: true
},
confirmBtn: {
type: Boolean,
default: true
},
show: {
type: Boolean,
default: false
},
cancelColor: {
type: String,
default: '#606266'
},
confirmColor: {
type: String,
default: '#2979ff'
},
safeAreaInsetBottom: {
type: Boolean,
default: false
},
maskCloseAble: {
type: Boolean,
default: true
},
title: {
type: String,
default: ''
},
parentId: {
type: [String, Number],
default: 0
},
cancelText: {
type: String,
default: '取消'
},
confirmText: {
type: String,
default: '确认'
}
},
data() {
return {
showPopup: false,
option: {},
id: ''
}
},
watch: {
show: {
handler(val) {
this.showPopup = val
},
immediate: true
}
},
computed: {
baseURL() {
return this.define.baseURL
},
token() {
return uni.getStorageSync('token')
},
},
methods: {
//文件上传
onCallback(e) {
this.$emit('onCallback', e)
},
// 获取默认选中的值
radioGroupChange(e) {
this.$emit('confirm', e)
},
close() {
this.$emit('close');
},
}
}
</script>
<style scoped lang="scss">
.notData-box {
width: 100%;
height: 100%;
justify-content: center;
align-items: center;
margin-top: -50px;
.notData-inner {
width: 286rpx;
height: 222rpx;
align-items: center;
.iconImg {
width: 100% !important;
height: 100% !important;
}
}
.notData-inner-text {
color: #909399;
}
}
.uploadFileBtn {
height: 3.125rem;
}
.jnpf-select {
width: 100%;
.u-select {
&__header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 20px;
position: relative;
::after {
content: "";
position: absolute;
border-bottom: 0.5px solid #eaeef1;
transform: scaleY(0.5);
bottom: 0;
right: 0;
left: 0;
}
}
&__body {
width: 100%;
overflow: hidden;
background-color: #fff;
&__picker-view {
height: 100%;
box-sizing: border-box;
&__item {
display: flex;
align-items: center;
justify-content: center;
font-size: 32rpx;
padding: 0 8rpx;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,91 @@
<template>
<view class="notice-warp" :style="{height:noticeWarpH + 'px'}">
<view class="search-box" style="background-color: #fff;">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72" :show-action="false"
@change="search" bg-color="#f0f2f6" shape="square" />
</view>
<CommonTabs class="commonTabs" :list="categoryList" @change="change" :current="current" ref="CommonTabs"
:icon="icon" type="doc" @iconClick="iconClick">
</CommonTabs>
</view>
</template>
<script>
import mixin from "../mixin.js"
import CommonTabs from '@/components/CommonTabs'
export default {
mixins: [mixin],
components: {
CommonTabs
},
data() {
return {
icon: 'icon-ym icon-ym-thumb-mode',
keyword: '',
current: 0,
categoryList: [{
fullName: '我的文档',
}, {
fullName: '我的共享'
}, {
fullName: '共享给我'
}, {
fullName: '回收站'
}],
commonTabs: 0,
noticeWarpH: 0
}
},
mounted() {
this.getContentHeight()
},
methods: {
async getContentHeight() {
const windowHeight = this.$u.sys().windowHeight;
const [commonTabs, searchBox] = await Promise.all([
this.$uGetRect('.search-box'),
this.$uGetRect('.commonTabs')
]);
this.commonTabs = commonTabs.height
this.searchBox = searchBox.height
this.noticeWarpH = this.commonTabs + this.searchBox
// #ifdef MP
this.$emit('mescrollTop', this.commonTabs + 10)
// #endif
// #ifndef MP
this.$emit('mescrollTop', this.noticeWarpH)
// #endif
},
search() {
this.searchTimer && clearTimeout(this.searchTimer);
this.searchTimer = setTimeout(() => {
this.$emit('search', this.keyword)
}, 300);
},
change(e) {
this.current = e;
this.keyword = ''
this.$emit('change', this.current)
},
iconClick(e) {
this.$emit('iconClick')
},
}
}
</script>
<style lang="scss">
.notice-warp {
z-index: 9;
position: fixed;
top: var(--window-top);
left: 0;
width: 100%;
height: 200rpx;
/*对应mescroll-body的top值*/
font-size: 26rpx;
text-align: center;
background-color: #fff;
}
</style>

View File

@@ -0,0 +1,183 @@
<template>
<view class="" v-if="list.length">
<view class="doc-list" v-if="modelValue">
<checkbox-group @change="checkboxChange" @click.stop>
<label class="item-label" v-for="(item,index) in list" :key="index">
<view class="u-flex item-label-left u-line-1" @click.stop="goDetail(item)">
<view class="doc-icon">
<u-image :src="getRecordImg(item.fileExtension)" width="74" height="74" />
</view>
<view class="text">
<p class="u-m-l-10 u-m-b-8 u-font-28 name u-line-1">{{item.fullName}}</p>
<p class="u-m-l-10 u-m-t-8 u-font-24 time">
{{item.time ? jnpf.toDate(item.time, 'yyyy-MM-dd HH:mm:ss') :''}}
</p>
</view>
</view>
<checkbox :value="item.id" :checked="item.checked" activeBackgroundColor="#0177FF" iconColor="#fff"
style="transform:scale(0.7)" />
</label>
</checkbox-group>
</view>
<view class="u-flex u-p-l-20 u-p-r-20 doc-list2 u-p-t-20" v-else>
<checkbox-group @change="checkboxChange" @click.stop class="checkbox-group">
<label class="group-label" v-for="(item,index) in list" :key="index">
<view class="u-flex-col doc-list-inner" @click.stop="goDetail(item)">
<view class="doc-icon u-flex">
<u-image :src="getRecordImg(item.fileExtension)" width="84" height="84" />
</view>
<view class="u-flex doc-name" @click.stop>
<view class="u-line-1 name">{{item.fullName}}</view>
<checkbox :value="item.id" :checked="item.checked" activeBackgroundColor="#0177FF"
iconColor="#fff" style="transform:scale(0.7)" />
</view>
</view>
</label>
</checkbox-group>
</view>
</view>
</template>
<script>
import mixin from "../mixin.js"
export default {
name: 'DocList',
mixins: [mixin],
props: {
modelValue: {
type: Boolean,
default: true
},
documentList: {
type: Array,
default: () => []
}
},
data() {
return {
list: []
}
},
watch: {
documentList: {
handler(val) {
this.list = val
},
immediate: true,
deep: true
}
},
methods: {
goDetail(item) {
this.$emit('goDetail', item)
},
checkboxChange(e) {
this.$emit('checkboxChange', e.detail.value)
}
}
}
</script>
<style lang="scss">
.doc-list {
background-color: #FFFFFF;
position: relative;
width: 100%;
display: flex;
flex-direction: column;
padding: 0 20rpx;
.item-label {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
border-bottom: 1rpx solid #f0f2f6;
height: 126rpx;
.item-label-left {
flex: 1;
.doc-icon {
width: 74rpx;
height: 74rpx;
img {
width: 100%;
height: 100%;
}
}
.text {
width: 80%;
.name {
color: #303133;
}
.time {
color: #909399;
}
}
}
.uni-checkbox-wrapper {
.uni-checkbox-input {
margin: 0 !important;
}
}
}
}
.doc-list2 {
background-color: #fff;
.doc-list-inner {
width: 100%;
background-color: #f2f3f7;
padding: 12rpx 12rpx 0 12rpx;
margin-bottom: 20rpx;
border-radius: 8rpx;
.doc-icon {
width: 100%;
background-color: #FFFFFF;
border-radius: 8rpx;
height: 160rpx;
justify-content: center;
}
.doc-name {
width: 100%;
height: 73rpx;
.name {
text-align: left;
flex: 1;
}
::v-deep .uni-checkbox-wrapper {
.uni-checkbox-input {
margin: 0;
}
}
}
}
}
.checkbox-group {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: space-between;
.group-label {
width: 48%;
}
::v-deep .uni-label-pointer {
width: 48%;
display: flex;
text-align: center;
}
}
</style>

View File

@@ -0,0 +1,28 @@
// 导航栏标题
.title {
height: 90rpx;
padding: 0 32rpx;
line-height: 90rpx;
font-size: 30rpx;
background-color: #f5f5f5;
color: #606064;
// 导航栏图标样式
.iconclass {
display: inline-block;
margin: 0 12rpx;
color: #D0D4DB;
font-size: 28rpx;
}
}
// 导航栏项样式
.inline-item {
display: inline-block
}
// 导航栏项-启用状态
.active {
color: #4297ED !important;
}
// 导航栏项-无状态
.none {
color: #666666;
}

View File

@@ -0,0 +1,168 @@
<template>
<view class="title" style="background-color: #fff;margin-top: 20rpx;">
<scroll-view ref="sea" scroll-x style="width: 100%;white-space: nowrap;">
<!-- 全部 -->
<view class="inline-item" @click="clickItem(null,-1)">
<text v-if="!isre && treeStack.length == 0" class="none">全部</text>
<text v-else class="active">全部</text>
</view>
<!-- 全部 -->
<!-- 搜索结果 -->
<view v-if="isre" @click="clickItem(null,-2)"
:class="activeSearch?'active inline-item':' none inline-item'">
<i class="iconfont icon-z043 iconclass" />
搜索结果
</view>
<!-- 搜索结果 -->
<!-- 当前树的层级值 -->
<view v-for="(item,index) in treeStack" class="inline-item" :key="index">
<view class="inline-item" @click="clickItem(item,index)">
<i class="iconfont icon-z043 iconclass" />
<text v-if="index== treeStack.length-1" class="none inline-item">
{{item[slabel]}}
</text>
<text v-else class="active">
{{item[slabel]}}
</text>
</view>
</view>
<!-- 当前树的层级值 -->
</scroll-view>
</view>
</template>
<script>
/**
* 无限级树-面包屑导航
* @description 无限级树的面包屑导航
* @property {String} slabel 显示的label值
* @return {Function} clickItemitem , index 点击导航栏的索引
* @item 表示导航项对应的值
* @index 表示导航项的层级别索引
* @value -1 全部
* @value -2 表示层级
* @value 其他 从最外层开始依次0,1,2,3……
* @return {Object} outF 导航条内部的方法
* @param {Function} isIre 设置是否搜索状态
* @param {Function} setTreeStack 设置导航树的值
* @param {Function} pushTreeStack 为导航树添加项
* @param {Function} clearTreeStack 清空导航树
*/
// scrollLeft : 暂时
export default {
name: "luyj-tree-navigation",
props: {
// 显示的label值
slabel: {
type: String,
default: 'label'
},
},
data() {
return {
isre: false, // 是否进行了搜索(返回是否进行了搜索)
treeStack: [], // 当前搜索值
}
},
computed: {
// 是否可点击搜索结果
activeSearch() {
return this.treeStack.length > 0;
}
},
created() {
// 浅拷贝导航列表的每一个对象为了不改变item值也不复制过多的数据
this.treeStack.forEach(item => {
let tempItem = Object.assign(item);
this.treeStack.push(tempItem);
});
let obj = {
setIsre: this.setIsre,
getIsre: this.getIsre,
setTreeStack: this.setTreeStack,
concatTreeStack: this.concatTreeStack,
pushTreeStack: this.pushTreeStack,
clearTreeStack: this.clearTreeStack,
getTreeStack: this.getTreeStack
};
this.$emit("inF", obj); // 导出的导航栏调用方法
},
methods: {
// ================================== 初始化时导出方法(用于外部调用内部结果) =========================================================
/** 设置isre值(是否搜索)
* @param {Boolean} isre 设置是否搜索
*/
setIsre(isre) {
this.isre = isre;
},
/**
* 获取isr值获取是否搜索中
*/
getIsre() {
return this.isre;
},
/** 设置导航树
* @param {Array} treeStack 导航树
*/
setTreeStack(treeStack) {
this.treeStack = treeStack;
},
/** 拼接导航树
* @param {Object} treeStack 导航树
*/
concatTreeStack(treeStack) {
this.treeStack = this.treeStack.concat(treeStack);
},
/** 为导航树添加项
* @param {Object} item 待添加的对象
*/
pushTreeStack(item) {
this.treeStack.push(item);
},
/**
* 获取当前导航条
*/
getTreeStack() {
return this.treeStack;
},
/**
* 清空导航树
*/
clearTreeStack() {
this.treeStack.splice(0);
},
// ================================== 监听事件 ===========================================================
/** 点击导航栏索引
* @param {Object} item 当前层的值
* @param {Number} index 索引值
*/
clickItem(item, index) {
if (index == -1) {
// 点击全部
this.isre = false;
this.treeStack.splice(0);
} else if (index == -2) {
// 搜索结果
if (this.activeSearch) {
this.isre = true;
this.treeStack.splice(0);
}
} else {
// 点击某一层级树
this.isre = false;
if (this.treeStack.length - 1 > index) {
this.treeStack.splice(index + 1);
}
}
this.$emit("clickItem", item, index);
},
},
// ============================================================================================================
}
</script>
<style lang="scss" scoped>
@import "navigation.scss";
@import "icon.css";
</style>

View File

@@ -0,0 +1,342 @@
@font-face {
font-family: "iconfont"; /* Project id 2009600 */
src: url('https://at.alicdn.com/t/font_2009600_gpzp7pxtnw.woff2?t=1620633089023') format('woff2'),
url('https://at.alicdn.com/t/font_2009600_gpzp7pxtnw.woff?t=1620633089023') format('woff'),
url('https://at.alicdn.com/t/font_2009600_gpzp7pxtnw.ttf?t=1620633089023') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-banxuanzhongshousuo1-shi:before {
content: "\e682";
}
.icon-xuanzhong3:before {
content: "\e6bb";
}
.icon-weixuanzhong2:before {
content: "\e62e";
}
.icon-danxuanxuanzhong:before {
content: "\e631";
}
.icon-xuanzhong4:before {
content: "\e63e";
}
.icon-xuanzhong1:before {
content: "\e62d";
}
.icon-xuanzhong2:before {
content: "\e656";
}
.icon-selected:before {
content: "\e615";
}
.icon-weixuanzhong1:before {
content: "\e614";
}
.icon-xingzhuang6kaobei3-copy-copy:before {
content: "\e613";
}
.icon-radio-checked:before {
content: "\e63f";
}
.icon-huifu:before {
content: "\e619";
}
.icon-dizhi:before {
content: "\e64a";
}
.icon-kuaijiecaidan:before {
content: "\e60a";
}
.icon-z043:before {
content: "\e62f";
}
.icon-guanbi:before {
content: "\e607";
}
.icon-xuanze:before {
content: "\e623";
}
.icon-caidanzhaolinggan:before {
content: "\e616";
}
.icon-xitongshezhi:before {
content: "\e60c";
}
.icon-xitongshezhi1:before {
content: "\e633";
}
.icon-lunbo:before {
content: "\e692";
}
.icon-shuping:before {
content: "\e659";
}
.icon-tongzhi:before {
content: "\e641";
}
.icon-pinglunguanlishezhi:before {
content: "\e6ac";
}
.icon-icon:before {
content: "\e600";
}
.icon-liuyanguanli:before {
content: "\e61d";
}
.icon-xuanzhong:before {
content: "\e669";
}
.icon--:before {
content: "\e622";
}
.icon-tushu:before {
content: "\e604";
}
.icon-huishouzhan:before {
content: "\e61c";
}
.icon-yonghutouxiang:before {
content: "\e617";
}
.icon-liebiao:before {
content: "\e630";
}
.icon-fenlei:before {
content: "\e621";
}
.icon-tushu1:before {
content: "\e605";
}
.icon-tubiao-:before {
content: "\e620";
}
.icon-weixuanze:before {
content: "\e624";
}
.icon-tushujieyue:before {
content: "\e690";
}
.icon-lunbo1:before {
content: "\e6c5";
}
.icon-shanchu:before {
content: "\e67b";
}
.icon-lunbo2:before {
content: "\e61e";
}
.icon-huaban:before {
content: "\e663";
}
.icon-kehuan:before {
content: "\e608";
}
.icon-icon02:before {
content: "\e601";
}
.icon-huishouzhan1:before {
content: "\e612";
}
.icon-huishouzhan2:before {
content: "\e63d";
}
.icon-sousuo:before {
content: "\e62c";
}
.icon-xingzhuang:before {
content: "\e625";
}
.icon-lunbobankuai:before {
content: "\e61f";
}
.icon-shangchuan:before {
content: "\e602";
}
.icon-yonghu:before {
content: "\e761";
}
.icon-tongzhi1:before {
content: "\e603";
}
.icon-jingsong:before {
content: "\e65c";
}
.icon-fenlei1:before {
content: "\e6c6";
}
.icon-xieshupingicon:before {
content: "\e72d";
}
.icon-liuyan:before {
content: "\e626";
}
.icon-weixuanzhong:before {
content: "\e627";
}
.icon-youxiang:before {
content: "\e646";
}
.icon-lunboguanggao:before {
content: "\e6b3";
}
.icon-xuanze1:before {
content: "\e60d";
}
.icon-chushaixuanxiang:before {
content: "\e606";
}
.icon-liuyanguanli1:before {
content: "\e61a";
}
.icon-shanchu1:before {
content: "\e609";
}
.icon-huishouzhan3:before {
content: "\e642";
}
.icon-shangchuan1:before {
content: "\e823";
}
.icon-huishouzhan4:before {
content: "\e61b";
}
.icon-chuangzuo:before {
content: "\e8ad";
}
.icon-dianzan:before {
content: "\e8ae";
}
.icon-paihangbang:before {
content: "\e8b3";
}
.icon-shouye:before {
content: "\e8b9";
}
.icon-shoucang:before {
content: "\e8c6";
}
.icon-addApp:before {
content: "\e60b";
}
.icon-huishouzhan5:before {
content: "\e63a";
}
.icon-add1:before {
content: "\e60e";
}
.icon-shoucang1:before {
content: "\e60f";
}
.icon-canshutongji:before {
content: "\e618";
}
.icon-rizhiguanli:before {
content: "\e628";
}
.icon-shanchu2:before {
content: "\e629";
}
.icon-xinzeng:before {
content: "\e62a";
}
.icon-zhankailiebiao:before {
content: "\e62b";
}
.icon-xiala-copy:before {
content: "\e610";
}
.icon-shangla:before {
content: "\e64e";
}
.icon-xianxingshezhi:before {
content: "\e611";
}

View File

@@ -0,0 +1,309 @@
<template>
<view class="document-v" :style="{'padding-bottom': show ? '88rpx' : '0','overflow':showAddSelect?'hidden':''}">
<view class="u-flex top-btn" :class="slide2" v-show="show && current !== 1">
<view class="button-left" @click.stop="bottomfun('cancel')">
<p class="u-m-t-10 u-font-28">取消</p>
</view>
<view class="button-center" @click.stop="bottomfun('select')">
<p class="u-m-t-10 u-font-28">已选中{{this.selectFiles.length}}文件</p>
</view>
<view class="button-right u-m-t-12" @click.stop="bottomfun('checkAll')">
<p class="icon-ym icon-ym-app-checkAll " :style="{'color':this.checkedAll ? '#0293fc' : '#303133'}">
</p>
</view>
</view>
<NaviGation @inF="navigationInt" @clickItem="backTree" :slabel="props.label" ref="NaviGation"></NaviGation>
<mescroll-body ref="mescrollRef" @down="downCallback" :down="downOption" :sticky="false" @up="upCallback"
:up="upOption" :bottombar="false" @init="mescrollInit" top="20">
<DocList v-model="changeStyle" :documentList="documentList" @goDetail="goDetail"
@checkboxChange="checkboxChange"></DocList>
</mescroll-body>
<view class="com-addBtn" @click="addFolder()" v-if="!selectFiles.length && current == 0">
<u-icon name="plus" size="48" color="#fff" />
</view>
<view class="u-flex bottom-btn" :class="slide" v-show="show && current !== 1">
<template v-if="current == 0">
<view class="button-preIcon" @click.stop="bottomfun('down')">
<p class="icon-ym icon-ym-app-download u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">下载</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('share')">
<p class="icon-ym icon-ym-app-share u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">共享</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('delete')">
<p class="icon-ym icon-ym-app-delete u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">删除</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('move')">
<p class="icon-ym icon-ym-app-move u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">移动到</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('restName')" v-if="this.selectFiles.length === 1">
<p class="icon-ym icon-ym-app-rename u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">重命名</p>
</view>
</template>
<template v-if="current == 2">
<view class="button-preIcon" @click.stop="bottomfun('down')">
<p class="icon-ym icon-ym-app-download u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">下载</p>
</view>
</template>
<template v-if="current == 1">
<view class="button-preIcon" @click.stop="bottomfun('shareCancel')">
<p class="icon-ym icon-ym-app-share u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">取消共享</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('share')">
<p class="icon-ym icon-ym-app-share u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">共享</p>
</view>
</template>
<template v-if="current == 3">
<view class="button-preIcon" @click.stop="bottomfun('revert')">
<p class="icon-ym icon-ym-recovery u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">还原</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('delete')">
<p class="icon-ym icon-ym-app-delete u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">删除</p>
</view>
</template>
</view>
<!-- 重命名弹窗 -->
<uni-popup ref="inputDialog" type="dialog">
<uni-popup-dialog ref="inputClose" mode="input" :title="modalTitle" v-model="modalValue" placeholder="请输入内容"
before-close @confirm="restName" @close="closeDialog"></uni-popup-dialog>
</uni-popup>
<treeCollapse :show="showApply" v-if="showApply" :treeData="folderTreeList" @change="handelClick" mode="right"
@close="close" width="100%" type="doc">
<view class="u-flex u-p-l-32 u-p-r-32" style="justify-content: space-between;height: 88rpx;">
<text style="color: #2979ff;" @click="close()">取消</text>
<text>移动到</text>
<text style="color: #2979ff;" v-if="selectFiles.length" @click="folderMove">移动到此</text>
</view>
</treeCollapse>
<JnpfUsersSelect ref="JnpfUsersSelect" @change="shareSubmit" v-model="usersSelectValue" :isInput="false" />
<AddFilePopup v-if="showAddFilePopup" :show="showAddSelect" @confirm="addSelect" @close="showAddSelect = false"
title="新建" :confirmBtn="false" @onCallback="onCallback" :parentId="parentId"></AddFilePopup>
</view>
</template>
<script>
import {
getDocumentList,
trash,
shareFolder,
shareTome
} from "@/api/workFlow/document";
import treeCollapse from '@/components/treeCollapse'
import AddFilePopup from './components/AddFilePopup.vue'
import NaviGation from './components/navigation/NaviGation.vue'
import DocList from './components/DocList.vue'
import mixin from "./mixin.js"
export default {
mixins: [mixin],
components: {
NaviGation,
DocList,
treeCollapse,
AddFilePopup,
},
data() {
return {
showAddFilePopup: false,
showModal: false,
show: false,
detailList: [],
keyword: '',
documentList: [],
changeStyle: true,
parentId: 0,
props: {
id: 'id',
label: 'fullName',
children: 'children',
multiple: false,
checkStrictly: false, //不关联
nodes: false, // nodes为false时可以选择任意一级选项nodes为true时只能选择叶子节点
}
}
},
onLoad(e) {
let config = JSON.parse(e.config)
this.config = JSON.parse(JSON.stringify(config))
this.parentId = this.config.id
this.$nextTick(() => {
this.$refs.NaviGation.pushTreeStack(this.config);
})
this.init()
},
watch: {
// 在select弹起的时候重新初始化所有数据
selectFiles: {
handler(val) {
if (!val.length) {
setTimeout(() => {
this.show = false
}, 500)
}
if (val.length === this.documentList.length) {
this.checkedAll = true
} else {
this.checkedAll = false
}
},
immediate: true
},
},
methods: {
init() {
this.isDetail = true
this.current = this.config.current
this.changeStyle = this.config.changeStyle
this.setTitle(this.config.fullName)
this.showAddFilePopup = true
},
navigationInt(e) {
this.pushTreeStack = e.pushTreeStack;
},
backTree(item, index) {
if (index === -1) {
this.isDetail = false
uni.navigateBack()
return
}
if (item.id === this.parentId) return
this.parentId = item.id
this.setTitle(item.fullName)
this.resetList()
},
setTitle(fullName) {
uni.setNavigationBarTitle({
title: fullName
})
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.document-v {
padding-bottom: 88rpx;
height: calc(100vh - 288rpx);
::v-deep .mescroll-empty {
padding-top: 400rpx;
}
::v-deep .u-model__title {
padding: 20rpx 0;
}
::v-deep .u-model__footer__button {
border-right: 1rpx solid #e4e7ed;
height: 76rpx;
line-height: 76rpx;
font-size: 28rpx;
}
.top-btn {
height: 80rpx;
position: fixed;
width: 100%;
top: 0;
left: 0;
background-color: #fff;
z-index: 9999;
justify-content: space-between;
padding: 0 20rpx;
.button-left {
color: #0293fc;
}
.button-right {
width: 30rpx;
height: 30rpx;
}
}
.slide-down2 {
animation: slide-down2 0.5s forwards;
opacity: 1;
transform: translateY(0);
}
.slide-up2 {
animation: slide-up2 0.5s forwards;
opacity: 0;
transform: translateY(-100%);
}
.slide-down {
animation: slide-down 0.5s forwards;
opacity: 1;
transform: translateY(0);
}
.slide-up {
animation: slide-up 0.5s forwards;
opacity: 0;
transform: translateY(100%);
}
.bottom-btn {
height: 100rpx;
position: fixed;
width: 100%;
bottom: 0;
left: 0;
background-color: #0293fc;
z-index: 9;
justify-content: space-around;
.button-preIcon {
color: #fff;
text-align: center;
width: 20%;
}
}
@keyframes slide-up {
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-down {
to {
transform: translateY(100%);
opacity: 0;
}
}
@keyframes slide-up2 {
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-down2 {
to {
transform: translateY(-100%);
opacity: 0;
}
}
.com-addBtn {
bottom: 320rpx;
right: 66rpx;
}
}
</style>

View File

@@ -0,0 +1,298 @@
<template>
<view class="document-v" :style="{'padding-bottom': show ? '88rpx' : '0','overflow':showAddSelect?'hidden':''}">
<view class="u-flex top-btn" :class="slide2" v-show="show">
<view class="button-left" @click.stop="bottomfun('cancel')">
<p class="u-m-t-10 u-font-28">取消</p>
</view>
<view class="button-center" @click.stop="bottomfun('select')">
<p class="u-m-t-10 u-font-28">已选中{{this.selectFiles.length}}文件</p>
</view>
<view class="button-right u-m-t-12" @click.stop="bottomfun('checkAll')">
<p class="icon-ym icon-ym-app-checkAll " :style="{'color':this.checkedAll ? '#0293fc' : '#303133'}">
</p>
</view>
</view>
<DocHead @search="search" @change="change" @iconClick="iconClick" @mescrollTop="mescrollTop">
</DocHead>
<mescroll-body ref="mescrollRef" @down="downCallback" :down="downOption" :sticky="false" @up="upCallback"
:up="upOption" :bottombar="false" @init="mescrollInit" :top="top">
<DocList :modelValue="changeStyle" :documentList="documentList" @goDetail="goDetail"
@checkboxChange="checkboxChange"></DocList>
</mescroll-body>
<view class="com-addBtn" @click="addFolder()" v-if="!selectFiles.length && current == 0">
<u-icon name="plus" size="48" color="#fff" />
</view>
<view class="u-flex bottom-btn" :class="slide" v-show="show">
<template v-if="current == 0">
<!-- #ifdef H5 -->
<view class="button-preIcon" @click.stop="bottomfun('down')">
<p class="icon-ym icon-ym-app-download u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">下载</p>
</view>
<!-- #endif -->
<view class="button-preIcon" @click.stop="bottomfun('share')">
<p class="icon-ym icon-ym-app-share u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">共享</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('delete')">
<p class="icon-ym icon-ym-app-delete u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">删除</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('move')">
<p class="icon-ym icon-ym-app-move u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">移动到</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('restName')" v-if="this.selectFiles.length === 1">
<p class="icon-ym icon-ym-app-rename u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">重命名</p>
</view>
</template>
<!-- #ifdef H5 -->
<template v-if="current == 2">
<view class="button-preIcon" @click.stop="bottomfun('down')">
<p class="icon-ym icon-ym-app-download u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">下载</p>
</view>
</template>
<!-- #endif -->
<template v-if="current == 1">
<view class="button-preIcon" @click.stop="bottomfun('shareCancel')">
<p class="icon-ym icon-ym-app-share u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">取消共享</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('share')" v-if="this.selectFiles.length === 1">
<p class="icon-ym icon-ym-app-share u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">共享</p>
</view>
</template>
<template v-if="current == 3">
<view class="button-preIcon" @click.stop="bottomfun('revert')">
<p class="icon-ym icon-ym-recovery u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">还原</p>
</view>
<view class="button-preIcon" @click.stop="bottomfun('delete')">
<p class="icon-ym icon-ym-app-delete u-m-b-8"></p>
<p class="u-m-t-10 u-font-24">删除</p>
</view>
</template>
</view>
<!-- 重命名弹窗 -->
<uni-popup ref="inputDialog" type="dialog" class="diyPopup">
<uni-popup-dialog ref="inputClose" mode="input" :title="modalTitle" placeholder="请输入内容" before-close
@confirm="restName" @close="closeDialog" class="popup-dialog">
<u-input v-model="modalValue" placeholder="请输入" :auto-height="false" maxlength="99999" height="150"
:clearable="true" :border="true" />
</uni-popup-dialog>
</uni-popup>
<treeCollapse :show="showApply" v-if="showApply" :treeData="folderTreeList" @change="handelClick" mode="right"
@close="close" width="100%" type="doc">
<view class="u-flex u-p-l-32 u-p-r-32" style="justify-content: space-between;height: 88rpx;">
<text style="color: #2979ff;" @click="close()">取消</text>
<text>移动到</text>
<text style="color: #2979ff;" v-if="selectFiles.length" @click="folderMove">移动到此</text>
</view>
</treeCollapse>
<JnpfUsersSelect ref="JnpfUsersSelect" @change="shareSubmit" :isInput="false" v-model="usersSelectValue" />
<AddFilePopup :show="showAddSelect" @confirm="addSelect" @close="showAddSelect = false" title="新建"
:confirmBtn="false" @onCallback="onCallback" :parentId="parentId"></AddFilePopup>
</view>
</template>
<script>
import treeCollapse from '@/components/treeCollapse'
import DocHead from './components/DocHead.vue'
import DocList from './components/DocList.vue'
import AddFilePopup from './components/AddFilePopup.vue'
import mixin from "./mixin.js"
export default {
mixins: [mixin],
components: {
DocHead,
DocList,
AddFilePopup,
treeCollapse
},
data() {
return {
documentList: [],
top: 0
}
},
onLoad(e) {
this.selectFiles = []
this.parentId = e.parentId ? e.parentId : 0
this.current = e.current ? Number(e.current) : 0
},
watch: {
// 在select弹起的时候重新初始化所有数据
selectFiles: {
handler(val) {
if (!val.length) {
setTimeout(() => {
this.show = false
}, 500)
}
if (val.length === this.documentList.length) {
this.checkedAll = true
} else {
this.checkedAll = false
}
},
immediate: true
}
},
methods: {
search(e) {
this.keyword = e
this.documentList = [];
this.mescroll.resetUpScroll();
},
mescrollTop(e) {
this.top = e + 10
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.document-v {
padding-bottom: 88rpx;
height: calc(100vh - 288rpx);
::v-deep .mescroll-empty {
padding-top: 400rpx;
}
::v-deep .u-model__title {
padding: 20rpx 0;
}
::v-deep .u-model__footer__button {
border-right: 1rpx solid #e4e7ed;
height: 76rpx;
line-height: 76rpx;
font-size: 28rpx;
}
.top-btn {
height: 80rpx;
position: fixed;
width: 100%;
top: 0;
left: 0;
background-color: #fff;
/* #ifdef MP-WEIXIN */
z-index: 99;
/* #endif */
/* #ifndef MP-WEIXIN */
z-index: 999;
/* #endif */
justify-content: space-between;
padding: 0 20rpx;
.button-left {
color: #0293fc;
}
.button-right {
width: 30rpx;
height: 30rpx;
}
}
.slide-down2 {
animation: slide-down2 0.5s forwards;
opacity: 1;
transform: translateY(0);
}
.slide-up2 {
animation: slide-up2 0.5s forwards;
opacity: 0;
transform: translateY(-100%);
}
.slide-down {
animation: slide-down 0.5s forwards;
opacity: 1;
transform: translateY(0);
}
.slide-up {
animation: slide-up 0.5s forwards;
opacity: 0;
transform: translateY(100%);
}
.bottom-btn {
height: 100rpx;
position: fixed;
width: 100%;
bottom: 0;
left: 0;
background-color: #0293fc;
z-index: 9;
justify-content: space-around;
.button-preIcon {
color: #fff;
text-align: center;
width: 20%;
}
}
.diyPopup {
.popup-dialog {
.uni-dialog-title {
justify-content: center
}
.uni-dialog-content {
height: 160rpx;
.u-input {
height: 80rpx;
}
}
}
}
@keyframes slide-up {
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-down {
to {
transform: translateY(100%);
opacity: 0;
}
}
@keyframes slide-up2 {
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-down2 {
to {
transform: translateY(-100%);
opacity: 0;
}
}
.com-addBtn {
bottom: 320rpx;
right: 66rpx;
}
}
</style>

View File

@@ -0,0 +1,541 @@
import {
getDocumentList,
packDownload,
resetFileName,
batchDelete,
trash,
recovery,
trashDelete,
folderTree,
folderMove,
addFolder,
shareFolder,
shareFolderList,
shareTome,
shareUser,
shareAdjustment,
cancelShare,
fileDetail
} from "@/api/workFlow/document";
import resources from "@/libs/resources.js";
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
import jnpf from '@/utils/jnpf'
const wordTypeList = ['doc', 'docx'];
const excelTypeList = ['xls', 'xlsx'];
const pptTypeList = ['ppt', 'pptx'];
const pdfTypeList = ['pdf'];
const zipTypeList = ['rar', 'zip', 'arj', 'z', '7z'];
const txtTypeList = ['txt', 'log'];
const codeTypeList = ['html', 'cs', 'xml'];
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif'];
const videoTypeList = ['avi', 'wmv', 'mpg', 'mpeg', 'mov', 'rm', 'ram', 'swf', 'flv', 'mp4', 'mp3', 'wma', 'avi', 'rm',
'rmvb', 'flv', 'mpg', 'mkv'
];
const previewTypeList = [...wordTypeList, ...excelTypeList, ...pptTypeList, ...pdfTypeList];
export default {
mixins: [MescrollMixin],
data() {
return {
usersSelectValue: '',
isDetail: true,
showApply: false,
folderTreeList: [],
modalValue: '',
keyword: '',
current: 0,
show: false,
slide: '',
slide2: '',
changeStyle: true,
checkedAll: false,
parentId: 0,
wordImg: resources.document.wordImg,
excelImg: resources.document.excelImg,
pptImg: resources.document.pptImg,
pdfImg: resources.document.pdfImg,
rarImg: resources.document.rarImg,
txtImg: resources.document.txtImg,
codeImg: resources.document.codeImg,
imageImg: resources.document.imageImg,
audioImg: resources.document.audioImg,
blankImg: resources.document.blankImg,
folderImg: resources.document.folderImg,
downOption: {
use: true,
auto: true,
},
upOption: {
page: {
num: 0,
size: 50,
time: null,
},
empty: {
use: true,
icon: resources.message.nodata,
tip: this.$t('common.noData'),
fixed: false,
top: "560rpx",
},
textNoMore: this.$t('app.apply.noMoreData'),
toTop: {
bottom: 200,
right: 80
}
},
selectFolder: {},
moveId: '',
selectFiles: [],
modalType: 'restName',
showAddSelect: false,
selector: [{
fullName: '新建文件夹',
id: 1,
icon: 'icon-ym icon-ym-add-folder'
},
{
fullName: '上传文件',
id: 2,
icon: 'icon-ym icon-ym-generator-menu'
}
],
isDetail: false
}
},
computed: {
baseURL() {
return this.define.baseURL
},
modalTitle() {
return this.modalType === 'restName' ? '重命名文件' : '新建文件夹'
}
},
methods: {
onCallback(e) {
this.$u.toast(e.msg)
setTimeout(() => {
this.showAddSelect = false
this.resetList()
}, 1000)
},
addSelect(item) {
if (item == 'add') {
this.showAddSelect = false
this.$refs.inputDialog.open('center')
this.modalType = 'addFolder'
}
},
addFolder() {
this.modalValue = ''
this.showAddSelect = true
},
shareSubmit(e) {
let method = this.current === 1 ? shareAdjustment : shareFolder;
let data = {
ids: this.selectFiles,
userIds: e
}
method(data).then(res => {
this.resetList()
})
},
upCallback(page) {
let method;
switch (this.current) {
case 1:
method = shareFolderList
break;
case 3:
method = trash
break;
case 2:
method = shareTome
break;
default:
method = getDocumentList
break;
}
let query = {
keyword: this.keyword,
parentId: this.parentId
};
method(query).then(res => {
this.documentList = [];
this.selectFiles = []
const timeMap = ['creatorTime', 'shareTime', 'shareTime', 'deleteTime'];
const useTime = timeMap[this.current] ?? 'creatorTime'; // 添加默认值
const list = res.data.list.map(o => ({
...o,
time: o[useTime]
}));
this.documentList = this.documentList.concat(list);
this.mescroll.endSuccess(list.length);
})
},
downLoad(id) {
let data = {
ids: id ? id : this.selectFiles
}
packDownload(JSON.stringify(data)).then(res => {
// #ifdef H5
const fileUrl = this.baseURL + res.data.url + '&name=' + encodeURI(res.data.name);
window.location.href = fileUrl;
// #endif
// #ifdef MP-WEIXIN
this.previewFile(res.data)
// #endif
// #ifndef H5 || MP
this.downloadFile(res.data);
// #endif
})
},
handelClick(item) {
this.moveId = item.id == '-1' ? 0 : item.id
},
downloadFile(data) {
const {
url,
name
} = data;
const fileExt = name.split('.').pop().toLowerCase();
const handleDownload = () => {
uni.downloadFile({
url: `${this.baseURL}${url}&name=${name}`,
success: (res) => {
if (res.statusCode !== 200) return this.$u.toast('文件下载失败');
// #ifdef APP-HARMONY
openDocument(res.tempFilePath)
// #endif
// #ifndef APP-HARMONY
if (plus.os.name == 'iOS') {
openDocument(res.tempFilePath)
return
}
/* 安卓 */
uni.saveFile({
tempFilePath: res.tempFilePath,
success: red => {
uni.showToast({
icon: 'none',
mask: true,
title: '文件已保存:' + red.savedFilePath, //保存路径
duration: 3000,
});
},
fail(err) {
console.log(err, 547);
}
});
// #endif
},
fail: (err) => {
this.$u.toast('文件下载失败');
}
})
}
const openDocument = (filePath) => {
uni.openDocument({
filePath,
showMenu: true,
success: () => {},
fail: (err) => {
console.error('打开文档失败:', err);
this.$u.toast('文件打开失败');
}
});
}
handleDownload();
},
previewFile(item) {
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx', 'txt']
let fileType = item.name.split('.')[1]
if (fileTypes.includes(fileType)) {
uni.downloadFile({
url: `${this.baseURL}${ item.url}&name=${item.name}`,
success: (res) => {
let filePath = res.tempFilePath;
uni.openDocument({
filePath,
showMenu: true,
success: (res) => {}
});
}
});
} else {
this.$u.toast('该文件类型无法打开')
}
},
checkboxChange(e) {
if (e.length) {
this.slide = 'slide-up'
this.slide2 = 'slide-up2'
const {
platform
} = uni.getSystemInfoSync()
if (platform === 'ios' && this.current == 2) return this.show = false
this.show = true
} else {
this.slide = 'slide-down'
this.slide2 = 'slide-down2'
}
this.selectOperation(e)
},
change(e) {
this.current = e
this.parentId = 0
this.resetList()
},
bottomfun(type) {
if (type === 'down') this.downLoad()
if (type === 'restName') {
fileDetail(this.selectFiles[0]).then(res => {
this.modalValue = res?.data?.fullName || ''
this.modalType = 'restName'
this.$refs.inputDialog.open()
})
}
if (type === 'checkAll') this.checkedAllFun()
if (type === 'revert') this.recoveryOrDelete(type)
if (type === 'delete') this.recoveryOrDelete(type)
if (type === 'share') {
if (this.current == 1) return this.shareUser()
this.usersSelectValue = ''
this.$nextTick(() => {
this.$refs.JnpfUsersSelect.openSelect()
})
}
if (type === 'shareCancel') return this.cancelShare()
if (type === 'move') this.getFolderTree()
if (type === 'cancel') {
this.selectFiles = []
this.selectOperation(this.selectFiles)
}
},
cancelShare() {
uni.showModal({
title: '提示',
content: '您确定要取消共享, 是否继续?',
success: (res) => {
if (res.confirm) {
cancelShare({
ids: this.selectFiles
}).then(res => {
this.$u.toast(res.msg)
this.resetList()
})
}
}
});
},
shareUser() {
shareUser(this.selectFiles[0]).then(res => {
let list = res.data.list || []
const ids = list.map(item => item.shareUserId);
this.usersSelectValue = ids
this.$nextTick(() => {
this.$refs.JnpfUsersSelect.openSelect()
})
})
},
folderMove() {
let data = {
ids: this.selectFiles,
id: this.moveId
}
folderMove(data).then(res => {
this.selectFiles = []
this.close()
this.resetList()
})
},
iconClick() {
if (this.documentList.length) this.changeStyle = !this.changeStyle
},
close() {
this.showApply = false
},
checkedAllFun() {
this.checkedAll = !this.checkedAll
this.selectFiles = [];
this.documentList.forEach(o => {
if (this.checkedAll) {
this.$set(o, 'checked', true)
this.selectFiles.push(o.id);
} else {
this.$set(o, 'checked', false)
this.selectFiles = [];
}
})
},
goDetail(e) {
if (e.type == 0 && this.current != 3) {
if (!this.isDetail) {
let item = {
current: this.current,
changeStyle: this.changeStyle,
...e
}
this.selectFiles = []
this.selectOperation()
uni.navigateTo({
url: './detail?config=' + JSON.stringify(item),
});
} else {
this.parentId = e.id
this.setTitle(e.fullName)
this.pushTreeStack(e);
this.resetList()
}
} else {
if (imgTypeList.includes(e.fileExtension)) {
const images = jnpf.getAuthImgUrl(e.uploaderUrl, false);
uni.previewImage({
urls: [images],
success: (res) => {},
fail: (err) => {
uni.showToast({
title: '预览图片失败',
icon: 'none'
});
}
});
return
}
if (this.current !== 3) this.downLoad([e.id])
}
},
selectOperation(value) {
let items = this.documentList;
this.selectFiles = value || [];
for (let i = 0, lenI = items.length; i < lenI; ++i) {
const item = items[i]
if (this.selectFiles.includes(item.id)) {
this.$set(item, 'checked', true)
} else {
this.$set(item, 'checked', false)
}
}
},
getRecordImg(ext) {
if (!ext) return this.folderImg;
if (ext) ext = ext.replace('.', '');
if (wordTypeList.includes(ext)) return this.wordImg;
if (excelTypeList.includes(ext)) return this.excelImg;
if (pptTypeList.includes(ext)) return this.pptImg;
if (pdfTypeList.includes(ext)) return this.pdfImg;
if (zipTypeList.includes(ext)) return this.rarImg;
if (txtTypeList.includes(ext)) return this.txtImg;
if (codeTypeList.includes(ext)) return this.codeImg;
if (imgTypeList.includes(ext)) return this.imageImg;
if (videoTypeList.includes(ext)) return this.audioImg;
return this.blankImg;
},
getFolderTree() {
let data = {
ids: this.selectFiles
}
folderTree(data).then(res => {
this.showApply = true
this.folderTreeList = JSON.parse(JSON.stringify(res.data.list)) || []
const loop = (list, parent) => {
list.forEach(o => {
o.icon = 'icon-ym icon-ym-folder';
if (o && o.children && Array.isArray(o.children)) {
loop(o.children, o)
}
})
}
loop(this.folderTreeList)
})
},
recoveryOrDelete(type) {
let data = {
ids: this.selectFiles
}
let content = '确定要还原选中的文件吗'
let method = recovery
if (type !== 'revert') {
content = '删除后,放入回收站!'
method = batchDelete
if (type === 'delete' && this.current == 3) {
content = '删除后数据无法恢复'
method = trashDelete
}
}
uni.showModal({
title: '提示',
content,
success: (res) => {
if (res.confirm) {
this.$nextTick(() => {
method(JSON.stringify(data)).then(res => {
this.$u.toast(res.msg)
setTimeout(() => {
this.$nextTick(() => {
this.documentList = [];
this.selectFiles = []
this.mescroll.resetUpScroll();
})
}, 1000)
})
})
}
}
});
},
/* 新建文件 */
handleAddFolder() {
let item = {
id: '',
parentId: this.parentId,
type: 0,
fullName: this.modalValue
}
addFolder(item).then(res => {
this.modalType = 'restName'
this.closeDialog()
this.resetList()
})
},
/* 重命名 */
closeDialog() {
this.$refs.inputDialog.close()
},
restName(e) {
let txt = this.modalType === 'addFolder' ? '文件夹名称不能为空' : '文件名不能为空'
if (!this.modalValue) return this.$u.toast(txt)
if (this.modalType === 'addFolder') return this.handleAddFolder()
if (this.modalType === 'restName') return this.handleRestName()
},
handleRestName() {
let item = {}
this.documentList.forEach(o => {
if (o.id === this.selectFiles[0]) item = {
id: o.id,
parentId: this.parentId,
type: o.type,
fullName: this.modalValue
}
})
resetFileName(item).then(res => {
this.selectFiles = []
this.closeDialog()
this.resetList()
})
},
resetList() {
this.$nextTick(() => {
this.selectFiles = []
this.documentList = [];
this.mescroll.resetUpScroll();
})
},
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 594 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,292 @@
<template>
<u-popup class="jnpf-tree-select-popup" :maskCloseAble="maskCloseAble" mode="right" :popup="false"
v-model="showPopup" :safeAreaInsetBottom="safeAreaInsetBottom" :z-index="uZIndex" width="100%">
<view class="jnpf-tree-select-body">
<view class="jnpf-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="jnpf-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="jnpf-tree-selected">
<view class="jnpf-tree-selected-head">
<view>{{$t('component.jnpf.common.selected')}}</view>
<view v-if="multiple" class="clear-btn" @click="cleanAll">{{$t('component.jnpf.common.clearAll')}}
</view>
</view>
<view class="jnpf-tree-selected-box">
<scroll-view scroll-y="true" style="max-height: 180rpx;height: 180rpx;">
<view class="jnpf-tree-selected-list">
<view class="u-selectTag u-selectTag-flow" v-for="(list,index) in selectList" :key="index">
<view class="jnpf-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="jnpf-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>
<JnpfEmpty v-if="list.length<1"></JnpfEmpty>
</view>
</scroll-view>
</view>
<!-- 底部按钮 -->
<view class="jnpf-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>
import {
getSystemList
} from '@/api/system'
import {
getFlowSelector
} from '@/api/workFlow/flowEngine'
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 {
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,
systemId: ''
};
},
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)
this.getSystemList()
},
methods: {
init() {
this.upCallback()
this.selectList = JSON.parse(JSON.stringify(this.selectedData)) || []
// #ifdef MP-WEIXIN
this.$nextTick(() => {
this.$refs.tabs.init()
})
// #endif
},
getSystemList() {
getSystemList().then(res => {
this.tabsList.push({
id: 0,
encode: "all",
fullName: "全部流程",
})
this.tabsList.push(...res.data)
})
},
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.systemId = !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,
systemId: this.systemId
}
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>

View File

@@ -0,0 +1,111 @@
<template>
<view class="jnpf-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" :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
},
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
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>
.jnpf-tree-select {
width: 100%;
}
</style>

View File

@@ -0,0 +1,244 @@
<template>
<u-popup class="jnpf-tree-select-popup" mode="right" v-model="showPopup" width="100%" @close="close">
<view class="jnpf-tree-select-body">
<view class="jnpf-tree-select-title">
<text class="icon-ym icon-ym-report-icon-preview-pagePre backIcon" @tap="close()"></text>
<view class="title">选择用户</view>
</view>
<view class="jnpf-tree-select-search">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
@clear="handleSearch" :show-action="false" @change="handleSearch" bg-color="#f0f2f6" shape="square">
</u-search>
</view>
<view class="jnpf-tree-selected">
<view class="jnpf-tree-selected-head">
<view>{{$t('component.jnpf.common.selected')}}({{selectedList.length||0}})</view>
<view v-if="multiple" class="clear-btn" @click="setCheckAll">
{{$t('component.jnpf.common.clearAll')}}
</view>
</view>
<view class="jnpf-tree-selected-box">
<scroll-view scroll-y="true" class="select-list">
<u-tag closeable @close="delSelect(index)" v-for="(item,index) in selectedList" :key="index"
:text="item.orgNameTree" class="u-selectTag" />
</scroll-view>
</view>
</view>
<view class="jnpf-tree-selected-line"></view>
<view class="jnpf-tree-selected-tabs">
<view class="tab-item" :class="{'tab-item-active':activeKey==='user'}">
用户
</view>
</view>
<view class="jnpf-tree-select-tree">
<scroll-view :scroll-y="true" style="height: 100%" :refresher-enabled="false" :refresher-threshold="100"
:scroll-with-animation='true' :refresher-triggered="triggered" @scrolltolower="handleScrollToLower">
<view class="jnpf-selcet-list" v-if="userList.length">
<view class="jnpf-selcet-cell" v-for="item in userList" :key="item.id"
@click.stop="handleUserNodeClick(item)">
<view class="jnpf-selcet-cell-action">
<lyCheckbox :type="multiple ? 'checkbox' : 'radio'"
:checked="selectedIds.includes(item.id)" />
</view>
<u-avatar class="jnpf-selcet-cell-avatar" :src="baseURL+item.headIcon" mode="circle"
size="44" v-if="activeKey==='user'"></u-avatar>
<view class="jnpf-selcet-cell-name">
{{item.orgNameTree}}
</view>
</view>
</view>
<JnpfEmpty class="h-full" v-else />
</scroll-view>
</view>
<!-- 底部按钮 -->
<view class="jnpf-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>
import {
getReceiveUserList
} from '@/api/common'
import lyCheckbox from '@/components/ly-tree/components/ly-checkbox.vue';
import {
useBaseStore
} from '@/store/modules/base'
export default {
props: {
selectedData: {
type: Array,
default: () => []
},
// 通过双向绑定控制组件的弹出与收起
modelValue: {
type: Boolean,
default: false
},
// 弹出的z-index值
zIndex: {
type: [String, Number],
default: 0
},
props: {
type: Object,
default: () => ({
label: 'fullName',
value: 'id',
icon: 'icon',
children: 'children',
isLeaf: 'isLeaf'
})
},
multiple: {
type: Boolean,
default: true
},
hasSys: {
type: Boolean,
default: false
},
type: {
type: [String, Number],
default: 0
}
},
components: {
lyCheckbox
},
data() {
return {
moving: false,
selectedList: [],
selectedIds: [],
keyword: '',
showPopup: false,
lazyOptions: [],
activeKey: 'user',
hasPage: false,
pagination: {
hasPage: 1,
currentPage: 1,
pageSize: 20
},
triggered: false,
finish: false,
list: [],
userList: [],
scrollTop: 0,
currStep: 0,
};
},
watch: {
// 在select弹起的时候重新初始化所有数据
modelValue: {
handler(val) {
this.showPopup = val
this.keyword = ""
if (val) setTimeout(() => this.init(), 10);
},
immediate: true
},
selectedList: {
handler(val) {
this.selectedIds = val.map((o) => o.id);
this.$refs.tree && this.$refs.tree.setCheckedKeys(this.selectedIds)
},
deep: true
},
},
computed: {
baseURL() {
return this.define.baseURL
},
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
},
realProps() {
return {
...defaultProps,
...this.props
}
},
},
methods: {
init() {
this.hasPage = 0
this.currStep = 0
this.activeKey = 'user'
this.resetQuery()
this.$nextTick(() => {
this.triggered = true
})
this.selectedList = JSON.parse(JSON.stringify(this.selectedData))
this.getUserList()
},
resetQuery() {
this.userList = []
this.finish = false
this.pagination.currentPage = 1
},
handleScroll(e) {
this.scrollTop = e.detail.scrollTop
},
goTop() {
this.scrollTop = 0
},
handleScrollToLower() {
if (this.finish || this.activeKey !== 'user') return
this.getUserList()
},
getUserList() {
let data = {
keyword: this.keyword,
...this.pagination,
type: this.type
}
getReceiveUserList(data).then(res => {
const list = (res.data.list || []).map((o) => ({
...o,
id: o.id,
orgNameTree: o.orgNameTree || o.fullName
}));
if (list.length < this.pagination.pageSize) this.finish = true;
this.userList = this.userList.concat(list);
this.pagination.currentPage++
})
},
handleUserNodeClick(data) {
const index = this.selectedList.findIndex((o) => o.id === data.id);
if (index !== -1) return this.selectedList.splice(index, 1);
this.multiple ? this.selectedList.push(data) : (this.selectedList = [data]);
},
delSelect(index) {
this.selectedList.splice(index, 1);
},
setCheckAll() {
this.selectedIds = []
this.selectedList = []
},
handleConfirm() {
this.$emit('confirm', this.selectedList, this.selectedIds);
this.close();
},
close() {
this.$emit('close', false);
},
handleSearch(val) {
this.keyword = val || ""
this.hasPage = !!val
this.currStep = val ? 1 : 0
this.goTop()
if (this.activeKey != 'user') this.activeKey = 'user'
this.$nextTick(() => {
this.resetQuery()
this.$u.debounce(this.getUserList, 300)
})
},
}
};
</script>

View File

@@ -0,0 +1,96 @@
<template>
<view class="jnpf-users-select w-full">
<selectBox v-model="innerValue" :placeholder="placeholder" @openSelect="openSelect" :select-open="selectShow"
:disabled="disabled" v-if="isInput" />
<SelectPopup v-model="selectShow" :selectedData="selectedData" :hasSys="hasSys" @close="handleClose"
@confirm="handleConfirm" :type="type" />
</view>
</template>
<script>
import SelectPopup from './SelectPopup';
import selectBox from '@/components/selectBox'
import {
getSelectedList
} from '@/api/common'
export default {
name: 'jnpf-users-select',
components: {
SelectPopup,
selectBox
},
props: {
modelValue: {
default: ''
},
isInput: {
type: Boolean,
default: true
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
hasSys: {
type: Boolean,
default: false
},
type: {
type: [String, Number],
default: 0
},
},
data() {
return {
selectShow: false,
innerValue: '',
selectedData: [],
multiple: true
}
},
watch: {
modelValue: {
handler() {
this.setDefault()
},
immediate: true
},
},
methods: {
setDefault() {
if (!this.modelValue || !this.modelValue.length) return this.setNullValue();
const ids = this.multiple ? this.modelValue : [this.modelValue];
getSelectedList(ids).then((res) => {
if (!this.modelValue || !this.modelValue.length) return this.setNullValue();
const selectedData = res.data.list || []
this.selectedData = selectedData
this.innerValue = this.selectedData.map(o => o.orgNameTree).join();
});
},
setNullValue() {
this.innerValue = '';
this.selectedData = [];
},
openSelect() {
if (this.disabled) return
this.selectShow = true
},
handleConfirm(selectedData, selectedIds) {
if (!this.multiple) {
this.$emit('update:modelValue', selectedIds[0])
this.$emit('change', selectedIds[0], selectedData[0])
} else {
this.$emit('update:modelValue', selectedIds)
this.$emit('change', selectedIds, selectedData)
}
},
handleClose() {
this.selectShow = false
}
}
}
</script>

View File

@@ -0,0 +1,198 @@
<template>
<view class="jnpf-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/workFlow/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>

View File

@@ -0,0 +1,295 @@
<template>
<view class="jnpf-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>
<JnpfUserSelect v-if="getTypeValue() === 1 " v-model="dataForm.toUserId" multiple
:placeholder="$t('common.chooseText')" :disabled="disabled" @change="toChangeUser" />
<userSelect v-else v-model="dataForm.toUserId" multiple :placeholder="$t('common.chooseText')"
:disabled="disabled" @change="toChangeUser" :type="getTypeValue()" />
</u-form-item>
<u-form-item :label="config.current == 2 ? '代理流程' : '委托流程'">
<FlowSelect v-model="dataForm.flowId" :placeholder="$t('app.my.allFlow')" multiple
@change="onChange" :disabled="disabled" clearable :current="config.current" />
</u-form-item>
<u-form-item label="开始时间" prop='startTime' required>
<JnpfDatePicker 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>
<JnpfDatePicker 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: {},
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'
}]
},
disabled: false,
config: {},
titleList: ['受委托人', '委托人', '代理人', '被代理人'],
type: 0
}
},
computed: {
baseURL() {
return this.define.baseURL
}
},
onLoad(e) {
this.userInfo = uni.getStorageSync('userInfo') || {}
this.sysConfigInfo = uni.getStorageSync('sysConfigInfo') || {}
if (e.data) this.config = JSON.parse(decodeURIComponent(e?.data));
if (this.config) this.init()
},
methods: {
getTypeValue() {
return this.sysConfigInfo[this.type === 0 ? 'delegateScope' : 'proxyScope'];
},
init() {
// 初始化配置
this.type = this.config.type ?? 0 // 统一转为数字类型
this.dataForm.id = this.config.id ?? ''
// 状态处理优化
this.config.status ??= 0
this.disabled = this.config.status === 1
// 系统配置处理(使用可选链操作符增强健壮性)
const {
delegateScope,
proxyScope
} = uni.getStorageSync('sysConfigInfo') || {}
// 类型映射处理确保sysConfigInfo存在
if (this.sysConfigInfo) {
const typeKey = this.type === 0 ? 'delegateScope' : 'proxyScope'
this.type = this.sysConfigInfo[typeKey] ?? 0
}
// 导航栏标题设置
uni.setNavigationBarTitle({
title: this.dataForm.id ? this.$t('common.editText') : this.$t('common.addText')
})
// 验证规则初始化
const setValidationRules = () => {
this.rules.toUserId[0].message = `${this.config.current === 2 ? '代理人' : '受委托人'}不能为空`
if (this.config.current !== 2) {
this.rules.type = [{
required: true,
message: '委托类型不能为空',
trigger: ['change', 'blur'],
type: 'number'
}]
}
}
this.type = this.dataForm.type
// 表单初始化
const initForm = async () => {
if (this.dataForm.id) {
try {
const res = await FlowDelegateInfo(this.dataForm.id)
this.dataForm = {
...res.data,
flowId: res.data?.flowId?.split(',') || []
}
this.type = this.dataForm.type
} catch (error) {
console.error('数据加载失败:', error)
}
}
setValidationRules()
this.$nextTick(() => {
this.$refs.dataForm.setRules(this.rules)
})
}
// 执行初始化
initForm()
this.getTypeValue()
// 统一设置数据类型
this.dataForm.type = Number(this.type) || 0
},
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>

View File

@@ -0,0 +1,340 @@
<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="115">
<view class="flow-list">
<view class="flow-list-box">
<SwipeItem :list="list" :buttons="options" @action="handleClick" ref="swipeItem" :marginB="20">
<template v-slot="{ item }">
<view class="item" :id="'item'+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>
</template>
</SwipeItem>
</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'
import SwipeItem from "@/components/SwipeItem/index"
export default {
components: {
flowlist,
SwipeItem
},
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: '删除',
value: 'delete',
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/workFlow${url}?data=${encodeURIComponent(JSON.stringify(data))}`
})
},
handleClick(data) {
const item = this.list[data.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),
'swipeAction': this.actionItemDisabled(o),
...o
}))
// #endif
// #ifdef APP-HARMONY
const list = res.data.list.map(o =>
Object.assign({}, {
'entrustStatus': this.getEntrustStatus(o),
'swipeAction': this.actionItemDisabled(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/workFlow/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>

View File

@@ -0,0 +1,31 @@
<template>
<view>
<web-view :src="fileUrl"></web-view>
</view>
</template>
<script>
import {
getDownloadUrl
} from '@/api/common'
export default {
data() {
return {
fileUrl: ''
}
},
onLoad(e) {
uni.setNavigationBarTitle({
title: e.name
})
this.getFileUrl(e.fileId)
},
methods: {
getFileUrl(fileId) {
getDownloadUrl('workFlow', fileId).then(res => {
this.fileUrl = this.define.baseURL + res.data.url;
})
}
}
}
</script>

View File

@@ -0,0 +1,988 @@
const emojiTree = [
[{
"url": "100.gif",
"alt": "[微笑]"
},
{
"url": "101.gif",
"alt": "[伤心]"
},
{
"url": "102.gif",
"alt": "[美女]"
},
{
"url": "103.gif",
"alt": "[发呆]"
},
{
"url": "104.gif",
"alt": "[墨镜]"
},
{
"url": "105.gif",
"alt": "[哭]"
},
{
"url": "106.gif",
"alt": "[羞]"
},
{
"url": "107.gif",
"alt": "[哑]"
},
{
"url": "108.gif",
"alt": "[睡]"
},
{
"url": "109.gif",
"alt": "[大哭]"
},
{
"url": "110.gif",
"alt": "[囧]"
},
{
"url": "111.gif",
"alt": "[怒]"
},
{
"url": "112.gif",
"alt": "[调皮]"
},
{
"url": "113.gif",
"alt": "[呲牙]"
},
{
"url": "114.gif",
"alt": "[惊讶]"
},
{
"url": "115.gif",
"alt": "[难过]"
},
{
"url": "116.gif",
"alt": "[酷]"
},
{
"url": "117.gif",
"alt": "[汗]"
},
{
"url": "118.gif",
"alt": "[抓狂]"
},
{
"url": "119.gif",
"alt": "[吐]"
},
{
"url": "120.gif",
"alt": "[笑]"
},
{
"url": "121.gif",
"alt": "[快乐]"
},
{
"url": "122.gif",
"alt": "[疑惑]"
},
{
"url": "123.gif",
"alt": "[傲]"
}
],
[{
"url": "124.gif",
"alt": "[饿]"
},
{
"url": "125.gif",
"alt": "[累]"
},
{
"url": "126.gif",
"alt": "[惊恐]"
},
{
"url": "127.gif",
"alt": "[汗1]"
},
{
"url": "128.gif",
"alt": "[高兴]"
},
{
"url": "129.gif",
"alt": "[闲]"
},
{
"url": "130.gif",
"alt": "[努力]"
},
{
"url": "131.gif",
"alt": "[骂]"
},
{
"url": "132.gif",
"alt": "[疑问]"
},
{
"url": "133.gif",
"alt": "[秘密]"
},
{
"url": "134.gif",
"alt": "[乱]"
},
{
"url": "135.gif",
"alt": "[疯]"
},
{
"url": "136.gif",
"alt": "[哀]"
},
{
"url": "137.gif",
"alt": "[鬼]"
},
{
"url": "138.gif",
"alt": "[打击]"
},
{
"url": "139.gif",
"alt": "[bye]"
},
{
"url": "140.gif",
"alt": "[擦汗]"
},
{
"url": "141.gif",
"alt": "[抠]"
},
{
"url": "142.gif",
"alt": "[鼓掌]"
},
{
"url": "143.gif",
"alt": "[糟糕]"
},
{
"url": "144.gif",
"alt": "[恶搞]"
},
{
"url": "145.gif",
"alt": "[左哼哼]"
},
{
"url": "146.gif",
"alt": "[右哼哼]"
},
{
"url": "147.gif",
"alt": "[哈欠]"
}
],
[{
"url": "148.gif",
"alt": "[看]"
},
{
"url": "149.gif",
"alt": "[委屈]"
},
{
"url": "150.gif",
"alt": "[难过1]"
},
{
"url": "151.gif",
"alt": "[坏]"
},
{
"url": "152.gif",
"alt": "[亲]"
},
{
"url": "153.gif",
"alt": "[吓]"
},
{
"url": "154.gif",
"alt": "[可怜]"
},
{
"url": "155.gif",
"alt": "[刀]"
},
{
"url": "156.gif",
"alt": "[水果]"
},
{
"url": "157.gif",
"alt": "[酒]"
},
{
"url": "158.gif",
"alt": "[篮球]"
},
{
"url": "159.gif",
"alt": "[乒乓]"
},
{
"url": "160.gif",
"alt": "[咖啡]"
},
{
"url": "161.gif",
"alt": "[美食]"
},
{
"url": "162.gif",
"alt": "[动物]"
},
{
"url": "163.gif",
"alt": "[鲜花]"
},
{
"url": "164.gif",
"alt": "[枯]"
},
{
"url": "165.gif",
"alt": "[唇]"
},
{
"url": "166.gif",
"alt": "[爱]"
},
{
"url": "167.gif",
"alt": "[分手]"
},
{
"url": "168.gif",
"alt": "[生日]"
},
{
"url": "169.gif",
"alt": "[电]"
},
{
"url": "170.gif",
"alt": "[炸弹]"
},
{
"url": "171.gif",
"alt": "[刀子]"
}
],
[{
"url": "172.gif",
"alt": "[足球]"
},
{
"url": "173.gif",
"alt": "[瓢虫]"
},
{
"url": "174.gif",
"alt": "[翔]"
},
{
"url": "175.gif",
"alt": "[月亮]"
},
{
"url": "176.gif",
"alt": "[太阳]"
},
{
"url": "177.gif",
"alt": "[礼物]"
},
{
"url": "178.gif",
"alt": "[抱抱]"
},
{
"url": "179.gif",
"alt": "[拇指]"
},
{
"url": "180.gif",
"alt": "[贬低]"
},
{
"url": "181.gif",
"alt": "[握手]"
},
{
"url": "182.gif",
"alt": "[剪刀手]"
},
{
"url": "183.gif",
"alt": "[抱拳]"
},
{
"url": "184.gif",
"alt": "[勾引]"
},
{
"url": "185.gif",
"alt": "[拳头]"
},
{
"url": "186.gif",
"alt": "[小拇指]"
},
{
"url": "187.gif",
"alt": "[拇指八]"
},
{
"url": "188.gif",
"alt": "[食指]"
},
{
"url": "189.gif",
"alt": "[ok]"
},
{
"url": "190.gif",
"alt": "[情侣]"
},
{
"url": "191.gif",
"alt": "[爱心]"
},
{
"url": "192.gif",
"alt": "[蹦哒]"
},
{
"url": "193.gif",
"alt": "[颤抖]"
},
{
"url": "194.gif",
"alt": "[怄气]"
},
{
"url": "195.gif",
"alt": "[跳舞]"
}
],
[{
"url": "196.gif",
"alt": "[拜]"
},
{
"url": "197.gif",
"alt": "[背着]"
},
{
"url": "198.gif",
"alt": "[伸手]"
},
{
"url": "199.gif",
"alt": "[耍帅]"
},
{
"url": "200.png",
"alt": "[微笑1]"
},
{
"url": "201.png",
"alt": "[生病]"
},
{
"url": "202.png",
"alt": "[哭泣]"
},
{
"url": "203.png",
"alt": "[吐舌]"
},
{
"url": "204.png",
"alt": "[迷糊]"
},
{
"url": "205.png",
"alt": "[瞪眼]"
},
{
"url": "206.png",
"alt": "[恐怖]"
},
{
"url": "207.png",
"alt": "[忧愁]"
},
{
"url": "208.png",
"alt": "[眨眉]"
},
{
"url": "209.png",
"alt": "[闭眼]"
},
{
"url": "210.png",
"alt": "[鄙视]"
},
{
"url": "211.png",
"alt": "[阴暗]"
},
{
"url": "212.png",
"alt": "[小鬼]"
},
{
"url": "213.png",
"alt": "[爱心1]"
},
{
"url": "214.png",
"alt": "[拜佛]"
},
{
"url": "215.png",
"alt": "[力量]"
},
{
"url": "216.png",
"alt": "[金钱]"
},
{
"url": "217.png",
"alt": "[蛋糕]"
},
{
"url": "218.png",
"alt": "[彩带]"
},
{
"url": "219.png",
"alt": "[礼物1]"
}
]
]
const emojiList = [{
"url": "100.gif",
"alt": "[微笑]"
},
{
"url": "101.gif",
"alt": "[伤心]"
},
{
"url": "102.gif",
"alt": "[美女]"
},
{
"url": "103.gif",
"alt": "[发呆]"
},
{
"url": "104.gif",
"alt": "[墨镜]"
},
{
"url": "105.gif",
"alt": "[哭]"
},
{
"url": "106.gif",
"alt": "[羞]"
},
{
"url": "107.gif",
"alt": "[哑]"
},
{
"url": "108.gif",
"alt": "[睡]"
},
{
"url": "109.gif",
"alt": "[大哭]"
},
{
"url": "110.gif",
"alt": "[囧]"
},
{
"url": "111.gif",
"alt": "[怒]"
},
{
"url": "112.gif",
"alt": "[调皮]"
},
{
"url": "113.gif",
"alt": "[呲牙]"
},
{
"url": "114.gif",
"alt": "[惊讶]"
},
{
"url": "115.gif",
"alt": "[难过]"
},
{
"url": "116.gif",
"alt": "[酷]"
},
{
"url": "117.gif",
"alt": "[汗]"
},
{
"url": "118.gif",
"alt": "[抓狂]"
},
{
"url": "119.gif",
"alt": "[吐]"
},
{
"url": "120.gif",
"alt": "[笑]"
},
{
"url": "121.gif",
"alt": "[快乐]"
},
{
"url": "122.gif",
"alt": "[疑惑]"
},
{
"url": "123.gif",
"alt": "[傲]"
},
{
"url": "124.gif",
"alt": "[饿]"
},
{
"url": "125.gif",
"alt": "[累]"
},
{
"url": "126.gif",
"alt": "[惊恐]"
},
{
"url": "127.gif",
"alt": "[汗1]"
},
{
"url": "128.gif",
"alt": "[高兴]"
},
{
"url": "129.gif",
"alt": "[闲]"
},
{
"url": "130.gif",
"alt": "[努力]"
},
{
"url": "131.gif",
"alt": "[骂]"
},
{
"url": "132.gif",
"alt": "[疑问]"
},
{
"url": "133.gif",
"alt": "[秘密]"
},
{
"url": "134.gif",
"alt": "[乱]"
},
{
"url": "135.gif",
"alt": "[疯]"
},
{
"url": "136.gif",
"alt": "[哀]"
},
{
"url": "137.gif",
"alt": "[鬼]"
},
{
"url": "138.gif",
"alt": "[打击]"
},
{
"url": "139.gif",
"alt": "[bye]"
},
{
"url": "140.gif",
"alt": "[擦汗]"
},
{
"url": "141.gif",
"alt": "[抠]"
},
{
"url": "142.gif",
"alt": "[鼓掌]"
},
{
"url": "143.gif",
"alt": "[糟糕]"
},
{
"url": "144.gif",
"alt": "[恶搞]"
},
{
"url": "145.gif",
"alt": "[左哼哼]"
},
{
"url": "146.gif",
"alt": "[右哼哼]"
},
{
"url": "147.gif",
"alt": "[哈欠]"
},
{
"url": "148.gif",
"alt": "[看]"
},
{
"url": "149.gif",
"alt": "[委屈]"
},
{
"url": "150.gif",
"alt": "[难过1]"
},
{
"url": "151.gif",
"alt": "[坏]"
},
{
"url": "152.gif",
"alt": "[亲]"
},
{
"url": "153.gif",
"alt": "[吓]"
},
{
"url": "154.gif",
"alt": "[可怜]"
},
{
"url": "155.gif",
"alt": "[刀]"
},
{
"url": "156.gif",
"alt": "[水果]"
},
{
"url": "157.gif",
"alt": "[酒]"
},
{
"url": "158.gif",
"alt": "[篮球]"
},
{
"url": "159.gif",
"alt": "[乒乓]"
},
{
"url": "160.gif",
"alt": "[咖啡]"
},
{
"url": "161.gif",
"alt": "[美食]"
},
{
"url": "162.gif",
"alt": "[动物]"
},
{
"url": "163.gif",
"alt": "[鲜花]"
},
{
"url": "164.gif",
"alt": "[枯]"
},
{
"url": "165.gif",
"alt": "[唇]"
},
{
"url": "166.gif",
"alt": "[爱]"
},
{
"url": "167.gif",
"alt": "[分手]"
},
{
"url": "168.gif",
"alt": "[生日]"
},
{
"url": "169.gif",
"alt": "[电]"
},
{
"url": "170.gif",
"alt": "[炸弹]"
},
{
"url": "171.gif",
"alt": "[刀子]"
},
{
"url": "172.gif",
"alt": "[足球]"
},
{
"url": "173.gif",
"alt": "[瓢虫]"
},
{
"url": "174.gif",
"alt": "[翔]"
},
{
"url": "175.gif",
"alt": "[月亮]"
},
{
"url": "176.gif",
"alt": "[太阳]"
},
{
"url": "177.gif",
"alt": "[礼物]"
},
{
"url": "178.gif",
"alt": "[抱抱]"
},
{
"url": "179.gif",
"alt": "[拇指]"
},
{
"url": "180.gif",
"alt": "[贬低]"
},
{
"url": "181.gif",
"alt": "[握手]"
},
{
"url": "182.gif",
"alt": "[剪刀手]"
},
{
"url": "183.gif",
"alt": "[抱拳]"
},
{
"url": "184.gif",
"alt": "[勾引]"
},
{
"url": "185.gif",
"alt": "[拳头]"
},
{
"url": "186.gif",
"alt": "[小拇指]"
},
{
"url": "187.gif",
"alt": "[拇指八]"
},
{
"url": "188.gif",
"alt": "[食指]"
},
{
"url": "189.gif",
"alt": "[ok]"
},
{
"url": "190.gif",
"alt": "[情侣]"
},
{
"url": "191.gif",
"alt": "[爱心]"
},
{
"url": "192.gif",
"alt": "[蹦哒]"
},
{
"url": "193.gif",
"alt": "[颤抖]"
},
{
"url": "194.gif",
"alt": "[怄气]"
},
{
"url": "195.gif",
"alt": "[跳舞]"
},
{
"url": "196.gif",
"alt": "[拜]"
},
{
"url": "197.gif",
"alt": "[背着]"
},
{
"url": "198.gif",
"alt": "[伸手]"
},
{
"url": "199.gif",
"alt": "[耍帅]"
},
{
"url": "200.png",
"alt": "[微笑1]"
},
{
"url": "201.png",
"alt": "[生病]"
},
{
"url": "202.png",
"alt": "[哭泣]"
},
{
"url": "203.png",
"alt": "[吐舌]"
},
{
"url": "204.png",
"alt": "[迷糊]"
},
{
"url": "205.png",
"alt": "[瞪眼]"
},
{
"url": "206.png",
"alt": "[恐怖]"
},
{
"url": "207.png",
"alt": "[忧愁]"
},
{
"url": "208.png",
"alt": "[眨眉]"
},
{
"url": "209.png",
"alt": "[闭眼]"
},
{
"url": "210.png",
"alt": "[鄙视]"
},
{
"url": "211.png",
"alt": "[阴暗]"
},
{
"url": "212.png",
"alt": "[小鬼]"
},
{
"url": "213.png",
"alt": "[爱心1]"
},
{
"url": "214.png",
"alt": "[拜佛]"
},
{
"url": "215.png",
"alt": "[力量]"
},
{
"url": "216.png",
"alt": "[金钱]"
},
{
"url": "217.png",
"alt": "[蛋糕]"
},
{
"url": "218.png",
"alt": "[彩带]"
},
{
"url": "219.png",
"alt": "[礼物1]"
}
]
const req = import.meta.glob('../static/emoji/*.*', {
eager: true
})
const imagesMap = {}
// 循环所有图片,将图片名设置成键,值为导入该图片的地址
for (const key in req) {
// let name = key.split('/').slice(-1)[0].split('.')[0]
let name = key.split('/').slice(-1)[0].replace('.', '')
// 抛出图片大对象后,文件页面直接引入后将图片的具体名称作为属性就能导入该图片
imagesMap[name] = req[key].default
}
export {
emojiList,
emojiTree,
imagesMap
}

View File

@@ -0,0 +1,525 @@
<template>
<view class="flowBefore-v">
<div class="flow-urgent-value" :style="{'background-color':flowUrgentList[selectflowUrgent.extra].bgColor}"
@click="handleShowSelect">
<span :style="{'color':flowUrgentList[selectflowUrgent.extra].color}">{{selectflowUrgent.label}}</span>
</div>
<view class="flowBefore-box">
<view class="scroll-v" scroll-y>
<childForm ref="child" :config="config" @eventReceiver="eventReceiver" v-if="!loading"
@setBtnLoad="setBtnLoad" :key="childFormKey" />
<ErrorForm @submitErrorForm="submitErrorForm" ref="ErrorForm" />
<RecordTimeList :progressList="progressList" :taskInfo="taskInfo" v-if="getShowExtraPanel"
:commentList="commentList" :taskId="config.opType==0 ? config.id:config.taskId" ref="RecordTimeList"
:hasComment="hasComment">
</RecordTimeList>
</view>
</view>
<u-select :list="flowUrgentList" v-model="showFlowUrgent" @confirm="seltConfirm"
:default-value="defaultValue" />
<uni-popup mode="bottom" ref="reduceApprover" background-color='#fff'>
<view class="approverContent">
<view class="notice-warp">
<view class="u-flex close-icon">
<u-icon name="close" size="32" @click="hideReduceApprover" color="#93969c"></u-icon>
</view>
<view class="search-box">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" @change="getAddSignUserList" bg-color="#f0f2f6" shape="square" />
</view>
</view>
<view class="popup">
<view v-for="(item, index) in signUserIdList" :key="index" v-if="signUserIdList.length"
class="list-box">
<view class="u-flex item">
<view class="u-flex" style="flex: 1;">
<u-avatar :src="define.baseURL+item.headIcon"></u-avatar>
<view class="u-m-l-10">
<view>{{item.fullName}}</view>
<view>{{item.organize}}</view>
</view>
</view>
<view class="" @click="deleteReduce(item.id)">
<u-icon name="trash" size="32" color="#93969c"></u-icon>
</view>
</view>
</view>
<JnpfEmpty v-else />
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import {
Create,
Update
} from '@/api/workFlow/workFlowForm'
import {
FlowTask,
Audit,
Reject,
Transfer,
saveAudit,
saveAssist,
AddSignUserIdList,
ReduceApprover,
Candidates,
RejectList,
FreeApprover,
launchRecall,
auditRecall,
cancel,
SignFor,
Transact,
sendBack,
back,
Assist
} from '@/api/workFlow/flowBefore'
import {
Revoke,
Press
} from '@/api/workFlow/flowLaunch'
import {
createModel,
updateModel
} from '@/api/apply/visualDev'
import {
createComment
} from '@/api/workFlow/flowEngine'
import {
checkInfo
} from '@/api/message.js'
import resources from '@/libs/resources.js'
import childForm from './form.vue'
import RecordTimeList from '../components/RecordTimeList'
import ErrorForm from '../components/ErrorForm'
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
import {
useUserStore
} from '@/store/modules/user'
export default {
mixins: [MescrollMixin],
components: {
childForm,
ErrorForm,
RecordTimeList
},
data() {
return {
childFormKey: +new Date(),
signUserIdList: [],
keyword: '',
formLoding: false,
loading: false,
taskInfo: {},
btnInfo: [],
nodeList: [],
show: false,
config: {},
formData: {},
recordList: [],
properties: {},
flowStatus: '',
commentList: [],
processId: "",
title: '',
selectflowUrgent: {
extra: '0',
label: '普通',
value: 1,
},
showFlowUrgent: false,
defaultValue: [0],
flowUrgent: 1,
flowUrgentList: [{
label: '普通',
color: '#409EFF',
bgColor: '#e5f3fe',
value: 1,
extra: '0'
},
{
label: '重要',
color: '#E6A23C',
bgColor: '#fef6e5',
value: 2,
extra: '1'
},
{
label: '紧急',
color: '#F56C6C',
bgColor: '#fee5e5',
value: 3,
extra: '2'
},
],
hasComment: false,
progressList: []
};
},
computed: {
baseURL() {
return this.define.baseURL
},
getShowExtraPanel() {
return this.config.opType != '-1' && !this.loading
}
},
methods: {
init(data) {
this.config = data
this.config.origin = 'scan'
this.processId = this.config.id
/**
* opType
* -1 - 我发起的新建/编辑
* 0 - 我发起的详情
* 1 - 待办事宜
* 2 - 已办事宜
* 3 - 抄送事宜
*/
this.getBeforeInfo(this.config)
},
handlePreviewImage(url) {
// #ifdef H5
uni.previewImage({
urls: [url],
current: url,
success: () => {},
fail: () => {
uni.showToast({
title: '预览图片失败',
icon: 'none'
});
}
});
// #endif
},
getBeforeInfo() {
let config = this.config;
this.formData.flowId = config.flowId;
this.loading = true;
const query = {
flowId: config.flowId, // 流程大Id
opType: config.opType == 2 ? 3 : config.opType, //特殊:待办点击开始办理需要跳到在办页面
};
if (config.opType != "-1" && config.opType != '0') query.operatorId = config.operatorId;
FlowTask(config?.taskId || config?.id || 0, query).then((res) => {
this.flowInfo = res.data.flowInfo || {};
this.formInfo = res.data.formInfo || {};
this.taskInfo = res.data.taskInfo || {};
this.btnInfo = res.data.btnInfo || [];
this.progressList = res.data.progressList || [];
config.formOperates = res.data.formOperates || [];
config.formType = this.formInfo.type
const fullName =
config.opType == "-1" ?
this.flowInfo.fullName :
this.taskInfo.fullName;
config.fullName = fullName;
this.title = this.flowInfo.fullName;
this.thisStep = this.taskInfo.thisStep || "";
if (config.status !== 0 && config.status !== 3) {
this.title = this.thisStep ?
config.fullName + "/" + this.thisStep :
config.fullName;
}
config.type = this.flowInfo.type;
config.draftData = res.data.draftData || null;
config.formData = res.data.formData || {};
config.formEnCode = this.formInfo.enCode;
this.nodeList = res.data.nodeList || [];
this.properties = res.data.nodeProperties || {};
this.recordList = (res.data.recordList || []).reverse();
config.formConf = this.formInfo.formData;
this.hasComment = this.flowInfo.flowNodes.global.hasComment;
this.loading = false;
this.formLoding = true;
uni.setNavigationBarTitle({
title: this.flowInfo.fullName,
});
if (config.formRecords && config.title) {
uni.setNavigationBarTitle({
title: config.title,
});
}
this.flowUrgent = this.taskInfo.flowUrgent || 1;
const getSelectInfo = () => {
var obj = {
value: this.flowUrgent,
extra: "0",
label: "普通",
};
this.flowUrgentList.forEach((e, i) => {
if (e.value == this.flowUrgent) {
obj.extra = i;
obj.label = e.label;
}
});
return obj;
};
this.selectflowUrgent = getSelectInfo();
if (config.opType != "-1" && config.opType != "3") config.readonly =
true;
config.formOperates = [];
if (config.opType == 0) {
if (this.properties && this.properties && this.properties.formOperates) config
.formOperates = this.properties.formOperates || [];
} else {
config.formOperates = res.data.formOperates || [];
}
setTimeout(() => {
this.$nextTick(() => {
if (!this.$refs.child || !this.$refs.child.$refs.form) {
uni.showToast({
title: "暂无此流程表单",
icon: "none",
complete: () => {
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
});
return;
}
this.$refs.child.$refs.form.init(config)
});
}, 100);
this.config = config;
});
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
height: 100%;
}
.flow-urgent-value {
position: fixed;
top: var(--window-top);
width: 100%;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
height: 60rpx;
font-size: 28rpx;
}
.flowBefore-v {
display: flex;
flex-direction: column;
margin-top: 60rpx;
.workFlowTitle {
width: 100%;
padding: 0 32rpx 32rpx 32rpx;
background-color: #FFFFFF;
font-size: 32rpx;
font-weight: 700;
white-space: pre-wrap;
text-align: left;
}
.flowBefore-box {
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding-bottom: 3.3rem;
.sticky-box {
z-index: 500;
}
}
.swiper-box {
height: 100vh;
}
.swiper-item {
flex: 1;
flex-direction: row;
}
.scroll-v {
flex: 1;
/* #ifndef MP-ALIPAY */
flex-direction: column;
/* #endif */
width: 100%;
height: 100%;
}
.flowStatus {
position: absolute;
top: 90rpx;
right: 0;
border: 0;
margin: 20rpx;
opacity: 0.7;
z-index: 999;
image {
width: 200rpx;
}
}
.discuss_btn {
background-color: #fff;
position: fixed;
bottom: 0;
display: flex;
width: 100%;
// height: 88rpx;
// box-shadow: 0 -2rpx 8rpx #e1e5ec;
z-index: 20;
.custom-style {
background-color: #2979ff;
color: #FFFFFF;
width: 100%;
border-radius: 0 !important;
&::after {
border: none !important;
}
}
.content {
padding: 24rpx;
text-align: center;
.confrim-btn {
display: flex;
flex-direction: row;
.send {
flex: 1;
background-color: #2979ff;
color: #FFFFFF;
}
.cancel {
flex: 1;
}
}
}
}
}
.approverContent {
height: 1000rpx;
overflow-y: scroll;
padding: 0 20rpx;
.notice-warp {
top: 0;
height: 5.6rem;
.close-icon {
height: 60rpx;
padding: 10rpx 20rpx;
justify-content: flex-end;
}
}
.popup {
margin-top: 5.65rem;
.list-box {
.item {
border-bottom: 1rpx solid #f0f0f0;
padding: 20rpx 0;
}
}
}
}
.nodeList-v {
background-color: #fff;
}
.record-v {
padding: 32rpx 32rpx 10rpx;
background-color: #fff;
}
.discuss_box {
.discuss_list {
.time_button {
padding-left: 110rpx;
margin-top: 20rpx;
padding-right: 10rpx;
}
.discuss_txt {
width: 100%;
justify-content: space-between;
.discuss_txt_left {
.uName {
margin-left: 8px;
font-size: 14px;
font-family: PingFang SC;
line-height: 17rpx;
.replyText {
margin-left: 8px;
}
}
}
.del {
color: red;
margin-left: 20px;
}
.reply {
margin-left: 20px;
}
}
.discuss_content {
font-size: 12px;
padding-left: 70rpx;
.img_box {
margin: 40rpx 0;
}
}
}
}
.comment-creator-time {
font-size: 12px;
color: #999999;
font-family: PingFang SC;
line-height: 17rpx;
}
.comment-content-color {
color: #606266;
}
.comment-header-color {
color: #303133;
}
</style>

View File

@@ -0,0 +1,48 @@
<template>
<view>
<template v-if="config.formType == 1">
<dynamicForm ref="form" @eventReceiver="eventReceiver" @setBtnLoad="setBtnLoad" :config="config" />
</template>
<template v-if="config.formType == 2">
<crmOrder ref="form" @eventReceiver="eventReceiver" v-if="config.formEnCode==='crmOrder'"
:config="config" />
<leaveApply ref="form" @eventReceiver="eventReceiver" v-if="config.formEnCode==='leaveApply'"
:config="config" />
<salesOrder ref="form" @eventReceiver="eventReceiver" v-if="config.formEnCode==='salesOrder'"
:config="config" />
<revokeApply ref="form" @eventReceiver="eventReceiver" v-if="config.formEnCode==='revoke'"
:config="config" />
</template>
</view>
</template>
<script>
import dynamicForm from '@/pages/workFlow/workFlowForm/dynamicForm'
import salesOrder from '@/pages/workFlow/workFlowForm/salesOrder'
import leaveApply from '@/pages/workFlow/workFlowForm/leaveApply'
import crmOrder from '@/pages/workFlow/workFlowForm/crmOrder'
import revokeApply from '@/pages/workFlow/workFlowForm/revokeApply'
export default {
components: {
crmOrder,
dynamicForm,
leaveApply,
salesOrder,
revokeApply
},
props: {
config: {
type: Object,
default: () => {}
},
},
methods: {
eventReceiver(formData, eventType) {
this.$emit('eventReceiver', formData, eventType)
},
setBtnLoad(val) {
this.$emit('setBtnLoad', val)
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
<template>
<view class="logError-v u-flex-col">
<view class="error-prompt">
<view class="error-prompt-title u-p-l-20 u-p-r-20">
错误提示
</view>
<view class="u-p-20 error-content">
{{config.errorTip}}
</view>
</view>
<view class="error-prompt">
<view class="error-prompt-title u-p-l-20 u-p-r-20">
错误内容
</view>
<view class="u-p-20 error-content">
{{config.errorData}}
</view>
</view>
</view>
</template>
<script>
export default {
data() {
return {
config: {},
errorData: [],
errorTip: {}
}
},
onLoad(e) {
this.config = JSON.parse(this.jnpf.base64.decode(e.data))
this.errorData = JSON.parse(this.config.errorData)
this.errorTip = JSON.parse(this.config.errorTip)
}
}
</script>
<style lang="scss">
.logError-v {
width: 100vw;
.error-prompt {
.error-prompt-title {
width: 100%;
height: 80rpx;
background-color: #edeff6;
line-height: 80rpx;
word-break: break-word;
}
.error-content {
word-break: break-word;
line-height: 50rpx;
}
}
}
</style>

View File

@@ -0,0 +1,519 @@
<template>
<view class="flowBefore-v">
<div class="flow-urgent-value" :style="{'background-color':flowUrgentList[selectflowUrgent.extra].bgColor}"
@click="handleShowSelect">
<span :style="{'color':flowUrgentList[selectflowUrgent.extra].color}">{{selectflowUrgent.label}}</span>
</div>
<view class="flowBefore-box">
<view class="scroll-v" scroll-y>
<childForm ref="child" :config="config" v-if="!loading" :key="childFormKey" />
<RecordTimeList :progressList="progressList" :taskInfo="taskInfo" v-if="!loading"
:commentList="commentList" :taskId="config.id" ref="RecordTimeList" @handleReply="goWriteComment"
:hasComment="hasComment" :dataLog="dataLog" :opType="config.opType" :formID="config?.formData?.id"
:dataLogList="dataLogList">
</RecordTimeList>
</view>
</view>
<flowBtn :actionList="actionList" :btnInfo="btnInfo" :opType="config.opType" :hideSaveBtn="config.hideSaveBtn"
@handleBtn="handleBtn" :btnLoading="btnLoading" v-if="formLoding" :rightBtnList="rightBtnList"
:saveBtnText="properties.saveBtnText" :hasComment="hasComment"
:hasSignFor="flowInfo?.flowNodes?.global?.hasSignFor">
</flowBtn>
<u-select :list="flowUrgentList" v-model="showFlowUrgent" @confirm="seltConfirm"
:default-value="defaultValue" />
</view>
</template>
<script>
import {
getDelegateUser
} from '@/api/workFlow/entrust.js'
import {
Create,
Update
} from '@/api/workFlow/workFlowForm'
import {
FlowTask,
} from '@/api/workFlow/flowBefore'
import {
Revoke,
Press
} from '@/api/workFlow/flowLaunch'
import {
getOnlineLog
} from '@/api/apply/visualDev'
import {
createComment
} from '@/api/workFlow/flowEngine'
import resources from '@/libs/resources.js'
import childForm from './form.vue'
import flowBtn from '../components/flowBtn'
import RecordTimeList from '../components/RecordTimeList'
import {
useUserStore
} from '@/store/modules/user'
const sysConfigInfo = uni.getStorageSync('sysConfigInfo')
export default {
components: {
childForm,
flowBtn,
RecordTimeList
},
data() {
return {
dataLogList: [],
dataLog: false,
childFormKey: +new Date(),
formLoding: false,
loading: false,
taskInfo: {},
btnInfo: [],
show: false,
config: {},
formData: {},
properties: {},
btnLoading: false,
commentList: [],
title: '',
selectflowUrgent: {
extra: '0',
label: '普通',
value: 1,
},
showFlowUrgent: false,
defaultValue: [0],
flowUrgent: 1,
flowUrgentList: [{
label: '普通',
color: '#409EFF',
bgColor: '#e5f3fe',
value: 1,
extra: '0'
},
{
label: '重要',
color: '#E6A23C',
bgColor: '#fef6e5',
value: 2,
extra: '1'
},
{
label: '紧急',
color: '#F56C6C',
bgColor: '#fee5e5',
value: 3,
extra: '2'
},
],
actionList: [],
hasComment: false,
progressList: [],
rightBtnList: [],
};
},
computed: {
baseURL() {
return this.define.baseURL
},
getShowExtraPanel() {
return this.dataLog && this.config?.formData?.id || (this.config.opType != '-1' && !this.loading)
}
},
onUnload() {
uni.$off('comment')
},
onShow() {
uni.$on('comment', data => {
this.commentList = [];
this.current = 0;
this.addComment(data)
})
},
onLoad(e) {
this.config = JSON.parse(this.jnpf.base64.decode(e.config))
this.$nextTick(() => {
this.getBeforeInfo(this.config)
})
},
methods: {
addComment(query) {
query.taskId = this.config.id
createComment(query).then(res => {
this.$nextTick(() => {
this.$refs.RecordTimeList.change(1)
})
})
},
//流程按钮事件
handleBtn(type, dataForm = {}) {
if ('comment'.includes(type)) this.goWriteComment();
},
goWriteComment(replyId) {
let data = {
taskId: this.config.id
};
if (replyId) data.replyId = replyId;
data = encodeURIComponent(JSON.stringify(data));
uni.navigateTo({
url: '/pages/workFlow/comment/index?data=' + data
})
},
getBeforeInfo() {
let config = this.config;
this.formData.flowId = config.flowId;
this.loading = true;
const query = {
flowId: config.flowId, // 流程大Id
opType: config.opType == 2 ? 3 : config.opType, //特殊:待办点击开始办理需要跳到在办页面
};
if (config.opType != "-1" && config.opType != '0') query.operatorId = config.operatorId;
FlowTask(config?.taskId || config?.id || 0, query).then((res) => {
this.flowInfo = res.data.flowInfo || {};
this.properties = res.data.nodeProperties || {};
this.formInfo = res.data.formInfo || {};
this.taskInfo = res.data.taskInfo || {};
this.btnInfo = res.data.btnInfo || [];
this.progressList = res.data.progressList || [];
config.formOperates = res.data.formOperates || [];
config.formType = this.formInfo.type
const fullName =
config.opType == "-1" ?
this.flowInfo.fullName :
this.taskInfo.fullName;
config.fullName = fullName;
this.title = this.flowInfo.fullName;
this.thisStep = this.taskInfo.thisStep || "";
if (config.status !== 0 && config.status !== 3) {
this.title = this.thisStep ?
config.fullName + "/" + this.thisStep :
config.fullName;
}
config.type = this.flowInfo.type;
config.draftData = res.data.draftData || null;
config.formData = res.data.formData || {};
let dataId = config.formData.id
config.formEnCode = this.formInfo.enCode;
this.recordList = (res.data.recordList || []).reverse();
config.formConf = this.formInfo.formData;
if (config.formConf) {
this.dataLog = JSON.parse(config.formConf).dataLog
if (this.dataLog) this.getOnlineLog(dataId)
}
this.hasComment = this.flowInfo.flowNodes.global.hasComment;
this.formLoding = true;
// 设置表单标题
uni.setNavigationBarTitle({
title: this.config.formEnCode === "revoke" ? `${this.flowInfo.fullName}撤销申请` : this
.flowInfo.fullName,
});
if (config.formRecords && config.title) {
uni.setNavigationBarTitle({
title: config.title,
});
}
this.flowUrgent = this.taskInfo.flowUrgent || 1;
const getSelectInfo = () => {
var obj = {
value: this.flowUrgent,
extra: "0",
label: "普通",
};
this.flowUrgentList.forEach((e, i) => {
if (e.value == this.flowUrgent) {
obj.extra = i;
obj.label = e.label;
}
});
return obj;
};
this.selectflowUrgent = getSelectInfo();
if (config.opType != "-1" && config.opType != "3") config.readonly =
true;
config.formOperates = [];
if (config.opType == 0) {
if (this.properties && this.properties && this.properties.formOperates) config
.formOperates = this.properties.formOperates || [];
} else {
config.formOperates = res.data.formOperates || [];
}
setTimeout(() => {
this.$nextTick(() => {
if (!this.$refs.child || !this.$refs.child.$refs.form) {
uni.showToast({
title: "暂无此流程表单",
icon: "none",
complete: () => {
setTimeout(() => {
uni.navigateBack();
}, 1500);
},
});
return;
}
this.$refs.child.$refs.form.init(config)
});
}, 100);
this.loading = false;
this.config = config;
});
},
//获取修改记录
getOnlineLog(dataId) {
let modelId = this.formInfo.id
getOnlineLog(modelId, dataId).then(res => {
let list = res.data.list || []
//倒序转正
this.dataLogList = [...list].reverse()
})
},
initBtnList() {
const list = [];
const properties = this.properties;
const btnInfo = this.btnInfo;
// 流程审批
if (this.hasComment && this.config.opType != '-1' && this.rightBtnList.length) list.push({
id: 'comment',
text: '评论'
});
this.actionList = list;
},
// 撤销操作
handleShowSelect() {
if (this.config.opType == '-1') this.showFlowUrgent = true
},
seltConfirm(e) {
this.flowUrgent = e[0].value
this.selectflowUrgent = e[0]
this.defaultValue = [this.flowUrgentList.findIndex(item => item.value === e[0].value)] || [0]
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
height: 100%;
}
.flow-urgent-value {
position: fixed;
top: var(--window-top);
width: 100%;
z-index: 99;
display: flex;
align-items: center;
justify-content: center;
height: 60rpx;
font-size: 28rpx;
}
.flowBefore-v {
display: flex;
flex-direction: column;
margin-top: 60rpx;
.workFlowTitle {
width: 100%;
padding: 0 32rpx 32rpx 32rpx;
background-color: #FFFFFF;
font-size: 32rpx;
font-weight: 700;
white-space: pre-wrap;
text-align: left;
}
.flowBefore-box {
height: 100%;
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
padding-bottom: 3.3rem;
.sticky-box {
z-index: 500;
}
}
.swiper-box {
height: 100vh;
}
.swiper-item {
flex: 1;
flex-direction: row;
}
.scroll-v {
flex: 1;
/* #ifndef MP-ALIPAY */
flex-direction: column;
/* #endif */
width: 100%;
height: 100%;
}
.flowStatus {
position: absolute;
top: 90rpx;
right: 0;
border: 0;
margin: 20rpx;
opacity: 0.7;
z-index: 999;
image {
width: 200rpx;
}
}
.discuss_btn {
background-color: #fff;
position: fixed;
bottom: 0;
display: flex;
width: 100%;
// height: 88rpx;
// box-shadow: 0 -2rpx 8rpx #e1e5ec;
z-index: 20;
.custom-style {
background-color: #2979ff;
color: #FFFFFF;
width: 100%;
border-radius: 0 !important;
&::after {
border: none !important;
}
}
.content {
padding: 24rpx;
text-align: center;
.confrim-btn {
display: flex;
flex-direction: row;
.send {
flex: 1;
background-color: #2979ff;
color: #FFFFFF;
}
.cancel {
flex: 1;
}
}
}
}
}
.approverContent {
height: 1000rpx;
overflow-y: scroll;
padding: 0 20rpx;
.notice-warp {
top: 0;
height: 5.6rem;
.close-icon {
height: 60rpx;
padding: 10rpx 20rpx;
justify-content: flex-end;
}
}
.popup {
margin-top: 5.65rem;
.list-box {
.item {
border-bottom: 1rpx solid #f0f0f0;
padding: 20rpx 0;
}
}
}
}
.nodeList-v {
background-color: #fff;
}
.record-v {
padding: 32rpx 32rpx 10rpx;
background-color: #fff;
}
.discuss_box {
.discuss_list {
.time_button {
padding-left: 110rpx;
margin-top: 20rpx;
padding-right: 10rpx;
}
.discuss_txt {
width: 100%;
justify-content: space-between;
.discuss_txt_left {
.uName {
margin-left: 8px;
font-size: 14px;
font-family: PingFang SC;
line-height: 17rpx;
.replyText {
margin-left: 8px;
}
}
}
.del {
color: red;
margin-left: 20px;
}
.reply {
margin-left: 20px;
}
}
.discuss_content {
font-size: 12px;
padding-left: 70rpx;
.img_box {
margin: 40rpx 0;
}
}
}
}
.comment-creator-time {
font-size: 12px;
color: #999999;
font-family: PingFang SC;
line-height: 17rpx;
}
.comment-content-color {
color: #606266;
}
.comment-header-color {
color: #303133;
}
</style>

View File

@@ -0,0 +1,269 @@
/**
* opType
* -1 - 我发起的新建/编辑
* 0 - 我发起的详情
* 1 - 待签事宜
* 2 - 待办事宜
* 3 - 在办事宜
* 4 - 已办事宜
* 5 - 抄送事宜
* 6 - 流程监控
*/
const statusMap = {
1: [{
name: '全部',
status: ''
},
{
name: '协办',
status: '7'
},
{
name: '退回',
status: '5'
},
{
name: '超时',
status: '-2'
}
],
2: [{
name: '全部',
status: ''
},
{
name: '协办',
status: '7'
},
{
name: '退回',
status: '5'
},
{
name: '超时',
status: '-2'
}
],
3: [{
name: '全部',
status: ''
},
{
name: '待提交',
status: '0'
},
{
name: '进行中',
status: '1'
},
{
name: '已完成',
status: '2'
}
],
4: [{
name: '全部',
status: ''
},
{
name: '同意',
status: '1'
},
{
name: '拒绝',
status: '2'
},
{
name: '转审',
status: '3'
},
{
name: '加签',
status: '4'
},
{
name: '退回',
status: '5'
}
],
5: [{
name: '全部',
status: ''
},
{
name: '已读',
status: '1'
},
{
name: '未读',
status: '0'
}
]
};
import {
getOperatorList,
getFlowLaunchList
} from '@/api/workFlow/template'
export default {
data() {
return {
mescrollTop: 206,
statusList: [],
tabsList: [{
fullName: '在办',
category: '2',
key: 2,
enCode: "workFlow.flowDoing"
}, {
fullName: '发起',
category: null,
key: 3,
enCode: "workFlow.flowLaunch"
}, {
fullName: '已办',
category: '3',
key: 4,
enCode: "workFlow.flowDone"
}, {
fullName: '抄送',
category: '4',
key: 5,
enCode: "workFlow.flowCirculate"
}],
current: 0,
subsectionIndex: 0,
status: '',
sysConfigInfo: {},
menuList: []
}
},
watch: {
current: {
handler(val) {
if (this.sysConfigInfo.flowSign == 1 && val == 0) {
this.statusList = []
this.mescrollTop = 206 / 2
return
}
this.statusList = statusMap[this.tabsList[val].key]
this.mescrollTop = 322 / 2
this.category = this.tabsList[this.current].category
},
immediate: true,
deep: true
},
},
onLoad(e) {
this.config = e?.data && JSON.parse(decodeURIComponent(e?.data))
this.sysConfigInfo = uni.getStorageSync('sysConfigInfo') || {}
this.addTabList()
/* 从门户来到工作流 */
if (e?.data) this.fromPortal()
},
methods: {
addTabList() {
const configToCheck = [{
key: 'flowTodo',
tab: {
fullName: '待办',
category: '1',
key: 1,
enCode: "workFlow.flowTodo"
}
},
{
key: 'flowSign',
tab: {
fullName: '待签',
category: '0',
key: 0,
enCode: "workFlow.flowToSign"
}
}
];
configToCheck.forEach(config => {
if (this.sysConfigInfo[config.key] === 1) return this.tabsList.unshift(config.tab);
});
this.menuList = uni.getStorageSync("menuList");
let workFlowList = this.menuList.filter(o => o.enCode === 'workFlow')
if (!workFlowList.length) return
let menuData = workFlowList[0]?.children
this.tabsList = this.tabsList.filter(tab =>
menuData.some(menu => menu.enCode === tab.enCode)
);
},
fromPortal() {
let i = this.tabsList.findIndex(o => o.key === this.config.tabIndex)
let item = this.tabsList[i]
this.current = i;
this.category = item?.category || null
this.resetUpScroll()
},
/* tab1 */
change(index) {
let item = this.tabsList[index]
this.current = index;
this.status = ''
this.keyword = ''
this.subsectionIndex = 0
this.category = item?.category || null
this.resetUpScroll()
},
/* tab2 */
subsection(e) {
let item = this.statusList[e]
this.status = item.status
this.subsectionIndex = e
this.resetUpScroll()
},
resetUpScroll() {
this.$nextTick(() => {
this.list = [];
this.mescroll.resetUpScroll();
})
},
/* 列表数据 */
upCallback(page) {
let methods = this.category ? getOperatorList : getFlowLaunchList;
let query = {
currentPage: page.num,
pageSize: page.size,
keyword: this.keyword,
category: this.tabsList[this.current].category,
status: this.status
}
methods(query, {
load: page.num == 1
}).then(res => {
this.mescroll.endSuccess(res.data.list.length);
if (page.num == 1) this.list = [];
let flowStatus;
const list = res.data.list.map(o => ({
'flowStatus': this.getFlowStatus(o.status),
'opType': this.setOpType(o.status),
'swipeAction': this.swipeAction(o.status),
...o
}))
this.list = this.list.concat(list);
}).catch(() => {
this.mescroll.endErr();
})
},
swipeAction(status) {
let swipeAction = true
if (this.tabsList[this.current].key === 3 && !this.category && (status == '0' || status == '9'))
swipeAction = false
return swipeAction
},
/* 设置opType */
setOpType(status) {
if (this.tabsList[this.current].key == 3) return status == '0' || status == '9' || status == '8' ? '-1' : 0
if (this.tabsList[this.current].key == 0) return 1
if (this.tabsList[this.current].key == 1) return 2
if (this.tabsList[this.current].key == 2) return 3
if (this.tabsList[this.current].key == 4) return 4
if (this.tabsList[this.current].key == 5) return 5
}
}
}

View File

@@ -0,0 +1,180 @@
<template>
<view class="flowLaunch-v">
<view class="flow-list" v-if="list.length > 0">
<view class="flow-list-box">
<SwipeItem :list="list" :buttons="options" @action="handleClick" ref="swipeItem" :marginB="20"
:borderR="10">
<template v-slot="{ item,index }">
<view class="item" @click="goDetail(item)" :id="'item'+index" ref="mydom">
<view class="item-left">
<view class="item-left-top">
<view class='common-lable-entrust' v-if="item.delegateUser">
{{!category ? '委托' : '代理' }}
</view>
<view class='common-lable'
:class="{'urgent-lable':item.flowUrgent==2,'important-lable':item.flowUrgent==3}">
{{getLableValue(item.flowUrgent)}}
</view>
<text class="title u-font-28 u-line-1">{{item.fullName}}</text>
</view>
<text class="title u-line-1 u-font-24">审批节点{{item.currentNodeName}}<text
class="titInner">{{item.thisStep ? item.thisStep : ''}}</text></text>
<text class="time title u-font-24">发起时间<text
class="titInner">{{item.startTime?$u.timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss'):''}}</text></text>
</view>
<view class="item-right">
<image :src="item.flowStatus" mode="widthFix" class="item-right-img">
</image>
</view>
</view>
</template>
</SwipeItem>
</view>
</view>
</view>
</template>
<script>
import {
delFlowLaunch
} from '@/api/workFlow/template'
import SwipeItem from "@/components/SwipeItem/index"
export default {
name: "FlowList",
components: {
SwipeItem
},
props: {
list: {
type: Array,
default: () => []
},
swipeAction: {
type: Boolean,
default: false
},
category: {
type: [String, Number],
default: '0'
},
},
data() {
return {
options: [{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}],
title: '',
};
},
methods: {
open(index) {
// 先将正在被操作的swipeAction标记为打开状态否则由于props的特性限制
// 原本为'false',再次设置为'false'会无效
this.list[index].show = true;
this.list.map((val, idx) => {
if (index != idx) this.list[idx].show = false;
})
},
goDetail(item) {
const config = {
opType: item.opType,
operatorId: item.id,
category: this.category,
...item
}
uni.navigateTo({
url: '/pages/workFlow/flowBefore/index?config=' +
this.jnpf.base64.encode(JSON.stringify(config))
})
},
handleClick(data) {
const {
index
} = data
const item = this.list[index]
if ([1, 2, 3, 5].includes(item.status)) {
this.$u.toast("流程正在审核,请勿删除")
this.list[index].show = false
return
}
delFlowLaunch(item.id).then(res => {
this.$u.toast(res.msg)
this.list.splice(index, 1)
})
},
getLableValue(value) {
var lableValue = ''
switch (value) {
case 1:
lableValue = '普通'
break;
case 2:
lableValue = '重要'
break;
case 3:
lableValue = '紧急'
break;
default:
lableValue = '普通'
break;
}
return lableValue
}
}
};
</script>
<style scoped lang="scss">
.flowLaunch-v {
width: 100%;
.flow-list-box {
width: 95%;
.item-left-top {
display: flex;
width: 100%;
.common-lable {
font-size: 24rpx;
padding: 2rpx 8rpx;
margin-right: 8rpx;
border-radius: 8rpx;
color: #409EFF;
border: 1px solid #409EFF;
background-color: #e5f3fe;
&-entrust {
margin-right: 8rpx;
font-size: 24rpx;
padding: 2rpx 8rpx;
border-radius: 8rpx;
background-color: #dbf1e1;
color: #19be6b;
border: 1px solid #19be6b;
}
}
.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>

View File

@@ -0,0 +1,172 @@
<template>
<view class="flow-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">
</u-search>
</view>
<view class="flow-tabs">
<u-tabs ref="tabs" :list="tabsList" active-color="#0177FF" inactive-color="#303133" font-size="30"
v-model="current" name="fullName" @change="change" height="80" :is-scroll="false"></u-tabs>
</view>
<view class="flow-status-tabs" v-if="statusList.length">
<u-subsection :list="statusList" :current="subsectionIndex" name="name" active-color="#2979FF"
inactive-color="#999999" bg-color="#F2F3F7" font-size="24" :bold="false"
@change="subsection"></u-subsection>
</view>
</view>
<mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption"
:up="upOption" :top="mescrollTop">
<flowlist :list='list' :swipeAction='current != 3' :category='category' />
</mescroll-body>
</view>
</template>
<script>
import resources from '@/libs/resources.js'
import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
import FlowMixin from "./FlowMixin.js";
import flowlist from './flowList.vue'
export default {
components: {
flowlist
},
mixins: [MescrollMixin, FlowMixin],
data() {
return {
activeItemStyle: {
backgroundColor: '#fff'
},
keyword: '',
category: '0',
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: "300rpx",
},
textNoMore: this.$t('app.apply.noMoreData')
},
}
},
onShow() {
uni.$off('operate')
uni.$on('refresh', () => {
this.list = [];
this.mescroll.resetUpScroll();
})
},
onUnload() {
uni.$off('refresh')
},
methods: {
getFlowStatus(status) {
let flowStatus;
//待签收
if (this.category == '0') flowStatus = resources.status.signfor
// 待办,在办
if (this.category == '1' || this.category == '2') {
//流转中
if (status == '1') flowStatus = resources.status.circulation
//已退回
if (status == '5') flowStatus = resources.status.back
//协办
if (status == '7') flowStatus = resources.status.assist
//转审
if (status == '3') flowStatus = resources.status.transfer
//撤回
if (status == '6') flowStatus = resources.status.recall
//撤销中
if (status == '8') flowStatus = resources.status.revoking
//加签
if (status == '2') flowStatus = resources.status.addSign
//指派
if (status == '4') flowStatus = resources.status.assign
//转办
if (status == '9') flowStatus = resources.status.transfer2
}
//发起
if (!this.category) {
//待提交
if (status == '0') flowStatus = resources.status.draft
//进行中
if (status == '1') flowStatus = resources.status.doing
//已通过
if (status == '2') flowStatus = resources.status.adopt
//已拒绝
if (status == '3') flowStatus = resources.status.reject
//已终止
if (status == '4') flowStatus = resources.status.cancel
//已暂停
if (status == '5') flowStatus = resources.status.pause
//撤销中
if (status == '6') flowStatus = resources.status.revoking
//已撤销
if (status == '7') flowStatus = resources.status.revoke
//退回
if (status == '8') flowStatus = resources.status.back
//撤回
if (status == '9') flowStatus = resources.status.recall
}
//已办
if (this.category == '3') {
//转审
if (status == '7') flowStatus = resources.status.transfer
//同意
if (status == '1') flowStatus = resources.status.agree
//拒绝
if (status == '0') flowStatus = resources.status.refuse
//加签
if (status == '5') flowStatus = resources.status.addSign
//退回
if (status == '3') flowStatus = resources.status.return
//转办
if (status == '18') flowStatus = resources.status.transfer2
}
//抄送
if (this.category == '4') {
//进行中
if (status == '1') flowStatus = resources.status.doing
//已通过
if (status == '2') flowStatus = resources.status.adopt
//已拒绝
if (status == '3') flowStatus = resources.status.reject
//已退回
if (status == '8') flowStatus = resources.status.back
}
return flowStatus
},
search() {
// 节流,避免输入过快多次请求
this.searchTimer && clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.list = [];
this.mescroll.resetUpScroll();
}, 300)
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.u-tabs {
padding-bottom: 4rpx;
}
</style>

View File

@@ -0,0 +1,103 @@
<template>
<view class="common-list-v" v-if="show">
<view class="common-list-contain">
<view class="common-list-main u-p-20 u-flex-col">
<view class="common-list-search" style=" ">
<uni-search-bar radius="100" placeholder="请输入" clearButton="always" cancelButton="always"
@cancel="cancel" v-model="searchValue" focus />
</view>
<view class="" style="flex: 1;margin-top: 10rpx; overflow-y: scroll;" v-if="columnList.length">
<view class="u-line-1" style="width: 100%;height:68rpx;line-height: 68rpx;"
v-for="(item,index) in columnList" :key="index" @click.stop="selectConfirm(item)">
{{item.commonWordsText}}
</view>
</view>
<JnpfEmpty v-else />
</view>
</view>
</view>
</template>
<script>
import {
getSelector
} from "@/api/commonWords.js";
export default {
data() {
return {
show: false,
commonWordsList: [],
searchValue: ''
}
},
computed: {
columnList() {
return this.commonWordsList.filter((o) => (o.commonWordsText && o.commonWordsText.match(this.searchValue)))
}
},
methods: {
open() {
this.show = true
this.getCommonList()
},
cancel() {
this.close()
},
close() {
this.searchValue = ""
this.show = false
},
selectConfirm(item) {
this.$emit('confirm', item)
this.close()
},
getCommonList() {
getSelector().then((res) => {
let list = JSON.parse(JSON.stringify(res.data.list)) || []
this.commonWordsList = list
});
}
}
}
</script>
<style lang="scss">
.common-list-v {
z-index: 9000;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100vh !important;
.common-list-contain {
height: 100%;
width: 100%;
.common-list-main {
background: white;
height: 100%;
.common-list-search {
width: 100%;
height: 70rpx;
.uni-searchbar {
width: 100%;
padding: 0;
.uni-searchbar__box {
justify-content: flex-start !important;
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,130 @@
<template>
<view class="opinion">
<view class="opinion-box">
<u-input type="textarea" v-model="handleOpinion" placeholder="请输入" :inputBorder='false' @input="onInput"
class="easyinput" :clearable="true" focus></u-input>
<view class="u-m-t-10 u-flex opinion-inner" v-if="showCommon">
<view class="u-flex opinion-l" v-if="commonList.length">
<text class="u-line-1 common-txt" v-if="commonList[0]"
@click.stop="addTextareaValue(0)">{{commonList[0].commonWordsText}}</text>
<text class="common-txt u-m-l-10 u-line-1" v-if="commonList[1]"
@click.stop="addTextareaValue(1)">{{commonList[1].commonWordsText}}</text>
</view>
<view class="u-flex opinion-r">
<text class="txt" @click.stop="addCommonWords">设为常用语</text>
<view class="icon-box" @click="showCommonList">
<u-icon name="search" color="#565656" size="28"></u-icon>
</view>
</view>
</view>
</view>
</view>
<CommonList ref="CommonList" @confirm="confirmCommonWord"></CommonList>
</template>
<script>
import CommonList from './CommonList'
export default {
emits: ['addCommonWords', 'update:modelValue'],
name: 'handle-opinion',
components: {
CommonList
},
props: {
modelValue: {
type: [String, Number]
},
commonList: {
type: Array,
default: () => []
},
showCommon: {
type: Boolean,
default: true
}
},
watch: {
modelValue: {
handler(val) {
this.handleOpinion = val
},
immediate: true,
deep: true
}
},
data() {
return {
handleOpinion: ''
}
},
methods: {
confirmCommonWord(e) {
this.handleOpinion = e.commonWordsText
this.$emit('update:modelValue', this.handleOpinion)
},
addCommonWords() {
if (!this.handleOpinion) return this.$u.toast('请输入意见');
this.$emit('addCommonWords')
},
onInput(e) {
this.$emit('update:modelValue', e)
},
addTextareaValue(i) {
this.handleOpinion += this.commonList[i].commonWordsText
this.$emit('update:modelValue', this.handleOpinion)
},
showCommonList() {
this.$nextTick(() => {
this.$refs.CommonList.open();
})
}
}
}
</script>
<style lang="scss">
.opinion {
width: 100%;
.opinion-box {
border-radius: 8rpx;
padding: 0 20rpx 10rpx 20rpx;
background: #f5f5f5 !important;
.opinion-inner {
.opinion-l {
max-width: 62%;
.common-txt {
max-width: 240rpx;
display: inline-block;
border: 1rpx dashed #dcdfe6;
padding: 0 10rpx;
background-color: #fff;
color: #303133;
height: 50rpx;
line-height: 50rpx;
}
}
.opinion-r {
flex: 1;
justify-content: flex-end;
.txt {
padding: 0 10rpx;
color: #0177FF;
}
.icon-box {
width: 60rpx;
text-align: center;
margin-left: 12rpx;
}
}
}
}
}
</style>

View File

@@ -0,0 +1,635 @@
<template>
<view class="flow-popup-content">
<u-form ref="dataForm" :model="dataForm" :label-width="200" :errorType="['toast']">
<view class="content">
<u-form-item label="分支选择" prop="branchList" required v-if="isBranch">
<JnpfSelect v-model="dataForm.branchList" @change="branchChange" placeholder="请选择审批分支"
:options="branchList" multiple :props="props" />
</u-form-item>
<u-form-item class="back-item" label="退回节点" v-if="config.type === 'back' && config.backType"
prop="backNodeCode" required>
<view class="u-flex-col back-item-inner">
<JnpfSelect v-model="dataForm.backNodeCode" :options="config.backNodeList" :props="props"
:disabled="config.backNodeCode != 2" />
<view class="u-m-t-20 selectNode u-flex" v-if="config.backType == 3">
<u-radio-group v-model="dataForm.backType">
<u-radio @change="radioChange(item)" v-for="(item, index) in list" :key="index"
:name="item.name" :disabled="item.disabled">
{{ item.fullName }}
</u-radio>
</u-radio-group>
</view>
</view>
</u-form-item>
<u-form-item label="转审给谁" prop="handleIds"
v-if="['transfer'].includes(config.type) && propertiesType !== 'processing'" required>
<JnpfUserSelect v-model=" dataForm.handleIds" />
</u-form-item>
<u-form-item label="转办给谁" prop="handleIds"
v-if="['transfer'].includes(config.type) && propertiesType === 'processing'" required>
<JnpfUserSelect v-model=" dataForm.handleIds" />
</u-form-item>
<u-form-item label="协办给谁" prop="handleVal" v-if="['assist'].includes(config.type)" required>
<JnpfUserSelect v-model=" dataForm.handleVal" multiple @change="changeUserSelect" />
</u-form-item>
<view v-if="config.type === 'freeApprover'">
<u-form-item label="加签人员" prop="addSignUserIdList" required>
<JnpfUserSelect v-model="dataForm.addSignUserIdList" multiple />
</u-form-item>
<u-form-item label="加签类型">
<JnpfSelect :options="typeList" v-model="dataForm.addSignType" @change="freeApproverChange" />
</u-form-item>
<u-form-item label="审批方式">
<JnpfRadio v-model="dataForm.counterSign" :options="options" />
</u-form-item>
<u-form-item label="会签比例" v-if="dataForm.counterSign == 1">
<view class="u-flex-col free-box">
<JnpfSelect :options="ratioList" v-model="dataForm.auditRatio" />
<text class="u-m-l-10 free-box-txt">达到会签比例则通过</text>
</view>
</u-form-item>
</view>
<template v-for="(item, index) in candidateList" :key="index" v-if="showCandidate">
<u-form-item :label="item.nodeName+ '审批人'" :required="!item.selected" :border-bottom="false">
<u-input type="select" v-model="item.fullName" placeholder="请选择审批候选人" input-align="right"
@click="openSelect(item)" />
</u-form-item>
<u-form-item label="已选审批人" v-if="item.selected">
<u-input type="textarea" v-model="item.selected" class="textarea" border disabled
placeholder="" />
</u-form-item>
</template>
<u-form-item v-if="showOpinion" :label="opinionTitle">
<HandleOpinion :commonList="commonList" v-model="dataForm.handleOpinion"
@addCommonWords="addCommonWords" :showCommon="false"></HandleOpinion>
</u-form-item>
<u-form-item prop="handleOpinion" required label-position="top" :label="opinionTitle"
v-if="showApproval">
<HandleOpinion :commonList="commonList" v-model="dataForm.handleOpinion"
@addCommonWords="addCommonWords"></HandleOpinion>
</u-form-item>
<template v-if="config.approvalField.length">
<u-form-item label-position="left" :label="item.fieldName"
v-for="(item,index) in config.approvalField" :key="index">
<JnpfInput v-if="item.jnpfKey=='input'" v-model="item.value" />
<JnpfTextarea v-if="item.jnpfKey=='textarea'" v-model="item.value" />
<JnpfInputNumber v-if="item.jnpfKey=='inputNumber'" v-model="item.value" />
</u-form-item>
</template>
<u-form-item :prop="signRule ? 'signImg' : '' " :required="signRule" v-if="config.hasSign">
<JnpfSign v-model="dataForm.signImg" signType="ApprovalSign" />
</u-form-item>
<!-- #ifndef APP-HARMONY -->
<u-form-item v-if="config.hasFile">
<view class="uploadFile">
<JnpfUploadFile v-model="dataForm.fileList" :limit="3" align="left" />
</view>
</u-form-item>
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<u-form-item v-if="config.hasFile">
<view class="uploadFile">
<JnpfUploadFileH v-model="dataForm.fileList" :limit="3" align="left" />
</view>
</u-form-item>
<!-- #endif -->
<u-form-item label="抄送人员" v-if="showCustomCopy">
<JnpfUserSelect v-model="copyIds" multiple />
</u-form-item>
</view>
</u-form>
<view class="flowBefore-actions">
<CustomButton class="u-flex buttom-btn-left-inner" :btnText="$t('common.cancelText')"
btnIcon="icon-ym icon-ym-add-cancel" customIcon />
<u-button class="buttom-btn" type="primary" @click="confirm('confirm')">{{$t('common.okText')}}
</u-button>
</view>
</view>
</template>
<script>
import CustomButton from '@/components/CustomButton'
import HandleOpinion from './components/HandleOpinion.vue'
import {
getSelector,
Create
} from '@/api/commonWords'
export default {
components: {
HandleOpinion,
CustomButton
},
data() {
return {
copyIds: [],
selectList: [],
candidateList: [],
commonList: [],
title: '',
label: '',
name: {
'reject': '拒绝',
'launchRecall': '撤回',
'auditRecall': '撤回',
'audit': '同意',
'back': '退回',
'freeApprover': '加签',
'transfer': '转审'
},
ratioList: [{
fullName: '10%',
id: 10
}, {
fullName: '20%',
id: 20
}, {
fullName: '30%',
id: 30
}, {
fullName: '40%',
id: 40
}, {
fullName: '50%',
id: 50
}, {
fullName: '60%',
id: 60
}, {
fullName: '70%',
id: 70
}, {
fullName: '80%',
id: 80
}, {
fullName: '90%',
id: 90
}, {
fullName: '100%',
id: 100
}],
typeList: [{
fullName: '审批前',
id: 1
}, {
fullName: '审批后',
id: 2
}],
options: [{
fullName: '或签',
id: 0
}, {
fullName: '会签',
id: 1
},
{
fullName: "依次审批",
id: 2,
}
],
list: [{
fullName: "重新审批",
disabled: false,
name: 1,
},
{
fullName: "直接提交给我",
disabled: false,
name: 2,
},
],
props: {
label: 'nodeName',
value: 'nodeCode'
},
showCommonWords: false,
dataForm: {
auditRatio: 100,
counterSign: 0,
addSignType: 1,
handleIds: '',
handleVal: [],
addSignUserIdList: '',
fileList: [],
handleOpinion: "",
signImg: "",
copyIds: "",
branchList: [],
candidateList: {},
backNodeCode: "",
backType: 1
},
config: {},
show: false,
selectVal: {},
isCandidates: false,
rules: {
branchList: [{
required: true,
message: '请选择分支',
type: 'array',
trigger: 'blur,change'
}],
backNodeCode: [{
required: true,
message: '请选择退回节点',
trigger: 'blur,change'
}],
signImg: [{
required: true,
message: '请签名',
trigger: 'blur,change'
}],
addSignUserIdList: [{
required: true,
message: '请选择加签人员',
type: 'array',
trigger: 'blur,change'
}],
handleIds: [{
required: true,
message: '请选择人员',
trigger: 'blur,change'
}],
handleVal: [{
required: true,
message: '请选择协办人员',
trigger: 'blur,change',
type: 'array'
}],
handleOpinion: [{
required: true,
message: '请输入意见',
trigger: 'blur,change'
}]
},
isCandidate: false,
propertiesType: ''
};
},
computed: {
isBranch() {
let show = false
if (this.candidateType !== 3) show = true;
this.branchList = this.config.branchList || [];
if (!this.branchList.length) show = false;
if (this.config.type === 'freeApprover' && this.dataForm.addSignType === 1) show = false;
return show
},
showApproval() {
return ['audit', 'reject', 'approvalButton'].includes(this.config.type)
},
showCustomCopy() {
return this.config.isCustomCopy && ['audit', 'reject'].includes(this.config.type)
},
showCandidate() {
return !['transfer', 'revoke', 'recall', 'back', 'assist'].includes(this.config.type) && this.isCandidate
},
showOpinion() {
const list = ['transfer', 'assist', 'revoke', 'auditRecall', 'launchRecall', 'back', 'freeApprover']
return list.includes(this.config.type)
},
opinionTitle() {
const typeMap = {
'transfer': '转审原因',
'revoke': '撤销原因',
'assist': '协办原因',
'back': '退回意见',
'freeApprover': '加签意见',
'launchRecall': '撤回原因',
'auditRecall': '撤回原因',
'audit': '审批意见',
'reject': '审批意见'
};
const specialTypeMap = {
'transfer': {
label: '转办原因'
},
'audit': {
label: '办理意见'
}
};
const setFormRules = (rules) => {
this.$nextTick(() => {
this.$refs.dataForm.setRules(rules);
});
};
const type = this.config.type;
let resultLabel = typeMap[type] || '';
if (specialTypeMap[type] && this.propertiesType === 'processing') resultLabel = specialTypeMap[type].label;
return resultLabel;
},
fileLabel() {
const type = this.config.type;
const typeMap = {
'auditRecall': '撤回附件',
'freeApprover': '加签附件',
'back': '退回附件',
'transfer': '转审附件',
'audit': '审批附件',
'reject': '审批附件'
};
if (type === 'transfer' && this.propertiesType === 'processing') return '转办附件';
if (type === 'audit' && this.propertiesType === 'processing') return '办理附件';
const result = typeMap[type] || '';
return result;
},
signRule() {
return this.config.hasSign && (['audit', 'reject'].includes(this.config.type))
}
},
onLoad(data) {
try {
this.config = JSON.parse(decodeURIComponent(data.config));
} catch {
this.config = JSON.parse(data.config);
}
uni.$on("confirm", (data, nodeCode) => {
this.selectConfirm(data, nodeCode);
});
this.init()
},
onReady() {
this.$refs.dataForm.setRules(this.rules);
},
methods: {
init() {
this.candidateType = this.config.candidateType; /* 1==分支 2==候选人 3==直接通过*/
this.candidateList = this.config.candidateList || [];
this.propertiesType = this.config?.propertiesType
this.getSelector()
this.handleLabel()
this.config.candidateList.map(o => {
this.isCandidates = o.isCandidates
})
this.copyIds = this.config?.circulateUser || ''
this.dataForm.backNodeCode = this.config?.backNodeList?.length ? this.config.backNodeList[0].nodeCode : '';
if (this.candidateType != 3) this.isCandidate = true;
this.branchList = this.config.branchList || [];
if (this.candidateType == 1) {
let list = [];
this.isCandidate = false;
const defaultList = this.candidateList;
for (let i = 0; i < this.dataForm.branchList.length; i++) {
inner: for (let j = 0; j < this.branchList.length; j++) {
let o = this.branchList[j];
if (this.dataForm.branchList[i] === o.nodeCode && o.isCandidates) {
this.isCandidate = true;
list.push({
...o,
label: o.nodeName + "审批人",
});
break inner;
}
}
this.candidateList = [...defaultList, ...list];
}
}
this.userInfo = uni.getStorageSync("userInfo") || {};
this.dataForm.signImg = this.userInfo.signImg;
if (this.config.type === 'freeApprover' && this.dataForm.addSignType == '1') this.isCandidates = false
},
handleLabel() {
const config = this.config;
const {
type,
propertiesType
} = config;
const typeMapping = {
'transfer': {
title: propertiesType === 'processing' ? '转办' : '转审',
label: '转审'
},
'assist': {
title: '协办',
label: '协办'
},
'revoke': {
title: '撤销流程',
label: '撤销'
},
'launchRecall': {
title: '撤回流程',
label: '撤回'
},
'reject': {
title: '审批拒绝',
label: '拒绝'
},
'audit': (propertiesType) => ({
title: propertiesType === 'processing' ? '办理' : '审批',
label: '审批'
}),
'auditRecall': {
title: '撤回审核',
label: '撤回'
},
'freeApprover': {
title: '加签',
label: '加签'
},
'back': {
title: '退回',
label: '退回'
},
'submit': {
title: '提交审核',
label: '提交审核'
}
};
const getTitleAndLabel = (typeKey, propertiesType) => {
const mapping = typeMapping[typeKey];
if (typeof mapping === 'function') return mapping(propertiesType);
return mapping || {
title: '',
label: ''
};
};
const {
title,
label
} = getTitleAndLabel(type, propertiesType);
this.title = title;
this.label = label;
uni.setNavigationBarTitle({
title: this.title
});
},
changeUserSelect(e) {
this.dataForm.handleIds = e.join()
},
branchChange(e, list) {
this.dataForm.branchList = e;
this.candidateList = []
this.init();
},
openSelect(item) {
item.formData = this.config.formData;
item.taskId = this.config.operatorId;
item.selectList = item.selectList || [];
item.candidateList = JSON.stringify(this.candidateList);
item.delegateUser = this.config.delegateUser
uni.navigateTo({
url: "/pages/workFlow/candiDateUserSelect/index?data=" +
encodeURIComponent(JSON.stringify(item)),
});
},
selectConfirm(data, nodeCode) {
let users = [];
const item = this.candidateList.filter(o => o.nodeCode == nodeCode)[0] || {}
item.value = data.map(o => o.id) || []
item.fullName = (data.map(o => o.fullName) || []).join(',') || ''
item.selectList = data || []
for (let i = 0; i < this.candidateList.length; i++) {
for (let j = 0; j < data.length; j++) {
if (data[j].nodeCode === this.candidateList[i].nodeCode) {
users.push(data[j].id);
}
}
}
this.$set(this.dataForm.candidateList, nodeCode, users);
},
getSelector() {
getSelector().then(res => {
this.commonList = res.data.list || []
})
},
confirmCommonWord(e) {
this.dataForm.handleOpinion = e.commonWordsText
},
handlePress(e) {
this.$emit('handlePress')
},
addCommonWords() {
let data = {
commonWordsText: this.dataForm.handleOpinion,
commonWordsType: 1
}
Create(data).then(res => {
this.$u.toast(res.msg);
})
},
freeApproverChange(e) {
this.isCandidates = false;
if (this.config.hasFreeApprover && e == 2 && this.candidateList.length) this.isCandidates = true;
},
confirm() {
if (this.config.type === 'freeApprover') {
this.dataForm.addSignParameter = {
'addSignUserIdList': this.dataForm.addSignUserIdList,
'auditRatio': this.dataForm.auditRatio,
'counterSign': this.dataForm.counterSign,
'addSignType': this.dataForm.addSignType
}
}
if (!this.config.hasSign) delete this.dataForm.signImg
this.dataForm.copyIds = Array.isArray(this.copyIds) && this.copyIds.length && this.copyIds.join()
if (this.config.backType !== 3) this.dataForm.backType = this.config.backType
let data = {
...this.dataForm,
eventType: ['auditRecall', 'launchRecall'].includes(this.config.type) ? 'recall' : this.config
.type,
approvalField: this.config.approvalField
}
if (this.isCandidates || this.isCandidate) {
let candidateList = {};
for (let i = 0; i < this.candidateList.length; i++) {
let item = this.candidateList[i]
if (!item.selected && !item.value?.length) return this.$u.toast('候选人不能为空')
candidateList[item.nodeCode] = item.value || [];
}
data.candidateList = candidateList;
}
this.$refs.dataForm.validate(valid => {
if (valid) {
uni.$emit('operate', data)
setTimeout(() => {
uni.navigateBack()
}, 500)
}
});
}
}
}
</script>
<style lang="scss">
page {
height: 100%;
background-color: #fff !important;
}
::v-deep .u-form-item--left {
align-items: flex-start !important;
}
::v-deep .u-form-item {
line-height: 1.5rem !important;
}
.textarea {
background-color: #f5f5f5;
}
::v-deep .u-input--border {
border: 1rpx solid #f5f5f5 !important;
}
.buttom-btn-left-inner {
width: 50% !important;
}
.free-box {
width: 100%;
.free-box-txt {
text-align: end;
}
}
.flow-popup-content {
padding-bottom: 88rpx;
::v-deep .u-form {
.content {
.u-form-item {
.u-form-item__body {
.u-form-item--left {
// align-items: center !important;
}
}
}
}
}
.signature-box {
border-top: none;
}
.content {
padding: 0 20rpx;
.back-item {
.back-item-inner {
justify-content: end;
width: 100%;
.selectNode {
width: 100%;
justify-content: flex-end;
}
}
}
.head-title {
height: 80rpx;
justify-content: space-between;
color: #333333;
}
.uploadFile {
width: 100%;
padding-bottom: 8rpx;
border-top: 1rpx solid #fbfbfc;
}
}
}
</style>

View File

@@ -0,0 +1,84 @@
<template>
<view class="flow-popup-content">
<u-form ref="dataForm" :model="dataForm" :label-width="150" :errorType="['toast']">
<view class="content">
<u-form-item label="撤销原因">
<HandleOpinion v-model="dataForm.handleOpinion" :showCommon="false"></HandleOpinion>
</u-form-item>
</view>
</u-form>
<view class="flowBefore-actions">
<view class="u-flex-col buttom-btn-left-inner" @click.stop="jnpf.goBack()">
<u-icon name="icon-ym" size="24" custom-prefix="icon-ym icon-ym-add-cancel"></u-icon>
<text>取消</text>
</view>
<u-button class="buttom-btn" type="primary" @click="confirm('confirm')">确定
</u-button>
</view>
</view>
</template>
<script>
import HandleOpinion from './components/HandleOpinion.vue'
import {
getSelector,
Create
} from '@/api/commonWords'
export default {
components: {
HandleOpinion
},
data() {
return {
dataForm: {
handleOpinion: ""
},
config: {}
};
},
onLoad(data) {
try {
this.config = JSON.parse(decodeURIComponent(data.config));
} catch {
this.config = JSON.parse(data.config);
}
},
methods: {
confirm() {
let data = {
...this.dataForm,
eventType: this.config.type
}
uni.$emit('operate', data)
setTimeout(() => {
uni.navigateBack()
}, 500)
}
}
}
</script>
<style lang="scss">
page {
height: 100%;
background-color: #fff !important;
}
::v-deep .u-form-item--left {
align-items: flex-start !important;
}
.buttom-btn-left-inner {
width: 50%;
}
.flow-popup-content {
.signature-box {
border-top: none;
}
.content {
padding: 0 20rpx;
}
}
</style>

View File

@@ -0,0 +1,76 @@
<template>
<view class="dynamicModel-v">
<view class="jnpf-wrap jnpf-wrap-form" v-if="isShow">
<childForm ref="child" :config="config" />
</view>
</view>
</template>
<script>
import childForm from '@/pages/workFlow/flowBefore/form'
import {
getConfigData
} from '@/api/apply/visualDev'
import {
getStartFormInfo
} from '@/api/workFlow/workFlowForm'
export default {
name: 'scanForm',
components: {
childForm
},
data() {
return {
config: {},
formConf: {},
isShow: false,
formInfo: {},
}
},
onLoad(data) {
let obj = JSON.parse(data.config)
this.initData(obj)
},
methods: {
initData(data) {
if (data.previewType === 'initiationForm') {
uni.setNavigationBarTitle({
title: data.title
})
getStartFormInfo(data.taskId).then(res => {
this.formInfo = res.data.formInfo;
this.config = {
...data,
formData: res.data.formData || {},
formConf: this.formInfo.formData,
readonly: true,
formType: 1
}
this.isShow = true
})
} else {
getConfigData(data.id, {
type: data.previewType
}).then(res => {
if (!res.data || !res.data.formData) return
this.config = {
formEnCode: data.enCode,
flowId: res.data.id,
formConf: res.data.formData
}
this.isShow = true
})
}
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.dynamicModel-v {
height: 100%;
}
</style>

View File

@@ -0,0 +1,546 @@
/**
* @1900-2100区间内的公历、农历互转
* @charset UTF-8
* @github https://github.com/jjonline/calendar.js
* @Author Jea杨(JJonline@JJonline.Cn)
* @Time 2014-7-21
* @Time 2016-8-13 Fixed 2033hex、Attribution Annals
* @Time 2016-9-25 Fixed lunar LeapMonth Param Bug
* @Time 2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
* @Version 1.0.3
* @公历转农历calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
* @农历转公历calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
*/
/* eslint-disable */
var calendar = {
/**
* 农历1900-2100的润大小信息表
* @Array Of Property
* @return Hex
*/
lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
/** Add By JJonline@JJonline.Cn**/
0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
0x0d520], // 2100
/**
* 公历每个月份的天数普通表
* @Array Of Property
* @return Number
*/
solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
/**
* 天干地支之天干速查表
* @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
* @return Cn string
*/
Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
/**
* 天干地支之地支速查表
* @Array Of Property
* @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
* @return Cn string
*/
Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
/**
* 天干地支之地支速查表<=>生肖
* @Array Of Property
* @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
* @return Cn string
*/
Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
/**
* 24节气速查表
* @Array Of Property
* @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
* @return Cn string
*/
solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
/**
* 1900-2100各年的24节气日期速查表
* @Array Of Property
* @return 0x string For splice
*/
sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
'97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
'97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
'97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
'97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
'9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
'97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
'7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
'97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
'97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
'9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
'97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
'977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
'7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
'977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
'977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
'7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
'7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
'7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
'7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
'7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
'7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
'7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
'665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
'7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
'7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
'7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
/**
* 数字转中文速查表
* @Array Of Property
* @trans ['日','一','二','三','四','五','六','七','八','九','十']
* @return Cn string
*/
nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
/**
* 日期转农历称呼速查表
* @Array Of Property
* @trans ['初','十','廿','卅']
* @return Cn string
*/
nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
/**
* 月份转农历称呼速查表
* @Array Of Property
* @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
* @return Cn string
*/
nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
/**
* 返回农历y年一整年的总天数
* @param lunar Year
* @return Number
* @eg:var count = calendar.lYearDays(1987) ;//count=387
*/
lYearDays: function (y) {
var i; var sum = 348
for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
return (sum + this.leapDays(y))
},
/**
* 返回农历y年闰月是哪个月若y年没有闰月 则返回0
* @param lunar Year
* @return Number (0-12)
* @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
*/
leapMonth: function (y) { // 闰字编码 \u95f0
return (this.lunarInfo[y - 1900] & 0xf)
},
/**
* 返回农历y年闰月的天数 若该年没有闰月则返回0
* @param lunar Year
* @return Number (0、29、30)
* @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
*/
leapDays: function (y) {
if (this.leapMonth(y)) {
return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
}
return (0)
},
/**
* 返回农历y年m月非闰月的总天数计算m为闰月时的天数请使用leapDays方法
* @param lunar Year
* @return Number (-1、29、30)
* @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
*/
monthDays: function (y, m) {
if (m > 12 || m < 1) { return -1 }// 月份参数从1至12参数错误返回-1
return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
},
/**
* 返回公历(!)y年m月的天数
* @param solar Year
* @return Number (-1、28、29、30、31)
* @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
*/
solarDays: function (y, m) {
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var ms = m - 1
if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
} else {
return (this.solarMonth[ms])
}
},
/**
* 农历年份转换为干支纪年
* @param lYear 农历年的年份数
* @return Cn string
*/
toGanZhiYear: function (lYear) {
var ganKey = (lYear - 3) % 10
var zhiKey = (lYear - 3) % 12
if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
},
/**
* 公历月、日判断所属星座
* @param cMonth [description]
* @param cDay [description]
* @return Cn string
*/
toAstro: function (cMonth, cDay) {
var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
},
/**
* 传入offset偏移量返回干支
* @param offset 相对甲子的偏移量
* @return Cn string
*/
toGanZhi: function (offset) {
return this.Gan[offset % 10] + this.Zhi[offset % 12]
},
/**
* 传入公历(!)y年获得该年第n个节气的公历日期
* @param y公历年(1900-2100)n二十四节气中的第几个节气(1~24)从n=1(小寒)算起
* @return day Number
* @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
*/
getTerm: function (y, n) {
if (y < 1900 || y > 2100) { return -1 }
if (n < 1 || n > 24) { return -1 }
var _table = this.sTermInfo[y - 1900]
var _info = [
parseInt('0x' + _table.substr(0, 5)).toString(),
parseInt('0x' + _table.substr(5, 5)).toString(),
parseInt('0x' + _table.substr(10, 5)).toString(),
parseInt('0x' + _table.substr(15, 5)).toString(),
parseInt('0x' + _table.substr(20, 5)).toString(),
parseInt('0x' + _table.substr(25, 5)).toString()
]
var _calday = [
_info[0].substr(0, 1),
_info[0].substr(1, 2),
_info[0].substr(3, 1),
_info[0].substr(4, 2),
_info[1].substr(0, 1),
_info[1].substr(1, 2),
_info[1].substr(3, 1),
_info[1].substr(4, 2),
_info[2].substr(0, 1),
_info[2].substr(1, 2),
_info[2].substr(3, 1),
_info[2].substr(4, 2),
_info[3].substr(0, 1),
_info[3].substr(1, 2),
_info[3].substr(3, 1),
_info[3].substr(4, 2),
_info[4].substr(0, 1),
_info[4].substr(1, 2),
_info[4].substr(3, 1),
_info[4].substr(4, 2),
_info[5].substr(0, 1),
_info[5].substr(1, 2),
_info[5].substr(3, 1),
_info[5].substr(4, 2)
]
return parseInt(_calday[n - 1])
},
/**
* 传入农历数字月份返回汉语通俗表示法
* @param lunar month
* @return Cn string
* @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
*/
toChinaMonth: function (m) { // 月 => \u6708
if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
var s = this.nStr3[m - 1]
s += '\u6708'// 加上月字
return s
},
/**
* 传入农历日期数字返回汉字表示法
* @param lunar day
* @return Cn string
* @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
*/
toChinaDay: function (d) { // 日 => \u65e5
var s
switch (d) {
case 10:
s = '\u521d\u5341'; break
case 20:
s = '\u4e8c\u5341'; break
break
case 30:
s = '\u4e09\u5341'; break
break
default :
s = this.nStr2[Math.floor(d / 10)]
s += this.nStr1[d % 10]
}
return (s)
},
/**
* 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
* @param y year
* @return Cn string
* @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
*/
getAnimal: function (y) {
return this.Animals[(y - 4) % 12]
},
/**
* 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
* @param y solar year
* @param m solar month
* @param d solar day
* @return JSON object
* @eg:console.log(calendar.solar2lunar(1987,11,01));
*/
solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
// 年份限定、上限
if (y < 1900 || y > 2100) {
return -1// undefined转换为数字变为NaN
}
// 公历传参最下限
if (y == 1900 && m == 1 && d < 31) {
return -1
}
// 未传参 获得当天
if (!y) {
var objDate = new Date()
} else {
var objDate = new Date(y, parseInt(m) - 1, d)
}
var i; var leap = 0; var temp = 0
// 修正ymd参数
var y = objDate.getFullYear()
var m = objDate.getMonth() + 1
var d = objDate.getDate()
var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
for (i = 1900; i < 2101 && offset > 0; i++) {
temp = this.lYearDays(i)
offset -= temp
}
if (offset < 0) {
offset += temp; i--
}
// 是否今天
var isTodayObj = new Date()
var isToday = false
if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
isToday = true
}
// 星期几
var nWeek = objDate.getDay()
var cWeek = this.nStr1[nWeek]
// 数字表示周几顺应天朝周一开始的惯例
if (nWeek == 0) {
nWeek = 7
}
// 农历年
var year = i
var leap = this.leapMonth(i) // 闰哪个月
var isLeap = false
// 效验闰月
for (i = 1; i < 13 && offset > 0; i++) {
// 闰月
if (leap > 0 && i == (leap + 1) && isLeap == false) {
--i
isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
} else {
temp = this.monthDays(year, i)// 计算农历普通月天数
}
// 解除闰月
if (isLeap == true && i == (leap + 1)) { isLeap = false }
offset -= temp
}
// 闰月导致数组下标重叠取反
if (offset == 0 && leap > 0 && i == leap + 1) {
if (isLeap) {
isLeap = false
} else {
isLeap = true; --i
}
}
if (offset < 0) {
offset += temp; --i
}
// 农历月
var month = i
// 农历日
var day = offset + 1
// 天干地支处理
var sm = m - 1
var gzY = this.toGanZhiYear(year)
// 当月的两个节气
// bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
// 依据12节气修正干支月
var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
if (d >= firstNode) {
gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
}
// 传入的日期的节气与否
var isTerm = false
var Term = null
if (firstNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 2]
}
if (secondNode == d) {
isTerm = true
Term = this.solarTerm[m * 2 - 1]
}
// 日柱 当月一日与 1900/1/1 相差天数
var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
var gzD = this.toGanZhi(dayCyclical + d - 1)
// 该日期所属的星座
var astro = this.toAstro(m, d)
return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
},
/**
* 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
* @param y lunar year
* @param m lunar month
* @param d lunar day
* @param isLeapMonth lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
* @return JSON object
* @eg:console.log(calendar.lunar2solar(1987,9,10));
*/
lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
var isLeapMonth = !!isLeapMonth
var leapOffset = 0
var leapMonth = this.leapMonth(y)
var leapDay = this.leapDays(y)
if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
var day = this.monthDays(y, m)
var _day = day
// bugFix 2016-9-25
// if month is leap, _day use leapDays method
if (isLeapMonth) {
_day = this.leapDays(y, m)
}
if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
// 计算农历的时间差
var offset = 0
for (var i = 1900; i < y; i++) {
offset += this.lYearDays(i)
}
var leap = 0; var isAdd = false
for (var i = 1; i < m; i++) {
leap = this.leapMonth(y)
if (!isAdd) { // 处理闰月
if (leap <= i && leap > 0) {
offset += this.leapDays(y); isAdd = true
}
}
offset += this.monthDays(y, i)
}
// 转换闰月农历 需补充该年闰月的前一个月的时差
if (isLeapMonth) { offset += day }
// 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
var calObj = new Date((offset + d - 31) * 86400000 + stmap)
var cY = calObj.getUTCFullYear()
var cM = calObj.getUTCMonth() + 1
var cD = calObj.getUTCDate()
return this.solar2lunar(cY, cM, cD)
}
}
export default calendar

View File

@@ -0,0 +1,12 @@
{
"uni-calender.ok": "ok",
"uni-calender.cancel": "cancel",
"uni-calender.today": "today",
"uni-calender.MON": "MON",
"uni-calender.TUE": "TUE",
"uni-calender.WED": "WED",
"uni-calender.THU": "THU",
"uni-calender.FRI": "FRI",
"uni-calender.SAT": "SAT",
"uni-calender.SUN": "SUN"
}

View File

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

View File

@@ -0,0 +1,12 @@
{
"uni-calender.ok": "确定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

View File

@@ -0,0 +1,12 @@
{
"uni-calender.ok": "確定",
"uni-calender.cancel": "取消",
"uni-calender.today": "今日",
"uni-calender.SUN": "日",
"uni-calender.MON": "一",
"uni-calender.TUE": "二",
"uni-calender.WED": "三",
"uni-calender.THU": "四",
"uni-calender.FRI": "五",
"uni-calender.SAT": "六"
}

View File

@@ -0,0 +1,194 @@
<template>
<view class="uni-calendar-item__weeks-box" :class="{
'uni-calendar-item--disable':weeks.disable,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':(calendar.fullDate === weeks.fullDate && !weeks.isDay) ,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}" @click="choiceDate(weeks)">
<view class="uni-calendar-item__weeks-box-item">
<text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text>
<text class="uni-calendar-item__weeks-box-text" :class="{
'uni-calendar-item--isDay-text': weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.date}}</text>
<text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
}">{{todayText}}</text>
<text v-if="lunar" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text>
<text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
'uni-calendar-item--extra':weeks.extraInfo.info,
'uni-calendar-item--isDay-text':weeks.isDay,
'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
'uni-calendar-item--before-checked':weeks.beforeMultiple,
'uni-calendar-item--multiple': weeks.multiple,
'uni-calendar-item--after-checked':weeks.afterMultiple,
'uni-calendar-item--disable':weeks.disable,
}">{{weeks.extraInfo.info}}</text>
</view>
</view>
</template>
<script>
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
export default {
emits: ['change'],
props: {
weeks: {
type: Object,
default () {
return {}
}
},
calendar: {
type: Object,
default: () => {
return {}
}
},
selected: {
type: Array,
default: () => {
return []
}
},
lunar: {
type: Boolean,
default: false
}
},
computed: {
todayText() {
return t("uni-calender.today")
},
},
methods: {
choiceDate(weeks) {
this.$emit('change', weeks)
}
}
}
</script>
<style lang="scss" scoped>
$uni-font-size-base: 14px;
$uni-text-color: #333;
$uni-font-size-sm: 12px;
$uni-color-error: #e43d33;
$uni-opacity-disabled: 0.3;
$uni-text-color-disable: #c0c0c0;
$uni-primary: #2979ff !default;
.uni-calendar-item__weeks-box {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.uni-calendar-item__weeks-box-text {
font-size: 16px;
color: $uni-text-color;
line-height: 36rpx;
font-weight: 600;
}
.uni-calendar-item__weeks-lunar-text {
font-size: $uni-font-size-sm;
color: $uni-text-color;
}
.uni-calendar-item__weeks-box-item {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
width: 100rpx;
height: 100rpx;
}
.uni-calendar-item__weeks-box-circle {
position: absolute;
right: 42rpx;
bottom: 1rpx;
font-size: 20rpx;
line-height: 1;
display: block;
padding: 8rpx;
border-radius: 12rpx;
background-color: #FE5146;
}
.uni-calendar-item--disable {
background-color: rgba(249, 249, 249, $uni-opacity-disabled);
color: $uni-text-color-disable;
}
.uni-calendar-item--isDay-text {
color: $uni-color-primary;
}
.uni-calendar-item--isDay {
background-color: $uni-color-primary;
color: #fff;
}
.uni-calendar-item--extra {
color: $uni-color-error;
}
.uni-calendar-item--checked {
background-color: $uni-color-primary;
color: #fff;
}
.uni-calendar-item--multiple {
background-color: $uni-color-primary;
color: #fff;
}
.uni-calendar-item--before-checked {
background-color: #ff5a5f;
color: #fff;
}
.uni-calendar-item--after-checked {
background-color: #ff5a5f;
color: #fff;
}
</style>

View File

@@ -0,0 +1,579 @@
<template>
<view class="uni-calendar">
<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}"
@click="clean"></view>
<view v-if="insert || show" class="uni-calendar__content"
:class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
<view class="uni-calendar__header-btn-box" @click="close">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
</view>
<view class="uni-calendar__header-btn-box" @click="confirm">
<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
</view>
</view>
<view class="uni-calendar__header">
<view class="uni-calendar__header-btn-box" @click.stop="pre">
<view class="uni-calendar__header-btn uni-calendar--left"></view>
</view>
<picker mode="date" :value="date" fields="month" @change="bindDateChange">
<text
class="uni-calendar__header-text">{{ (nowDate.year||'') + '年' +( nowDate.month||'') + '月'}}</text>
</picker>
<view class="uni-calendar__header-btn-box" @click.stop="next">
<view class="uni-calendar__header-btn uni-calendar--right"></view>
</view>
<text class="uni-calendar__backtoday" @click="backToday">回到今天</text>
</view>
<view class="uni-calendar__box">
<view v-if="showMonth" class="uni-calendar__box-bg">
<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
</view>
<view class="uni-calendar__weeks">
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{monText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
</view>
<view class="uni-calendar__weeks-day">
<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
</view>
</view>
<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar"
:selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import Calendar from './util.js';
import CalendarItem from './uni-calendar-item.vue'
import {
initVueI18n
} from '@dcloudio/uni-i18n'
import i18nMessages from './i18n/index.js'
const {
t
} = initVueI18n(i18nMessages)
/**
* Calendar 日历
* @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
* @tutorial https://ext.dcloud.net.cn/plugin?id=56
* @property {String} date 自定义当前时间,默认为今天
* @property {Boolean} lunar 显示农历
* @property {String} startDate 日期选择范围-开始日期
* @property {String} endDate 日期选择范围-结束日期
* @property {Boolean} range 范围选择
* @property {Boolean} insert = [true|false] 插入模式,默认为false
* @value true 弹窗模式
* @value false 插入模式
* @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
* @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
* @property {Boolean} showMonth 是否选择月份为背景
* @event {Function} change 日期改变,`insert :ture` 时生效
* @event {Function} confirm 确认选择`insert :false` 时生效
* @event {Function} monthSwitch 切换月份时触发
* @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
*/
export default {
components: {
CalendarItem
},
emits: ['close', 'confirm', 'change', 'monthSwitch', 'initdate'],
props: {
date: {
type: String,
default: ''
},
selected: {
type: Array,
default () {
return []
}
},
lunar: {
type: Boolean,
default: false
},
startDate: {
type: String,
default: ''
},
endDate: {
type: String,
default: ''
},
range: {
type: Boolean,
default: false
},
insert: {
type: Boolean,
default: true
},
showMonth: {
type: Boolean,
default: true
},
clearDate: {
type: Boolean,
default: true
}
},
data() {
return {
show: false,
weeks: [],
calendar: {},
nowDate: '',
aniMaskShow: false
}
},
computed: {
/**
* for i18n
*/
okText() {
return t("uni-calender.ok")
},
cancelText() {
return t("uni-calender.cancel")
},
todayText() {
return t("uni-calender.today")
},
monText() {
return t("uni-calender.MON")
},
TUEText() {
return t("uni-calender.TUE")
},
WEDText() {
return t("uni-calender.WED")
},
THUText() {
return t("uni-calender.THU")
},
FRIText() {
return t("uni-calender.FRI")
},
SATText() {
return t("uni-calender.SAT")
},
SUNText() {
return t("uni-calender.SUN")
},
},
watch: {
date(newVal) {
// this.cale.setDate(newVal)
this.init(newVal)
},
startDate(val) {
this.cale.resetSatrtDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
endDate(val) {
this.cale.resetEndDate(val)
this.cale.setDate(this.nowDate.fullDate)
this.weeks = this.cale.weeks
},
selected(newVal) {
this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
this.weeks = this.cale.weeks
}
},
created() {
this.cale = new Calendar({
selected: this.selected,
startDate: this.startDate,
endDate: this.endDate,
range: this.range,
})
this.init(this.date)
},
methods: {
// 取消穿透
clean() {},
bindDateChange(e) {
const value = e.detail.value + '-1'
this.setDate(value)
const {
year,
month
} = this.cale.getDate(value)
this.$emit('monthSwitch', {
year,
month
})
},
/**
* 初始化日期显示
* @param {Object} date
*/
init(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.calendar = this.cale.getInfo(date)
this.$emit('initdate', this.cale, this.nowDate);
},
/**
* 打开日历弹窗
*/
open() {
// 弹窗模式并且清理数据
if (this.clearDate && !this.insert) {
this.cale.cleanMultipleStatus()
// this.cale.setDate(this.date)
this.init(this.date)
}
this.show = true
this.$nextTick(() => {
setTimeout(() => {
this.aniMaskShow = true
}, 50)
})
},
/**
* 关闭日历弹窗
*/
close() {
this.aniMaskShow = false
this.$nextTick(() => {
setTimeout(() => {
this.show = false
this.$emit('close')
}, 300)
})
},
/**
* 确认按钮
*/
confirm() {
this.setEmit('confirm')
this.close()
},
/**
* 变化触发
*/
change() {
if (!this.insert) return
this.setEmit('change')
},
/**
* 选择月份触发
*/
monthSwitch() {
let {
year,
month
} = this.nowDate
this.$emit('monthSwitch', {
year,
month: Number(month)
})
this.$emit('initdate', this.cale, this.cale.date);
},
/**
* 派发事件
* @param {Object} name
*/
setEmit(name) {
let {
year,
month,
date,
fullDate,
lunar,
extraInfo
} = this.calendar
this.$emit(name, {
range: this.cale.multipleStatus,
year,
month,
date,
fulldate: fullDate,
lunar,
extraInfo: extraInfo || {},
cale: this.cale
})
},
/**
* 选择天触发
* @param {Object} weeks
*/
choiceDate(weeks) {
if (weeks.disable) return
this.calendar = weeks
// 设置多选
this.cale.setMultiple(this.calendar.fullDate)
this.weeks = this.cale.weeks
this.change()
},
/**
* 回到今天
*/
backToday() {
const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
const date = this.cale.getDate(new Date())
const todayYearMonth = `${date.year}-${date.month}`
// if (nowYearMonth !== todayYearMonth) {
// this.monthSwitch()
// }
this.init(date.fullDate)
// this.change()
},
/**
* 上个月
*/
pre() {
const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
this.setDate(preDate)
this.monthSwitch()
},
/**
* 下个月
*/
next() {
const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
this.setDate(nextDate)
this.monthSwitch()
},
/**
* 设置日期
* @param {Object} date
*/
setDate(date) {
this.cale.setDate(date)
this.weeks = this.cale.weeks
this.nowDate = this.cale.getInfo(date)
}
}
}
</script>
<style lang="scss" scoped>
$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
$uni-border-color: #EDEDED;
$uni-text-color: #333;
$uni-bg-color-hover: #f1f1f1;
$uni-font-size-base: 14px;
$uni-text-color-placeholder: #808080;
$uni-color-subtitle: #555555;
$uni-text-color-grey: #999;
.uni-calendar {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
}
.uni-calendar__mask {
position: fixed;
bottom: 0;
top: 0;
left: 0;
right: 0;
background-color: $uni-bg-color-mask;
transition-property: opacity;
transition-duration: 0.3s;
opacity: 0;
/* #ifndef APP-NVUE */
z-index: 99;
/* #endif */
}
.uni-calendar--mask-show {
opacity: 1
}
.uni-calendar--fixed {
position: fixed;
/* #ifdef APP-NVUE */
bottom: 0;
/* #endif */
left: 0;
right: 0;
transition-property: transform;
transition-duration: 0.3s;
transform: translateY(460px);
/* #ifndef APP-NVUE */
bottom: calc(var(--window-bottom));
z-index: 99;
/* #endif */
}
.uni-calendar--ani-show {
transform: translateY(0);
}
.uni-calendar__content {
background-color: #fff;
}
.uni-calendar__header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: center;
align-items: center;
height: 50px;
border-bottom-color: $uni-border-color;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar--fixed-top {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
justify-content: space-between;
border-top-color: $uni-border-color;
border-top-style: solid;
border-top-width: 1px;
}
.uni-calendar--fixed-width {
width: 50px;
}
.uni-calendar__backtoday {
position: absolute;
right: 0;
top: 25rpx;
padding: 0 5px;
height: 25px;
line-height: 25px;
font-size: 12px;
color: $uni-text-color;
background-color: $uni-bg-color-hover;
margin-right: 28rpx;
}
.uni-calendar__header-text {
text-align: center;
width: 100px;
font-size: $uni-font-size-base;
color: $uni-text-color;
}
.uni-calendar__header-btn-box {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
justify-content: center;
width: 50px;
height: 50px;
}
.uni-calendar__header-btn {
width: 10px;
height: 10px;
border-left-color: $uni-text-color-placeholder;
border-left-style: solid;
border-left-width: 2px;
border-top-color: $uni-color-subtitle;
border-top-style: solid;
border-top-width: 2px;
}
.uni-calendar--left {
transform: rotate(-45deg);
}
.uni-calendar--right {
transform: rotate(135deg);
}
.uni-calendar__weeks {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
margin: 10rpx 0;
}
.uni-calendar__weeks-item {
flex: 1;
}
.uni-calendar__weeks-day {
flex: 1;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
justify-content: center;
align-items: center;
height: 45px;
border-bottom-color: #F5F5F5;
border-bottom-style: solid;
border-bottom-width: 1px;
}
.uni-calendar__weeks-day-text {
font-size: 14px;
}
.uni-calendar__box {
position: relative;
}
.uni-calendar__box-bg {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.uni-calendar__box-bg-text {
font-size: 200px;
font-weight: bold;
color: $uni-text-color-grey;
opacity: 0.1;
text-align: center;
/* #ifndef APP-NVUE */
line-height: 1;
/* #endif */
}
</style>

View File

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

View File

@@ -0,0 +1,281 @@
<template>
<view class="">
<u-navbar title="详情" :custom-back="back">
<template #right>
<view class="navbar-right">
<view class="message-box right-item" style="padding-right: 20rpx;">
<u-icon name='more-dot-fill' @click="show=true" v-if="!groupId"></u-icon>
</view>
</view>
</template>
</u-navbar>
<view class="scheduleForm-v jnpf-wrap">
<u-toast ref="uToast" />
<u-form :model="dataForm" ref="dataForm" :errorType="['toast']" label-position="left" label-width="150"
label-align="left">
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="标题" prop="title">
<u-input input-align='right' v-model="dataForm.title" label-align="right" placeholder=""
disabled></u-input>
</u-form-item>
<u-form-item label="内容" prop="content">
<u-input input-align='right' v-model="dataForm.content" type="textarea" placeholder=""
disabled></u-input>
</u-form-item>
</view>
<!-- #ifndef APP-HARMONY -->
<view class="u-p-l-20 u-p-r-20 u-m-t-20 form-item-box">
<u-form-item label="附件" prop="content">
<JnpfUploadFile v-model="dataForm.files" detailed />
</u-form-item>
</view>
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<view class="u-p-l-20 u-p-r-20 u-m-t-20 form-item-box">
<u-form-item label="附件" prop="content">
<JnpfUploadFileH v-model="dataForm.files" detailed />
</u-form-item>
</view>
<!-- #endif -->
<view class="jnpf-card">
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="紧急程度" prop="urgent">
<u-input input-align='right' v-model="dataForm.urgent" label-align="right" placeholder=""
disabled></u-input>
</u-form-item>
<u-form-item label="类型" prop="category">
<u-input input-align='right' v-model="dataForm.category" placeholder="" disabled></u-input>
</u-form-item>
<u-form-item label="创建人" prop="creatorUserId">
<u-input input-align='right' v-model="dataForm.creatorUserId" placeholder=""
disabled></u-input>
</u-form-item>
<u-form-item label="参与人" prop="toUserIds">
<u-input input-align='right' v-model="dataForm.toUserIds" placeholder="" disabled></u-input>
</u-form-item>
</view>
</view>
<view class="jnpf-card">
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="开始时间" prop="startDay">
<datetime :delayMin="0" v-model="startDay" :date="startDate" disabled
:showtdatetime='showtdatetime' :type="1" :allDay='dataForm.allDay' placeholder="" />
</u-form-item>
<u-form-item label="结束时间" prop="endDay">
<datetime :delayMin="0" v-model="endDay" :date="endDate" disabled
:showtdatetime='showtdatetime' :type="2" :allDay='dataForm.allDay' placeholder="" />
</u-form-item>
</view>
</view>
</u-form>
</view>
<u-action-sheet @click="handleAction" :list="actionList" :tips="{ text:'' , color: '#000' , fontSize: 30 }"
v-model="show">
</u-action-sheet>
<u-action-sheet @click="delAction" :list="delList"
:tips="{ text:toUserType===true?dataForm.repetition != '1'?'此为重复日程,将删除(含参与人)应用于':'确认删除(含参与人)当前日程':dataForm.repetition != '1'?'此为重复日程,将删除应用于':'确认删除当前日程' , color: '#000' , fontSize: 30 }"
v-model="showBtn">
</u-action-sheet>
</view>
</template>
<script>
import datetime from './t-datetime.vue'
import {
ScheduleDetail,
ScheduleDelete
} from '@/api/workFlow/schedule.js'
import {
useBaseStore
} from '@/store/modules/base'
const baseStore = useBaseStore()
export default {
components: {
datetime
},
data() {
return {
showAction: '',
userInfo: {},
show: false,
showtdatetime: false,
dataForm: {
id: 0,
category: '',
creatorUserId: 0,
userName: '',
allDay: 0,
urgent: '1',
startDay: '',
startTime: '',
endDay: '',
endTime: '',
duration: '',
content: '',
title: '',
toUserIds: '',
reminderTime: -2,
reminderType: 1,
send: '',
sendName: '',
repetition: 1,
repeatTime: '',
color: '#188ae2'
},
showBtn: false,
startDate: {},
endDate: {},
repeatDate: {},
repeat: '',
endDay: '',
startDay: '',
toUserType: false,
actionList: [],
delList: [],
groupId: ''
}
},
onReady() {
this.$refs.dataForm.setRules(this.rules);
},
onLoad(option) {
this.dataForm.id = option.id
this.toUserType = option.type
this.actionList = []
this.delList = []
this.groupId = option.groupId
if (this.toUserType == 'true') {
this.actionList.push({
text: '编辑',
id: 'update'
})
this.actionList.push({
text: '删除',
id: 'deldete',
color: '#ff3a3a',
})
} else {
this.actionList.push({
text: '删除',
id: 'deldete',
color: '#ff3a3a',
})
}
ScheduleDetail(this.groupId, option.id).then(res => {
let data = res.data || {};
data.files = data.files ? JSON.parse(data.files) : [];
this.dataForm = data
if (this.dataForm.repetition != '1') {
this.delList.push({
text: '仅删除此日程',
id: '1'
})
this.delList.push({
text: '删除此日程及后续日程',
id: '2',
})
this.delList.push({
text: '删除所有日程',
id: '3',
})
} else {
this.delList.push({
text: '删除',
id: '3',
color: '#ff3a3a',
})
}
this.startDate = this.timestampToTime(this.dataForm.startDay)
this.endDate = this.timestampToTime(this.dataForm.endDay)
}).catch((err) => {
uni.showToast({
title: err,
complete: () => {
setTimeout(() => {
uni.navigateBack()
}, 300)
}
})
})
},
methods: {
delAction(index) {
if (this.dataForm.repetition != '1') {
ScheduleDelete(this.dataForm.id, index + 1).then(res => {
uni.showToast({
title: res.msg,
complete: () => {
uni.$emit('refresh')
uni.navigateBack()
}
})
})
} else {
ScheduleDelete(this.dataForm.id, 3).then(res => {
uni.showToast({
title: res.msg,
complete: () => {
uni.$emit('refresh')
uni.navigateBack()
}
})
})
}
},
handleAction(index) {
if (this.actionList[index].id == 'update') {
uni.navigateTo({
url: './form?id=' + this.dataForm.id
})
} else {
this.showBtn = true
}
},
back() {
uni.navigateBack();
},
getResult() {
uni.navigateBack()
},
changeSend(id, name) {
this.dataForm.send = id
this.dataForm.sendName = name
},
getDictionaryData() {
baseStore.getDictionaryData({
sort: 'scheduleType'
}).then((res) => {
this.typeOptions = res
})
},
timestampToTime(timestamp) {
let list = {}
var date = new Date(timestamp);
let Y = date.getFullYear();
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1);
let D = date.getDate();
let h = date.getHours();
let m = date.getMinutes();
let s = date.getSeconds();
list.year = Y
list.month = M
list.date = D
list.hours = h < 10 ? 0 + h : h
list.minutes = m < 10 ? 0 + m : m
list.seconds = s
return list
},
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.scheduleForm-v {
padding-bottom: 110rpx;
}
</style>

View File

@@ -0,0 +1,610 @@
<template>
<view class="scheduleForm-v jnpf-wrap">
<u-toast ref="uToast" />
<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 form-item-box">
<u-form-item label="标题" prop="title" required>
<u-input input-align='right' v-model="dataForm.title" placeholder="请输入"></u-input>
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="内容" prop="content">
<u-input input-align='right' v-model="dataForm.content" placeholder="请输入" type="textarea"></u-input>
</u-form-item>
</view>
<!-- #ifndef APP-HARMONY -->
<view class="jnpf-card u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="附件" prop="files">
<JnpfUploadFile v-model="dataForm.files" />
</u-form-item>
</view>
<!-- #endif -->
<!-- #ifdef APP-HARMONY -->
<view class="jnpf-card u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="附件" prop="files">
<JnpfUploadFileH v-model="dataForm.files" />
</u-form-item>
</view>
<!-- #endif -->
<view class="jnpf-card">
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="紧急程度" prop="urgent">
<JnpfSelect v-model="dataForm.urgent" :options='urgentList' placeholder="请选择" />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="类型" prop="category" required>
<JnpfSelect v-model="dataForm.category" :options='categoryOptions' placeholder="请选择" />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="创建人" prop="creatorUserId" required>
<JnpfUserSelect v-model="dataForm.creatorUserId" placeholder="请输入" disabled="disabled" />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="参与人" prop="toUserIds">
<JnpfUserSelect v-model="dataForm.toUserIds" placeholder="请选择" multiple />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="标签颜色" prop="color">
<JnpfColorPicker v-model="dataForm.color" />
</u-form-item>
</view>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="全天" prop="allDay">
<JnpfSwitch v-model="dataForm.allDay" @change="change_providerType" />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="开始时间" prop="startDay" required>
<tdatetime :delayMin="0" v-model="startDay" :date="startDate" placeholder="请选择"
:showtdatetime='showtdatetime' :type="1" @confirm='confirm' :allDay='dataForm.allDay' />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="时长" prop="duration" required v-if="dataForm.duration!=-1&&dataForm.allDay==0">
<JnpfSelect v-model="dataForm.duration" :options='durationList' />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="结束时间" prop="endDay" required v-if='dataForm.duration==-1||dataForm.allDay'>
<tdatetime :delayMin="0" v-model="endDay" :date="endDate" placeholder="请选择"
:showtdatetime='showtdatetime' :type="2" @confirm='confirm' :allDay='dataForm.allDay' />
</u-form-item>
</view>
<view class="jnpf-card">
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="提醒时间" prop="reminderTime">
<JnpfSelect v-model="dataForm.reminderTime"
:options='dataForm.allDay?reminderTimeList_:reminderTimeList' placeholder="请选择" />
</u-form-item>
</view>
<view v-if="dataForm.reminderTime!=-2" class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="提醒方式" prop="reminderType">
<jnpf-select v-model="dataForm.reminderType" :options='remindList'
placeholder="请选择"></jnpf-select>
</u-form-item>
<u-form-item label="发送配置" prop="send" v-if="dataForm.reminderType==2">
<sendSelect v-model="dataForm.sendName" :send='dataForm.send' @change='changeSend'
placeholder="请选择" />
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="重复提醒" prop="repetition">
<jnpf-select v-model="dataForm.repetition" :options='repeatReminderList'
@change='repeatTimeChange' placeholder="请选择"></jnpf-select>
</u-form-item>
</view>
<view class="u-p-l-20 u-p-r-20 form-item-box">
<u-form-item label="结束重复" prop="repeatTime" required v-if='dataForm.repetition!=1'>
<tdatetime :delayMin="0" v-model="repeat" :date="repeatDate" placeholder="请选择"
:showtdatetime='showtdatetime' :type="3" @confirm='confirm' :allDay='1' />
</u-form-item>
</view>
</view>
</u-form>
<view class="flowBefore-actions">
<u-button class="buttom-btn" @click="getResult('cancel')">{{$t('common.cancelText')}}</u-button>
<u-button class="buttom-btn" type="primary" :loading='btnLoading'
@click.stop="save()">{{$t('common.okText')}}</u-button>
</view>
<u-action-sheet @click="handleAction" :list="actionList"
:tips="{ text:'此为重复日程,将修改应用于' , color: '#000' , fontSize: 30 }" v-model="show">
</u-action-sheet>
</view>
</template>
<script>
import tdatetime from './t-datetime.vue'
import sendSelect from './sendSelect/index.vue'
import {
ScheduleInfo,
ScheduleCreate,
ScheduleUpdate
} from '@/api/workFlow/schedule.js'
import {
useBaseStore
} from '@/store/modules/base'
const baseStore = useBaseStore()
export default {
components: {
tdatetime,
sendSelect
},
data() {
return {
title: '',
userInfo: {},
show: false,
showtdatetime: false,
dataForm: {
id: 0,
category: '',
creatorUserId: 0,
userName: '',
allDay: 0,
urgent: '1',
startDay: '',
startTime: '',
endDay: '',
endTime: '',
duration: 60,
content: '',
title: '',
toUserIds: [],
reminderTime: -2,
reminderType: 1,
send: '',
sendName: '',
repetition: 1,
repeatTime: '',
color: '#FFFFFF',
files: []
},
btnLoading: false,
showBtn: false,
startDate: {},
endDate: {},
repeatDate: {},
repeat: '',
endDay: '',
startDay: '',
urgentList: [{
id: "1",
fullName: '普通'
}, {
id: '2',
fullName: '重要'
}, {
id: '3',
fullName: '紧急'
}],
durationList: [{
id: 30,
fullName: '30分钟'
}, {
id: 60,
fullName: '1小时'
}, {
id: 90,
fullName: '1小时30分钟'
}, {
id: 120,
fullName: '2小时'
}, {
id: 180,
fullName: '3小时'
}, {
id: -1,
fullName: '自定义'
}],
categoryOptions: [],
repetitionType: false,
reminderTimeList: [{
id: -2,
fullName: '不提醒'
}, {
id: -1,
fullName: '开始时'
}, {
id: 5,
fullName: '提前5分钟'
}, {
id: 10,
fullName: '提前10分钟'
}, {
id: 15,
fullName: '提前15分钟'
}, {
id: 30,
fullName: '提前30分钟'
}, {
id: 60,
fullName: '提前1小时'
}, {
id: 120,
fullName: '提前2小时'
}, {
id: 1440,
fullName: '1天前'
}, {
id: 2880,
fullName: '2天前'
}, {
id: 10080,
fullName: '1周前'
}],
reminderTimeList_: [{
id: -2,
fullName: '不提醒'
},
{
id: 1,
fullName: '当天8:00'
},
{
id: 2,
fullName: '当天9:00'
},
{
id: 3,
fullName: '当天10:00'
},
{
id: 4,
fullName: '1天前8:00'
},
{
id: 5,
fullName: '1天前9:00'
},
{
id: 6,
fullName: '1天前10:00'
},
{
id: 7,
fullName: '2天前8:00'
},
{
id: 8,
fullName: '2天前9:00'
},
{
id: 9,
fullName: '2天前10:00'
},
{
id: 10,
fullName: '1周前8:00'
},
{
id: 11,
fullName: '1周前9:00'
},
{
id: 12,
fullName: '1周前10:00'
}
],
remindList: [{
id: 1,
fullName: '默认'
}, {
id: 2,
fullName: '自定义'
}],
repeatReminderList: [{
id: 1,
fullName: '不重复'
}, {
id: 2,
fullName: '每天重复'
}, {
id: 3,
fullName: '每周重复'
}, {
id: 4,
fullName: '每月重复'
}, {
id: 5,
fullName: '每年重复'
}],
actionList: [],
rules: {
startTime: [{
required: true,
message: '开始时间不能为空',
trigger: 'change',
type: 'number'
}],
startTime: [{
required: true,
message: '开始时间不能为空',
trigger: 'change',
type: 'number'
}],
endTime: [{
required: true,
message: '结束时间不能为空',
trigger: 'change',
type: 'number'
}],
repeat: [{
required: true,
message: '记录不能为空',
trigger: 'change',
}],
category: [{
required: true,
message: '请选择类型',
trigger: 'change'
}],
title: [{
required: true,
message: '请输入标题',
trigger: 'blur'
}],
duration: [{
required: true,
message: '请选择时长',
trigger: 'change',
type: 'number'
}],
send: [{
required: true,
message: '发送配置不能为空',
trigger: 'change'
}],
reminderType: [{
required: true,
message: '提醒方式不能为空',
trigger: 'change',
type: 'number'
}]
}
}
},
onReady() {
this.$refs.dataForm.setRules(this.rules);
},
onLoad(option) {
this.repetitionType = false
this.userInfo = uni.getStorageSync('userInfo') || {}
this.dataForm.id = option.id
this.btnLoading = false
this.title = this.dataForm.id ? '编辑' : '新建'
uni.setNavigationBarTitle({
title: this.title
});
if (this.dataForm.id) {
ScheduleInfo(option.id).then(res => {
let data = res.data
data.files = data.files ? JSON.parse(data.files) : [];
this.dataForm = data
this.startDate = this.timestampToTime(this.dataForm.startDay)
this.endDate = this.timestampToTime(this.dataForm.endDay)
this.repeatDate = this.dataForm.repeatTime ? this.timestampToTime(this.dataForm.repeatTime) :
{}
if (this.dataForm.repetition != "1") return this.repetitionType = true
})
} else {
let startDate = this.timestampToTime(+new Date())
this.startDate = this.timestampToTime(option.startTime || +new Date())
this.startDate.hours = startDate.hours + 1
this.startDate.minutes = '00'
this.confirm(this.startDate, 1)
this.endDate = this.timestampToTime(option.startTime || +new Date())
this.endDate.hours = startDate.hours + 2
this.endDate.minutes = '00'
this.confirm(this.endDate, 2)
this.repeatDate = {}
this.dataForm.creatorUserId = this.userInfo.userId
this.dataForm.duration = Number(option.duration) || 60
}
this.getDictionaryData()
},
methods: {
back() {
if (!this.dataForm.id) return uni.navigateBack()
uni.navigateBack({
delta: 2
})
},
handleAction(index) {
ScheduleUpdate(this.dataForm, index + 1).then(res => {
uni.showToast({
title: res.msg,
complete: () => {
uni.$emit('refresh')
uni.navigateBack({
delta: 2
})
}
})
})
},
change_providerType(val) {
if (!val) {
let startDate = this.timestampToTime(+new Date())
this.startDate = this.timestampToTime(this.dataForm.startDay)
this.startDate.hours = startDate.hours + 1
this.startDate.minutes = '00'
this.confirm(this.startDate, 1)
this.endDate = this.timestampToTime(this.dataForm.endDay)
this.endDate.hours = startDate.hours + 2
this.endDate.minutes = '00'
this.confirm(this.endDate, 2)
} else {
this.dataForm.endDay = this.dataForm.startDay
}
this.dataForm.reminderTime = -2
},
repeatTimeChange(val) {
let time = new Date()
time.setFullYear(time.getFullYear() + 1)
if (val != 1) {
let date = time.getTime()
this.repeatDate = this.timestampToTime(date)
this.repeatDate.minutes = '00'
this.confirm(this.repeatDate, 3)
}
},
getResult() {
if (!this.dataForm.id) return uni.navigateBack()
uni.showModal({
title: '退出此次编辑?',
content: '日程信息将不会保存',
success: res => {
if (res.confirm) {
uni.$emit('refresh')
uni.navigateBack({
delta: 2
})
}
}
})
},
changeSend(id, name) {
this.dataForm.send = id
this.dataForm.sendName = name
},
getDictionaryData() {
baseStore.getDictionaryData({
sort: 'scheduleType'
}).then((res) => {
this.categoryOptions = res || []
if (this.categoryOptions.length) this.dataForm.category = this.categoryOptions[0].id
})
},
confirm(e, type) {
if (type == 1) {
this.dataForm.startDay = e.year + '-' + e.month + '-' + e.date
this.dataForm.startDay = new Date(this.dataForm.startDay).getTime()
this.dataForm.startTime = e.hours + ":" + e.minutes
} else if (type == 2) {
this.dataForm.endDay = e.year + '-' + e.month + '-' + e.date
this.dataForm.endDay = new Date(this.dataForm.endDay).getTime()
this.dataForm.endTime = e.hours + ":" + e.minutes
} else {
this.dataForm.repeatTime = e.year + '-' + e.month + '-' + e.date
this.dataForm.repeatTime = new Date(this.dataForm.repeatTime).getTime()
}
},
save() {
this.$refs.dataForm.validate((valid) => {
if (valid) {
if (this.dataForm.duration == -1) {
if (this.dataForm.startDay > this.dataForm.endDay) {
this.$refs.uToast.show({
title: '结束时间必须晚于开始时间',
type: 'error'
})
return
}
}
if (this.dataForm.allDay == 1) {
let startDay = this.timestampToData(this.dataForm.startDay)
let endDay = this.timestampToData(this.dataForm.endDay)
if (this.dataForm.startDay > this.dataForm.endDay) {
this.$refs.uToast.show({
title: '结束时间必须晚于开始时间',
type: 'error'
})
return
}
this.dataForm.startDay = new Date(startDay).getTime()
this.dataForm.endDay = new Date(endDay).getTime()
}
if (this.dataForm.repetition != 1) {
if (this.dataForm.startDay > this.dataForm.repeatTime) {
this.$refs.uToast.show({
title: '结束重复时间必须晚于开始时间',
type: 'error'
})
return
}
}
if (this.dataForm.id && this.repetitionType) {
this.show = true
this.actionList = []
this.actionList.push({
text: '仅修改此日程',
id: '1'
})
this.actionList.push({
text: '修改此日程及后续日程',
id: '2',
})
} else {
this.btnLoading = true
const formMethod = this.dataForm.id ? ScheduleUpdate : ScheduleCreate;
const query = {
...this.dataForm,
files: JSON.stringify(this.dataForm.files)
}
formMethod(query, 3).then(res => {
uni.showToast({
title: res.msg,
complete: () => {
this.btnLoading = false
uni.$emit('refresh')
if (this.dataForm.id) {
uni.navigateBack({
delta: 2
})
} else {
uni.navigateBack()
}
}
})
})
}
}
})
},
timestampToTime(timestamp) {
let list = {}
timestamp = timestamp || 0
var date = new Date(Number(timestamp));
let Y = date.getFullYear();
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1);
let D = date.getDate();
let h = date.getHours();
let m = date.getMinutes();
let s = date.getSeconds();
list.year = Y
list.month = M
list.date = D
list.hours = h < 10 ? 0 + h : h
list.minutes = m < 10 ? 0 + m : m
list.seconds = s < 10 ? 0 + s : s
return list
},
timestampToData(timestamp) {
var date = new Date(timestamp);
let Y = date.getFullYear();
let M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1);
let D = date.getDate();
return Y + '-' + M + '-' + D + " 00:00:00"
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.scheduleForm-v {
padding-bottom: 110rpx;
}
</style>

View File

@@ -0,0 +1,277 @@
<template>
<view class="schedule-v">
<view class="calendar-v">
<calendar :lunar="true" :selected="selected" :showMonth="true" @change="change" @initdate="initdate"
:key='key' />
</view>
<view class="u-p-20 block">
<view class="block-a u-font-24">
{{dateDay}}
<div>{{changedate}}</div>
</view>
<view class="u-m-b-20 list-box">
<view v-for="(item,index) in scheduleList" :key="index" class="list">
<view class="u-m-t-20 u-flex item" @click="goDetail(item.id,item.creatorUserId)">
<text class="u-font-28">{{item.allDay?'全天':item.startTime}}</text>
<view class="u-flex-col u-m-l-20 u-p-l-20 content u-font-28">
<view>{{item.title}}</view>
<view class="u-font-24">{{item.content}}</view>
</view>
</view>
</view>
</view>
<view :class="scheduleList.length<3?'lunar1':'addlunar'">
<view @click="goDetail()" class="add-title">+添加日程内容</view>
</view>
</view>
</view>
</template>
<script>
import calendar from './calendar/uni-calendar.vue'
import {
List
} from '@/api/workFlow/schedule.js'
export default {
components: {
direction: 'col',
calendar,
},
props: {
config: {
type: Object,
default: () => {}
}
},
data() {
return {
selected: [],
showForm: false,
horizontal: 'right',
vertical: 'bottom',
direction: 'horizontal',
pattern: {
color: '#7A7E83',
backgroundColor: '#fff',
selectedColor: '#007AFF',
buttonColor: "#007AFF"
},
changedate: '',
scheduleList: [],
exhibitionList: [],
startDate: '',
endDate: '',
dateDay: '',
query: {},
options: [{
text: '删除',
style: {
backgroundColor: '#dd524d'
}
}],
startTime: "",
formVisible: false,
userInfo: {},
key: +new Date(),
toDay: '',
sysConfigInfo: {}
}
},
onLoad() {
this.userInfo = uni.getStorageSync('userInfo') || {}
this.sysConfigInfo = uni.getStorageSync('sysConfigInfo') || {}
},
onShow() {
uni.$on('refresh', () => {
this.key = +new Date()
});
},
onUnload() {
uni.$off('refresh')
},
methods: {
/* 初始化请求 */
initdate(cale, nowDate) {
let canlender = cale.canlender;
let weeks = cale.weeks;
for (let i = 0; i < canlender.length; i++) {
if (canlender[i].fullDate === nowDate.fullDate) {
let day = this.toChinaDay(nowDate.lunar.nWeek)
this.toDay = nowDate.fullDate
this.dateTime = nowDate.fullDate
this.dateDay = nowDate.month + '月' + nowDate.date + '日' + " 周" + day +
" (今天)"
this.changedate = ''
if (this.sysConfigInfo.showLunarCalendar) this.changedate = '农历 ' + canlender[i].lunar.IMonthCn +
canlender[i].lunar.IDayCn;
break;
}
}
let data = {
weeks: weeks,
canlender: canlender
}
this.$nextTick(() => {
this.handleScheduleList(data)
})
},
handleScheduleList(data) {
let canlender = data.canlender
let startTime = this.startDate = canlender[0].lunar.cYear + '-' + canlender[0].lunar.cMonth + '-' +
canlender[0].lunar
.cDay;
let endTime = this.endDate = canlender[canlender.length - 1].lunar.cYear + '-' + canlender[canlender
.length - 1].lunar
.cMonth + '-' + canlender[canlender.length - 1].lunar.cDay;
let query = {
startTime: startTime,
endTime: endTime,
dateTime: data.fulldate || this.toDay
}
List(query).then(res => {
let signList = res.data.signList;
if (res.data.todayList) {
this.scheduleList = res.data.todayList.map(o => ({
...o,
show: false
}));
}
let selected = []
for (let [key, value] of Object.entries(signList)) {
const cYear = key.slice(0, 4)
const cMonth = key.slice(4, 6)
const cDay = key.slice(6, 8)
let date = cYear + '-' + cMonth + '-' + cDay
if (value && value != 0) {
selected.push({
date,
info: ''
})
}
}
this.selected = selected
})
},
change(e) {
let weeks = e.cale.weeks;
let canlender = e.cale.canlender;
let lunar = e.lunar;
lunar.cMonth = lunar.cMonth < 10 ? '0' + Number(lunar.cMonth) : lunar.cMonth
lunar.cDay = lunar.cDay < 10 ? '0' + Number(lunar.cDay) : lunar.cDay
let allDay = lunar.lYear + '-' + lunar.cMonth + '-' + lunar.cDay
let srt = this.time(e.fulldate)
let day = this.toChinaDay(lunar.nWeek)
this.startTime = new Date(e.fulldate).getTime()
this.dateDay = lunar.cMonth + '月' + lunar.cDay + '日' + " 周" + day +
srt
this.changedate = ''
if (this.sysConfigInfo.showLunarCalendar) this.changedate = '农历 ' + lunar.IMonthCn + lunar.IDayCn;
let data = {
weeks: weeks,
canlender: canlender,
lunar: lunar,
fulldate: e.fulldate
}
this.handleScheduleList(data)
},
goDetail(id = '', creatorUserId) {
let type = this.userInfo.userId == creatorUserId ? true : false
let url = id ? `/pages/workFlow/schedule/detail?id=${id}&type=${type}` :
`/pages/workFlow/schedule/form?id=${id}&startTime=${this.startTime}&duration=${this.sysConfigInfo.duration}`
uni.navigateTo({
url
})
},
open(index) {
this.scheduleList[index].show = true;
this.scheduleList.map((val, idx) => {
if (index != idx) this.scheduleList[idx].show = false;
})
},
toChinaDay(d) {
if (d == 1) return '一'
if (d == 2) return '二'
if (d == 3) return '三'
if (d == 4) return '四'
if (d == 5) return '五'
if (d == 6) return '六'
if (d == 7) return '日'
},
time(date) {
let time = new Date()
if (new Date(date).getFullYear() == time.getFullYear() && new Date(date).getMonth() == time.getMonth()) {
let time_str = "";
if (new Date(date).getDate() === new Date().getDate()) {
time_str = " (今天)";
} else if (new Date(date).getDate() === (new Date().getDate() - 1)) {
time_str = " (昨天)";
} else if (new Date(date).getDate() === (new Date().getDate() + 1)) {
time_str = " (明天)";
} else if (new Date(date).getDate() < new Date().getDate()) {
time_str = "";
}
return time_str;
}
return ''
}
}
}
</script>
<style lang="scss">
page {
background-color: #f0f2f6;
}
.schedule-v {
width: 100%;
.block {
.block-a {
background-color: #fff;
padding: 20rpx 30rpx;
}
.list-box {
.list {
.item {
width: 100%;
background-color: #fff;
height: 120rpx;
align-items: center;
padding: 0 20rpx;
.content {
flex: 1;
border-left: 4rpx solid #2A7DFD;
justify-content: center;
height: 80rpx;
}
}
}
}
.lunar1 {
height: 124rpx;
line-height: 124rpx;
background-color: #fff;
}
.addlunar {
height: 124rpx;
line-height: 124rpx;
border-top: 1rpx solid rgb(228, 231, 237);
}
.add-title {
margin-left: 20rpx;
font-size: 24rpx;
color: #C0C0C0;
}
}
.uni-calendar-item__weeks-box-item {
height: 3.23rem;
}
}
</style>

View File

@@ -0,0 +1,218 @@
<template>
<u-popup class="jnpf-tree-select-popup" :maskCloseAble="maskCloseAble" mode="right" v-model="showPopup"
:safeAreaInsetBottom="safeAreaInsetBottom" @close="close" :z-index="uZIndex" width="100%">
<view class="jnpf-tree-select-body">
<view class="jnpf-tree-select-title">
<view class="icon-ym icon-ym-report-icon-preview-pagePre u-font-40 backIcon" @tap="close()"></view>
<view class="title">选择提醒</view>
</view>
<view class="jnpf-tree-select-search">
<u-search :placeholder="$t('app.apply.pleaseKeyword')" v-model="keyword" height="72"
:show-action="false" @change="search()" bg-color="#f0f2f6" shape="square">
</u-search>
</view>
<view class="jnpf-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="list-box" v-if="list.length">
<radio-group class="list-item" :value="innerValue" v-for="(item,index) in list" :key="index"
@change="radioChange(item)">
<label class="radio-label">
<radio class="u-radio" :value="item.id" :checked="item.id === selectId" />
<view class="list-item-content u-line-1">{{item.fullName}}</view>
</label>
</radio-group>
</view>
<JnpfEmpty v-else></JnpfEmpty>
</scroll-view>
</view>
<!-- 底部按钮 -->
<view class="jnpf-bottom-actions">
<u-button class="buttom-btn" @click="close()">取消</u-button>
<u-button class="buttom-btn" type="primary" @click.stop="handleConfirm()">确定</u-button>
</view>
</view>
</u-popup>
</template>
<script>
const defaultProps = {
label: 'fullName',
value: 'id',
}
import {
getMsgTemplate
} from '@/api/portal/portal.js'
export default {
props: {
selectType: {
type: String,
default: 'all'
},
clearable: {
type: Boolean,
default: false
},
query: {
type: Object,
default: () => ({})
},
// 是否显示边框
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: 99999
},
safeAreaInsetBottom: {
type: Boolean,
default: false
},
// 是否允许通过点击遮罩关闭Picker
maskCloseAble: {
type: Boolean,
default: true
},
//多选
multiple: {
type: Boolean,
default: false
},
// 顶部标题
title: {
type: String,
default: ''
},
// 取消按钮的文字
cancelText: {
type: String,
default: '取消'
},
// 确认按钮的文字
confirmText: {
type: String,
default: '确认'
},
selectedId: {
type: String,
default: ''
},
},
data() {
return {
keyword: '',
selectList: [],
list: [],
pagination: {
currentPage: 1,
pageSize: 20,
messageSource: 4
},
total: 0,
triggered: false,
innerValue: '',
selectId: this.selectedId,
showPopup: false
};
},
watch: {
modelValue: {
immediate: true,
handler(val) {
this.showPopup = val
}
},
},
computed: {
uZIndex() {
// 如果用户有传递z-index值优先使用
return this.zIndex ? this.zIndex : this.$u.zIndex.popup;
},
realProps() {
return {
...defaultProps,
}
}
},
methods: {
cleanAll() {
this.selectList = [];
},
radioChange(item) {
this.selectId = item.id;
this.innerValue = item.id;
this.selectList = item
},
search() {
this.searchTimer && clearTimeout(this.searchTimer)
this.searchTimer = setTimeout(() => {
this.resetData()
}, 300)
},
resetData() {
this.list = []
this.pagination = {
currentPage: 1,
pageSize: 20,
messageSource: 4
}
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,
messageSource: this.pagination.messageSource
}
this.loading = false
getMsgTemplate(query).then(res => {
const list = res.data.list;
this.list = this.list.concat(list);
this.pagination = res.data.pagination
this.total = this.pagination.total
let item = this.list.filter(o => o.id == this.selectId)[0]
if (item) this.selectList = item
})
},
handleConfirm() {
this.keyword = '';
this.$emit('confirm', this.selectList);
this.close();
},
close() {
this.$emit('close', false);
},
}
};
</script>

View File

@@ -0,0 +1,86 @@
<template>
<view class="jnpf-tree-select">
<u-input input-align='right' type="select" :select-open="selectShow" v-model="innerValue"
:placeholder="placeholder" @click="openSelect" />
<Select v-model="selectShow" :selectedId="selectedId" ref="userTree" @close="handleClose()"
@confirm="handleConfirm" />
</view>
</template>
<script>
import Select from './Select.vue';
import {
getMsgTemplate
} from '@/api/portal/portal.js'
import {
login
} from '@/api/common';
export default {
components: {
Select
},
props: {
modelValue: {
default: ''
},
send: {
type: String,
default: ''
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
sendName: {
type: String,
default: '请选择'
},
},
data() {
return {
selectShow: false,
innerValue: '',
selectedId: '',
}
},
watch: {
send: {
handler(val) {
if (!val) return this.innerValue = ''
this.setDefault(val)
},
immediate: true
}
},
methods: {
setDefault(id) {
this.innerValue = this.modelValue
this.selectedId = id
},
openSelect() {
if (this.disabled) return
this.selectShow = true
this.$refs.userTree.resetData()
},
handleConfirm(e) {
this.selectedId = e.id;
this.defaultValue = e.id
this.innerValue = e.fullName
this.$emit('update:modelValue', e.id)
this.$emit('change', e.id, e.fullName)
},
handleClose() {
this.selectShow = false
}
}
}
</script>
<style lang="scss" scoped>
.jnpf-tree-select {
width: 100%;
}
</style>

View File

@@ -0,0 +1,375 @@
<template>
<view class="jnpf-dateTime">
<u-input input-align='right' type="select" :select-open="selectShow" v-model="innerValue"
:placeholder="placeholder" @click="openSelect" :disabled="disabled">
</u-input>
<u-popup v-model="selectShow" mode="bottom" @click="colse()">
<view class="t-pop" @tap.stop>
<view class="pop-main">
<view class="top">
<view class="top-l">
<view @click="changeSwp('1')" :style="{color:sindex==1?'#1E79FF':'#333333'}">
<text>{{checkyear}}</text>
</view>
<view @click="changeSwp('2')" :style="{color:sindex==2?'#1E79FF':'#333333'}"
v-if="allDay==0">
<text>{{checkhour}}:{{checkminute}}</text>
</view>
</view>
<view class="top-r" @click="onOK()">
<text>确定</text>
</view>
</view>
<swiper class="swiper" circular :current-item-id="sindex">
<swiper-item :item-id="'1'">
<view class="mid">
<scroll-view scroll-y="true" style="height: 960rpx" @scrolltolower="tolower">
<uni-calendar ref="calendar" :insert="insert" :lunar='lunar'
@change='calendarChange' :date='today' />
</scroll-view>
</view>
</swiper-item>
<swiper-item :item-id="'2'" v-if="allDay==0">
<picker-view :indicator-style="indicatorStyle" :value="swiperTime" @change="bindChange"
class="picker-view">
<picker-view-column>
<view class="item" v-for="(v,i) in 24" :key="i">{{i<10?'0'+i:i}}</view>
</picker-view-column>
<picker-view-column>
<view class="item" v-for="(v,i) in 12" :key="i">{{i<2?'0'+i*5:i*5}}
</view>
</picker-view-column>
</picker-view>
</swiper-item>
</swiper>
</view>
</view>
</u-popup>
</view>
</template>
<script>
export default {
name: "t-datetime",
props: {
type: {
type: Number,
default: 0 //默认时间后推0分钟
},
allDay: {
type: Number,
default: 0
},
modelValue: {
type: String,
default: ''
},
date: {
type: Object,
default: () => {}
},
placeholder: {
type: String,
default: '请选择'
},
delayMin: {
type: Number,
default: 0 //默认时间后推0分钟
},
disabled: {
type: Boolean,
default: false
},
canToday: { //是否可选择当天之前的时间
type: Boolean,
default: false
},
},
data() {
return {
textList: ['日', '一', '二', '三', '四', '五', '六'],
mList: [],
checkyear: 0,
checkmonth: 0,
checkdate: 0,
checkhour: 0,
checkminute: 0,
indicatorStyle: `height: 50px;`,
sindex: '1',
nowYear: 0,
nowMonth: 0,
nowDate: 0,
lunar: false,
insert: true,
innerValue: '',
selectShow: false,
swiperTime: [],
year: 0,
hours: '',
minutes: '',
today: ''
};
},
watch: {
allDay(val) {
let allTime = this.date.year + '-' + this.date.month + '-' + this.date.date
this.today = allTime
let srt = this.time(allTime)
this.innerValue = ''
if (srt) {
this.innerValue = srt + this.date.month + '月' + this.date.date + '日'
} else {
if (this.date.year == this.year) {
this.innerValue = this.date.month + '月' + this.date.date + '日'
} else {
this.innerValue = this.date.year + '年' + this.date.month + '月' + this.date.date + '日'
}
}
if (this.allDay == 0) {
this.innerValue = this.innerValue + ' ' + this.hours + ':' +
this.minutes
}
},
date(val) {
this.init()
},
},
created() {
this.timestampToTime(+new Date())
this.init()
},
methods: {
tolower() {
this.init()
},
openSelect() {
if (this.disabled) return
this.selectShow = true
this.init()
},
calendarChange(e) {
this.date.year = e.year
this.date.month = e.month
this.date.date = e.date
this.date.date = this.date.date < 10 ? '0' + Number(this.date.date) : this.date.date
this.date.month = this.date.month < 10 ? '0' + Number(this.date.month) : this.date.month
this.checkyear = this.year == this.date.year ? this.date.month + '月' + this.date.date + '日' : this.date
.year +
'年' + this.date.month + '月' + this.date.date + '日'
this.sindex = '2'
},
init() {
this.innerValue = ''
this.today = this.date.year + '-' + this.date.month + '-' + this.date.date
this.date.minutes = this.date.minutes < 10 ? '0' + Number(this.date.minutes) : this.date.minutes
this.date.hours = this.date.hours < 10 ? '0' + Number(this.date.hours) : this.date.hours
this.date.date = this.date.date < 10 ? '0' + Number(this.date.date) : this.date.date
this.date.month = this.date.month < 10 ? '0' + Number(this.date.month) : this.date.month
this.checkyear = this.year == this.date.year ? this.date.month + '月' + this.date.date + '日' : this.date
.year + '年' + this.date.month + '月' + this.date.date + '日'
this.checkhour = this.date.hours
this.checkminute = this.date.minutes < 10 ? '0' + Number(this.date.minutes) : this.date.minutes
let checkminute = this.date.minutes / 5
this.swiperTime = [Number(this.checkhour), checkminute]
this.hours = this.date.hours
this.minutes = this.date.minutes
let allTime = this.date.year + '-' + this.date.month + '-' + this.date.date
let srt = this.time(allTime)
if (srt) {
this.innerValue = srt + this.date.month + '月' + this.date.date + '日'
} else {
if (this.date.year == this.year) {
this.innerValue = this.date.month + '月' + this.date.date + '日'
} else {
this.innerValue = this.date.year + '年' + this.date.month + '月' + this.date.date + '日'
}
}
if (this.allDay == 0) {
this.innerValue = this.innerValue + ' ' + this.hours + ':' +
this.checkminute
}
},
colse() {
this.selectShow = false
},
bindChange(e) {
const val = e.detail.value
this.swiperTime = [val[0], val[1]]
this.checkhour = Number(val[0]) < 10 ? '0' + val[0] : val[0]
this.checkminute = val[1] == '天' ? '00' : val[1] < 2 ? '0' + val[1] * 5 : val[1] * 5
},
changeSwp(i) {
this.sindex = i
},
onOK() {
if (this.allDay == 1) {
this.date.hours = '00'
this.date.minutes = '00'
} else {
this.date.hours = this.checkhour
this.date.minutes = this.checkminute
}
this.selectShow = false
let allTime = this.date.year + '-' + this.date.month + '-' + this.date.date
let srt = this.time(allTime)
if (srt) {
this.innerValue = srt + this.date.month + '月' + this.date.date + '日'
if (this.allDay == 0) {
this.innerValue = this.innerValue + ' ' + this.date.hours +
':' + this.date.minutes
}
} else {
if (this.date.year == this.year) {
this.innerValue = this.date.month + '月' + this.date.date + '日'
} else {
this.innerValue = this.date.year + '年' + this.date.month + '月' + this.date.date + '日'
}
if (this.allDay == 0) {
this.innerValue = this.innerValue + ' ' + this.date.hours +
':' + this.date.minutes
}
}
this.selectShow = false
this.$emit('confirm', this.date, this.type)
},
timestampToTime(timestamp) {
var date = new Date(timestamp);
this.year = date.getFullYear();
},
time(date) {
if (this.date.year != this.year) return false
let time_str = "";
if (new Date(date).getDate() === new Date().getDate()) {
time_str = "今天 · ";
} else if (new Date(date).getDate() === (new Date().getDate() - 1)) {
time_str = "昨天 · ";
} else if (new Date(date).getDate() === (new Date().getDate() + 1)) {
time_str = "明天 · ";
} else if (new Date(date).getDate() < new Date().getDate()) {
time_str = "";
}
return time_str;
}
}
}
</script>
<style lang="scss" scoped>
.jnpf-dateTime {
width: 100%;
:deep(.u-drawer) {
z-index: 999 !important;
}
}
:deep(.uni-calendar-item__weeks-box-item) {
line-height: 36rpx;
}
.t-pop {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
.pop-main {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
background-color: #fff;
border-radius: 24px;
height: 900rpx;
width: 100%;
}
}
.swiper {
height: 840rpx;
width: 100vw;
}
.top {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
margin: 20rpx 0;
.top-l {
display: flex;
flex-direction: row;
margin-left: 30rpx;
}
.top-r {
margin-right: 30rpx;
color: #1E79FF;
}
}
.calendar {
display: flex;
flex-wrap: wrap;
flex-direction: row;
align-items: center;
width: 100vw;
position: relative;
}
.ca-top {
width: 14.2vw;
display: flex;
justify-content: center;
align-items: center;
height: 66rpx;
z-index: 10;
}
.cell {
width: 60rpx;
height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
align-content: center;
border-radius: 30rpx;
}
.cell-active {
background-color: #1E79FF;
color: #fff;
}
.cabg {
display: flex;
justify-content: center;
width: 100vw;
font-size: 180rpx;
color: beige;
position: absolute;
z-index: 9;
}
.picker-view {
width: 750rpx;
height: 600rpx;
margin-top: 20rpx;
}
.item {
height: 50px;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Some files were not shown because too many files have changed in this diff Show More