init
This commit is contained in:
255
src/views/workflow/form/ComponentsConfigExport.js
Normal file
255
src/views/workflow/form/ComponentsConfigExport.js
Normal file
@@ -0,0 +1,255 @@
|
||||
import {ScaleToOriginal,EditPen,More,Edit,Tickets,Warning,CircleCheck,Money,FolderChecked,Calendar,Picture,User,SetUp,Star,FolderOpened } from '@element-plus/icons-vue'
|
||||
|
||||
|
||||
export const ValueType = {
|
||||
string: 'String',
|
||||
object: 'Object',
|
||||
array: 'Array',
|
||||
number: 'Number',
|
||||
date: 'Date',
|
||||
user: 'User',
|
||||
dept: 'Dept',
|
||||
star: 'star',
|
||||
dateRange: 'DateRange'
|
||||
}
|
||||
|
||||
export const baseComponents = [
|
||||
{
|
||||
name: '布局',
|
||||
components: [
|
||||
{
|
||||
title: '分栏布局',
|
||||
name: 'SpanLayout',
|
||||
icon: 'ScaleToOriginal',
|
||||
value: [],
|
||||
valueType: ValueType.array,
|
||||
props: {
|
||||
items: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}, {
|
||||
name: '基础组件',
|
||||
components: [
|
||||
{
|
||||
title: '单行文本输入',
|
||||
name: 'TextInput',
|
||||
icon: 'EditPen',
|
||||
value: '',
|
||||
valueType: ValueType.string,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '多行文本输入',
|
||||
name: 'TextareaInput',
|
||||
icon: 'More',
|
||||
value: '',
|
||||
valueType: ValueType.string,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '数字输入框',
|
||||
name: 'NumberInput',
|
||||
icon: 'Edit',
|
||||
value: '',
|
||||
valueType: ValueType.number,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '金额输入框',
|
||||
name: 'AmountInput',
|
||||
icon: 'Money',
|
||||
value: '',
|
||||
valueType: ValueType.number,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
showChinese: true
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '单选框',
|
||||
name: 'SelectInput',
|
||||
icon: 'CircleCheck',
|
||||
value: '',
|
||||
valueType: ValueType.string,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
expanding: false,
|
||||
options: ['选项1', '选项2']
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '多选框',
|
||||
name: 'MultipleSelect',
|
||||
icon: 'FolderChecked',
|
||||
value: [],
|
||||
valueType: ValueType.array,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
expanding: false,
|
||||
options: ['选项1', '选项2']
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '日期时间点',
|
||||
name: 'DateTime',
|
||||
icon: 'Calendar',
|
||||
value: '',
|
||||
valueType: ValueType.date,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '日期时间区间',
|
||||
name: 'DateTimeRange',
|
||||
icon: 'Calendar',
|
||||
valueType: ValueType.dateRange,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
placeholder: ['开始时间', '结束时间'],
|
||||
format: 'YYYY-MM-DD HH:mm',
|
||||
showLength: false,
|
||||
length: 0
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '上传图片',
|
||||
name: 'ImageUpload',
|
||||
icon: 'Picture',
|
||||
value: [],
|
||||
valueType: ValueType.array,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
maxSize: 5, //图片最大大小MB
|
||||
maxNumber: 10, //最大上传数量
|
||||
enableZip: true, //图片压缩后再上传
|
||||
placeholder: '请选择图片',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '上传附件',
|
||||
name: 'FileUpload',
|
||||
icon: 'FolderOpened',
|
||||
value: [],
|
||||
valueType: ValueType.array,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
onlyRead: false, //是否只读,false只能在线预览,true可以下载
|
||||
maxSize: 100, //文件最大大小MB
|
||||
maxNumber: 10, //最大上传数量
|
||||
fileTypes: [], //限制文件上传类型,
|
||||
placeholder: '请选择附件',
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '人员选择',
|
||||
name: 'UserPicker',
|
||||
icon: 'User' ,
|
||||
value: [],
|
||||
valueType: ValueType.user,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
multiple: false
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '部门选择',
|
||||
name: 'DeptPicker',
|
||||
icon: 'SetUp',
|
||||
value: [],
|
||||
valueType: ValueType.dept,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
multiple: false
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '评分',
|
||||
name: 'RatePicker',
|
||||
icon: 'Star',
|
||||
value: '',
|
||||
valueType: ValueType.star,
|
||||
props: {
|
||||
color: '#f0a732',
|
||||
max: 5,
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
showScore: true,
|
||||
enableHalf: true,
|
||||
placeholder: undefined,
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '说明文字',
|
||||
name: 'Description',
|
||||
icon: 'Warning',
|
||||
value: '',
|
||||
valueType: ValueType.string,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true
|
||||
}
|
||||
},
|
||||
]
|
||||
}, {
|
||||
name: '扩展组件',
|
||||
components: [
|
||||
{
|
||||
title: '明细表',
|
||||
name: 'TableList',
|
||||
icon: 'Tickets',
|
||||
value: [],
|
||||
valueType: ValueType.array,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
showBorder: true,
|
||||
rowLayout: true,
|
||||
showSummary: false,
|
||||
summaryColumns: [],
|
||||
maxSize: 0, //最大条数,为0则不限制
|
||||
columns: [] //列设置
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '签名',
|
||||
name: 'SignPanel',
|
||||
icon: 'EditPen',
|
||||
value: [],
|
||||
valueType: ValueType.string,
|
||||
props: {
|
||||
required: false,
|
||||
enablePrint: true,
|
||||
isCrop: true,
|
||||
lineColor: '#ff0000',
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
export default {
|
||||
baseComponents
|
||||
}
|
||||
|
||||
89
src/views/workflow/form/FormComponentConfig.vue
Normal file
89
src/views/workflow/form/FormComponentConfig.vue
Normal file
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form label-width="90px" v-if="selectFormItem.name !== 'SpanLayout'">
|
||||
<el-form-item label="表单ID">
|
||||
<el-input size="small" clearable v-model="selectFormItem.id" disabled/>
|
||||
</el-form-item>
|
||||
<el-form-item label="表单名称">
|
||||
<el-input size="small" clearable v-model="selectFormItem.title"/>
|
||||
</el-form-item>
|
||||
<component :is="selectFormItem.name" v-model="selectFormItem.props"/>
|
||||
<el-form-item label="必填项">
|
||||
<el-switch v-model="selectFormItem.props.required"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="可打印">
|
||||
<el-switch v-model="selectFormItem.props.enablePrint"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-empty v-else description="当前组件不支持配置"></el-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TextInput from './config/TextInputConfig.vue'
|
||||
import NumberInput from './config/NumberInputConfig.vue'
|
||||
import AmountInput from './config/AmountInputConfig.vue'
|
||||
import TextareaInput from './config/TextareaInputConfig.vue'
|
||||
import MultipleSelect from './config/SelectInputConfig.vue'
|
||||
import SelectInput from './config/SelectInputConfig.vue'
|
||||
import DateTime from './config/DateTimeConfig.vue'
|
||||
import DateTimeRange from './config/DateTimeRangeConfig.vue'
|
||||
import ImageUpload from './config/ImageUploadConfig.vue'
|
||||
import FileUpload from './config/FileUploadConfig.vue'
|
||||
import Description from './config/DescriptionConfig.vue'
|
||||
import MoneyInput from './config/MoneyInputConfig.vue'
|
||||
import DeptPicker from './config/OrgPickerConfig.vue'
|
||||
import UserPicker from './config/OrgPickerConfig.vue'
|
||||
import TableList from './config/TableListConfig.vue'
|
||||
import SignPanel from './config/SignPanelConfig.vue'
|
||||
import RatePicker from './config/RatePickerConfig.vue'
|
||||
import {computed,ref} from "vue";
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
const processStore = useProcessStore()
|
||||
// const component = ref()
|
||||
|
||||
const selectFormItem = computed(()=>{
|
||||
return processStore.getSelectedFormItem()
|
||||
})
|
||||
|
||||
// const init = () => {
|
||||
// console.log(processStore.getSelectedFormItem().name)
|
||||
// switch (processStore.getSelectedFormItem().name) {
|
||||
// case 'TextInput':
|
||||
// component.value = TextInput;
|
||||
// break;
|
||||
// case 'TextareaInput':
|
||||
// component.value = TextareaInput;
|
||||
// break;
|
||||
// case 'NumberInput':
|
||||
// component.value = NumberInput;
|
||||
// break;
|
||||
// case 'AmountInput':
|
||||
// component.value = AmountInput;
|
||||
// break;
|
||||
// case 'SelectInput':
|
||||
// component.value = SelectInput;
|
||||
// break;
|
||||
// case 'MultipleSelect':
|
||||
// component.value = MultipleSelect;
|
||||
// break;
|
||||
// case 'DateTime':
|
||||
// component.value = DateTime;
|
||||
// break;
|
||||
// case 'DateTimeRange':
|
||||
// component.value = DateTimeRange;
|
||||
// break;
|
||||
// case 'ImageUpload':
|
||||
// component.value = ImageUpload;
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
// init()
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
596
src/views/workflow/form/FormDesign.vue
Normal file
596
src/views/workflow/form/FormDesign.vue
Normal file
@@ -0,0 +1,596 @@
|
||||
<template>
|
||||
<div class="form-design">
|
||||
<!--左侧组件库-->
|
||||
<div style="width: 350px">
|
||||
<div class="components-nav">
|
||||
<span @click="libSelect = 0">组件库</span>
|
||||
</div>
|
||||
<div style="overflow-y: auto;height:calc(100vh - 172px);">
|
||||
<div class="components" v-for="(item, i) in baseComponents" :key="i">
|
||||
<p>{{ item.name }}</p>
|
||||
<ul>
|
||||
<draggable
|
||||
class="drag"
|
||||
:list="item.components"
|
||||
:group="{ name: 'form', pull: 'clone', put: false }"
|
||||
itemKey="name"
|
||||
@start="isStart = true" @end="isStart = false" :clone="clone"
|
||||
>
|
||||
<template #item="{ element, index }">
|
||||
<div class="list-group-item">
|
||||
<li :key="id">
|
||||
<el-icon>
|
||||
<component :is="element.icon"/>
|
||||
</el-icon>
|
||||
<span>{{ element.title }}</span>
|
||||
</li>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--中间编辑区-->
|
||||
<div class="layout-main">
|
||||
<div class="tool-nav">
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="撤销" placement="bottom-start">
|
||||
<el-icon>
|
||||
<RefreshLeft/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="恢复" placement="bottom-start">
|
||||
<el-icon>
|
||||
<RefreshRight/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div>
|
||||
<el-tooltip class="item" effect="dark" content="预览表单" placement="bottom-start">
|
||||
<el-icon @click="viewForms">
|
||||
<View/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="移动端" placement="bottom-start">
|
||||
<el-icon :class="{'select': showMobile}" @click="showMobile = true">
|
||||
<Cellphone/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
<el-tooltip class="item" effect="dark" content="PC端" placement="bottom-start">
|
||||
<el-icon :class="{'select': !showMobile}" @click="showMobile = false">
|
||||
<Monitor/>
|
||||
</el-icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="work-form">
|
||||
<div :class="{'mobile': showMobile, 'pc': !showMobile}">
|
||||
<div :class="{'bd': showMobile}">
|
||||
<div :class="{'form-content': showMobile}">
|
||||
<div class="form">
|
||||
<div class="tip" v-show="formItems.length === 0 && !isStart">👈 请在左侧选择控件并拖至此处</div>
|
||||
<draggable class="drag-from" :list="formItems" group="form"
|
||||
:options="{animation: 300, chosenClass:'choose', sort:true}"
|
||||
@start="drag = true; selectFormItem = null" @end="drag = false">
|
||||
<template #item="{ element, index }">
|
||||
<div class="form-item" @click="selectItem(element)"
|
||||
:style="getSelectedClass(element)">
|
||||
<div class="form-header">
|
||||
<p><span v-if="element.props.required">*</span>{{ element.title }}</p>
|
||||
<div class="option">
|
||||
<!--<i class="el-icon-copy-document" @click="copy"></i>-->
|
||||
<!-- <el-icon @click="copy">-->
|
||||
<!-- <CopyDocument/>-->
|
||||
<!-- </el-icon>-->
|
||||
<el-icon size="25px" @click="delItem(index)">
|
||||
<Close/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<form-design-render :config="element"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="layout-param" v-if="isShow">
|
||||
<div class="tool-nav-r" v-if="selectFormItem">
|
||||
<i :class="selectFormItem.icon" style="margin-right: 5px; font-size: medium"></i>
|
||||
<span>{{ selectFormItem.title }}</span>
|
||||
</div>
|
||||
<div v-if="!selectFormItem || forms.length === 0" class="tip">
|
||||
😀 选中控件后在这里进行编辑
|
||||
</div>
|
||||
<div style="text-align:left; padding: 10px" v-else>
|
||||
<form-component-config/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="viewFormVisible" title="表单预览" width="800px">
|
||||
<el-radio-group v-model="formPreviewModel" @change="formPreviewModelChange">
|
||||
<el-radio-button label="E">编辑</el-radio-button>
|
||||
<el-radio-button label="R">查看</el-radio-button>
|
||||
</el-radio-group>
|
||||
<form-render v-if="viewFormVisibleRender" ref="form" :form-items="forms" v-model:value="formValue"
|
||||
:is-preview="true" :mode="formPreviewModel"/>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import draggable from 'vuedraggable';
|
||||
import FormRender from './FormRender.vue'
|
||||
import FormDesignRender from './FormDesignRender.vue'
|
||||
import FormComponentConfig from './FormComponentConfig.vue'
|
||||
import {ValueType} from "./ComponentsConfigExport";
|
||||
import {CopyDocument, Close} from '@element-plus/icons-vue'
|
||||
import {ref, computed, defineExpose} from 'vue'
|
||||
import {baseComponents} from './ComponentsConfigExport'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
|
||||
const processStore = useProcessStore()
|
||||
|
||||
const form = ref()
|
||||
const isStart = ref(false)
|
||||
const isShow = ref(true)
|
||||
const showMobile = ref(false)
|
||||
const formValue = ref({})
|
||||
const formPreviewModel = ref("E")
|
||||
const viewFormVisible = ref(false)
|
||||
const viewFormVisibleRender = ref(true)
|
||||
const forms = ref(processStore.getDesign().formItems)
|
||||
const formItems = computed(() => {
|
||||
return processStore.getDesign().formItems
|
||||
})
|
||||
const selectFormItem = computed({
|
||||
get() {
|
||||
return processStore.getSelectedFormItem()
|
||||
},
|
||||
set(val) {
|
||||
processStore.setSelectedFormItem(val)
|
||||
},
|
||||
})
|
||||
|
||||
const clone = (obj) => {
|
||||
obj.id = getId()
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
// const copy = (node, index) => {
|
||||
// console.log(node)
|
||||
// processStore.getDesign().formItems.push(index + 1, 0,{...node})
|
||||
// }
|
||||
|
||||
const formPreviewModelChange = () => {
|
||||
// 卸载
|
||||
// viewFormVisibleRender.value= false
|
||||
// nextTick(()=>{
|
||||
// // 加载
|
||||
// viewFormVisibleRender.value= true
|
||||
// })
|
||||
}
|
||||
const delItem = (index) => {
|
||||
ElMessageBox.confirm('删除组件将会连带删除包含该组件的条件以及相关设置,是否继续?', '提示', {
|
||||
confirmButtonText: '确 定',
|
||||
cancelButtonText: '取 消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
if (processStore.getDesign().formItems[index].name === 'SpanLayout') {
|
||||
//删除的是分栏则遍历删除分栏内所有子组件
|
||||
processStore.getDesign().formItems[index].props.items.forEach(item => {
|
||||
removeFormItemAbout(item)
|
||||
})
|
||||
processStore.getDesign().formItems[index].props.items.length = 0
|
||||
} else {
|
||||
removeFormItemAbout(processStore.getDesign().formItems[index])
|
||||
}
|
||||
processStore.getDesign().formItems.splice(index, 1)
|
||||
})
|
||||
}
|
||||
|
||||
const viewForms = () => {
|
||||
viewFormVisible.value = true
|
||||
formValue.value = {}
|
||||
}
|
||||
|
||||
const removeFormItemAbout = async (item) => {
|
||||
processStore.nodeMap.forEach(node => {
|
||||
//搜寻条件,进行移除
|
||||
if (node.type === 'CONDITION') {
|
||||
node.props.groups.forEach(group => {
|
||||
let i = group.cids.remove(item.id)
|
||||
if (i > -1) {
|
||||
//从子条件移除
|
||||
group.conditions.splice(i, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
//搜寻权限,进行移除
|
||||
if (node.type === 'ROOT' || node.type === 'APPROVAL' || node.type === 'CC') {
|
||||
node.props.formPerms.removeByKey('id', item.id)
|
||||
if (node.props.formUser === item.id) {
|
||||
node.props.formUser = ''
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getId = () => {
|
||||
return 'field' + (Math.floor(Math.random() * (99999 - 10000)) + 10000).toString()
|
||||
+ new Date().getTime().toString().substring(5);
|
||||
}
|
||||
const getSelectedClass = (element) => {
|
||||
return processStore.getSelectedFormItem() && processStore.getSelectedFormItem().id === element.id ?
|
||||
'border-left: 4px solid #409eff' : ''
|
||||
}
|
||||
|
||||
const selectItem = (element) => {
|
||||
isShow.value = false
|
||||
selectFormItem.value = element
|
||||
nextTick(() => {
|
||||
isShow.value = true
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
const validateItem = (err, titleSet, item) => {
|
||||
if (titleSet.has(item.title) && item.name !== 'SpanLayout') {
|
||||
err.push(`表单 ${item.title} 名称重复`)
|
||||
}
|
||||
titleSet.add(item.title)
|
||||
if (item.name === 'SelectInput' || item.name === 'MultipleSelect') {
|
||||
if (item.props.options.length === 0) {
|
||||
err.push(`${item.title} 未设置选项`)
|
||||
}
|
||||
} else if (item.name === 'TableList') {
|
||||
if (item.props.columns.length === 0) {
|
||||
err.push(`明细表 ${item.title} 内未添加组件`)
|
||||
}
|
||||
} else if (item.name === 'SpanLayout') {
|
||||
if (item.props.items.length === 0) {
|
||||
err.push('分栏内未添加组件')
|
||||
} else {
|
||||
item.props.items.forEach(sub => validateItem(err, titleSet, sub))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const validate = () => {
|
||||
let err = []
|
||||
if (processStore.getDesign().formItems.length > 0) {
|
||||
let titleSet = new Set()
|
||||
processStore.getDesign().formItems.forEach(item => {
|
||||
//主要校验表格及分栏/选择器/表单名称/是否设置
|
||||
validateItem(err, titleSet, item)
|
||||
})
|
||||
} else {
|
||||
err.push('表单为空,请添加组件')
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
defineExpose({
|
||||
validate
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.form-design {
|
||||
display: flex;
|
||||
|
||||
}
|
||||
|
||||
.choose {
|
||||
border: 1px dashed #1890FF !important;
|
||||
}
|
||||
|
||||
.process-form {
|
||||
//.el-form-item__label {
|
||||
// padding: 0 0;
|
||||
//}
|
||||
}
|
||||
|
||||
.components-nav {
|
||||
box-sizing: content-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 12px 12px 0;
|
||||
height: 28px;
|
||||
box-shadow: 0 2px 4px 0 rgba(17, 31, 44, 0.04);
|
||||
border: 1px solid #ecedef;
|
||||
border-radius: 16px;
|
||||
background-color: #fff;
|
||||
|
||||
.selected {
|
||||
color: #1890FF;
|
||||
}
|
||||
|
||||
.border {
|
||||
border-left: 1px solid #f5f6f6;
|
||||
border-right: 1px solid #f5f6f6;
|
||||
}
|
||||
|
||||
span {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
color: rgba(17, 31, 44, 0.72);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #1890FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.components {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
//margin-top: 20px;
|
||||
//padding: 0 20px;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
color: rgba(17, 31, 44, 0.85);
|
||||
|
||||
& > p {
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.drag {
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
li {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 124px;
|
||||
height: 38px;
|
||||
margin-bottom: 12px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 8px;
|
||||
cursor: grab;
|
||||
background-color: #fff;
|
||||
|
||||
&:hover {
|
||||
border: 1px solid #1890FF;
|
||||
color: #1890FF;
|
||||
}
|
||||
|
||||
i {
|
||||
margin: 0 12px;
|
||||
}
|
||||
}
|
||||
|
||||
li:nth-child(odd) {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.layout-main {
|
||||
width: 80%;
|
||||
background-color: #feffff;
|
||||
|
||||
.tool-nav {
|
||||
font-size: medium;
|
||||
padding: 8px 20px;
|
||||
background: #fafafb;
|
||||
border-bottom: 1px solid #ebecee;
|
||||
|
||||
div:first-child {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
|
||||
i {
|
||||
margin-right: 10px
|
||||
}
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
float: right;
|
||||
|
||||
i {
|
||||
margin-left: 10px
|
||||
}
|
||||
}
|
||||
|
||||
i {
|
||||
color: #7a7a7a;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #4b4b4b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.work-form {
|
||||
margin: 0 auto;
|
||||
//height: calc(100% - 38px);
|
||||
overflow-y: auto;
|
||||
background: rgb(245, 246, 246);
|
||||
border-left: 1px solid rgb(235, 236, 238);
|
||||
border-right: 1px solid rgb(235, 236, 238);
|
||||
margin-right: -1px;
|
||||
|
||||
.pc {
|
||||
margin-top: 4%;
|
||||
padding: 0 150px;
|
||||
|
||||
.drag-from {
|
||||
height: calc(100vh - 190px);
|
||||
background-color: rgb(245, 246, 246);
|
||||
|
||||
.form-item, li {
|
||||
cursor: grab;
|
||||
background: #ffffff;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebecee;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: 360px;
|
||||
max-height: 640px;
|
||||
margin-top: 4%;
|
||||
border-radius: 24px;
|
||||
box-shadow: 0 8px 40px 0 rgba(17, 31, 44, 0.12);
|
||||
|
||||
.bd {
|
||||
border: 1px solid rgba(17, 31, 44, 0.08);
|
||||
border-radius: 24px;
|
||||
padding: 10px 10px;
|
||||
background-color: #ffffff;
|
||||
|
||||
.form-content {
|
||||
padding: 3px 2px;
|
||||
border-radius: 14px;
|
||||
background-color: #f2f4f5;
|
||||
|
||||
.drag-from {
|
||||
width: 100%;
|
||||
height: calc(100vh - 190px);
|
||||
min-height: 200px;
|
||||
max-height: 600px;
|
||||
}
|
||||
|
||||
.form {
|
||||
overflow-y: auto;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
max-height: 640px;
|
||||
|
||||
.form-item, li {
|
||||
border: 1px solid #ffffff;
|
||||
list-style: none;
|
||||
background: #ffffff;
|
||||
padding: 10px;
|
||||
margin: 5px 0;
|
||||
cursor: grab;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tip {
|
||||
//float: left;
|
||||
margin: 0 auto;
|
||||
width: 65%;
|
||||
max-width: 400px;
|
||||
padding: 35px 20px;
|
||||
border-radius: 10px;
|
||||
border: 1px dashed rgba(25, 31, 37, 0.12);
|
||||
margin-top: 50px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: rgb(122, 122, 122);
|
||||
z-index: 9999;
|
||||
|
||||
&:hover {
|
||||
border: 1px dashed #1890FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.layout-param {
|
||||
width: 300px;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
color: rgb(122, 122, 122);
|
||||
|
||||
.tool-nav-r {
|
||||
text-align: left;
|
||||
font-size: small;
|
||||
border-left: 1px solid #ebecee;
|
||||
padding: 8px 20px;
|
||||
background: #fafafb;
|
||||
border-bottom: 1px solid #ebecee;
|
||||
}
|
||||
|
||||
.tip {
|
||||
margin-top: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
.flip-list-move {
|
||||
transition: transform 0.5s;
|
||||
}
|
||||
|
||||
.no-move {
|
||||
transition: transform 0s;
|
||||
}
|
||||
|
||||
.select {
|
||||
color: #4b4b4b !important;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
font-size: small;
|
||||
color: #818181;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
p {
|
||||
position: relative;
|
||||
margin: 0 0 10px 0;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 3px;
|
||||
color: rgb(217, 0, 19);
|
||||
}
|
||||
}
|
||||
|
||||
.option {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
|
||||
i {
|
||||
font-size: large;
|
||||
cursor: pointer;
|
||||
color: #8c8c8c;
|
||||
padding: 5px;
|
||||
|
||||
&:hover {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
border-radius: 16px;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
</style>
|
||||
135
src/views/workflow/form/FormDesignRender.vue
Normal file
135
src/views/workflow/form/FormDesignRender.vue
Normal file
@@ -0,0 +1,135 @@
|
||||
<template>
|
||||
<component ref="form" :is="com" :config="config" :mode="mode" :perm="perm" @update="update" v-model:value="_value"
|
||||
v-bind="config.props"/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TextInput from './components/TextInput.vue'
|
||||
import TextareaInput from './components/TextareaInput.vue'
|
||||
import NumberInput from './components/NumberInput.vue'
|
||||
import AmountInput from './components/AmountInput.vue'
|
||||
import SelectInput from './components/SelectInput.vue'
|
||||
import MultipleSelect from './components/MultipleSelect.vue'
|
||||
import DateTime from './components/DateTime.vue'
|
||||
import DateTimeRange from './components/DateTimeRange.vue'
|
||||
import ImageUpload from './components/ImageUpload.vue'
|
||||
import FileUpload from './components/FileUpload.vue'
|
||||
import SpanLayout from './components/SpanLayout.vue'
|
||||
import UserPicker from './components/UserPicker.vue'
|
||||
import DeptPicker from './components/DeptPicker.vue'
|
||||
import RatePicker from './components/RatePicker.vue'
|
||||
import Description from './components/Description.vue'
|
||||
import TableList from './components/TableList.vue'
|
||||
import SignPanel from './components/SignPanel.vue'
|
||||
import {defineProps, defineEmits, computed} from 'vue'
|
||||
|
||||
const emit = defineEmits()
|
||||
const com = ref()
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
},
|
||||
config: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
})
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
|
||||
// const _value = ref(props.value)
|
||||
watch(() => props.value, (value) => {
|
||||
_value.value = value
|
||||
})
|
||||
watch(() => props.config, (value) => {
|
||||
init(value.name)
|
||||
})
|
||||
watch(() => props.perm, (value) => {
|
||||
props.perm=value
|
||||
})
|
||||
const init = (value) => {
|
||||
switch (value) {
|
||||
case 'TextInput':
|
||||
com.value = TextInput;
|
||||
break;
|
||||
case 'TextareaInput':
|
||||
com.value = TextareaInput;
|
||||
break;
|
||||
case 'NumberInput':
|
||||
com.value = NumberInput;
|
||||
break;
|
||||
case 'AmountInput':
|
||||
com.value = AmountInput;
|
||||
break;
|
||||
case 'SelectInput':
|
||||
com.value = SelectInput;
|
||||
break;
|
||||
case 'MultipleSelect':
|
||||
com.value = MultipleSelect;
|
||||
break;
|
||||
case 'DateTime':
|
||||
com.value = DateTime;
|
||||
break;
|
||||
case 'DateTimeRange':
|
||||
com.value = DateTimeRange;
|
||||
break;
|
||||
case 'ImageUpload':
|
||||
com.value = ImageUpload;
|
||||
break;
|
||||
case 'FileUpload':
|
||||
com.value = FileUpload;
|
||||
break;
|
||||
case 'SpanLayout':
|
||||
com.value = SpanLayout;
|
||||
break;
|
||||
case 'UserPicker':
|
||||
com.value = UserPicker;
|
||||
break;
|
||||
case 'DeptPicker':
|
||||
com.value = DeptPicker;
|
||||
break;
|
||||
case 'RatePicker':
|
||||
com.value = RatePicker;
|
||||
break;
|
||||
case 'Description':
|
||||
com.value = Description;
|
||||
break;
|
||||
case 'TableList':
|
||||
com.value = TableList;
|
||||
break;
|
||||
case 'SignPanel':
|
||||
com.value = SignPanel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
init(props.config.name)
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
130
src/views/workflow/form/FormRender.vue
Normal file
130
src/views/workflow/form/FormRender.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<el-form v-if="showForm" ref="formRenderView" class="process-form" label-position="top" :rules="rules"
|
||||
:model="_value">
|
||||
<div v-for="(item, index) in formItems" :key="item.name + index">
|
||||
<el-form-item v-if="item.name !== 'SpanLayout' && item.name !== 'Description'"
|
||||
:prop="item.id" :label="item.title">
|
||||
<!-- {{_value[item.id]}}-->
|
||||
<form-design-render :ref="`sub-item_${item.id}`" :perm="mode" v-model:value="_value[item.id]" mode="PC"
|
||||
:config="item"/>
|
||||
</el-form-item>
|
||||
<form-design-render ref="span-layout" v-else :perm="mode" v-model:value="_value" mode="PC" :config="item"/>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import FormDesignRender from './FormDesignRender.vue'
|
||||
import {getCurrentInstance} from '@vue/runtime-core';
|
||||
import {defineProps, defineEmits} from 'vue'
|
||||
|
||||
const currentInstance = getCurrentInstance()
|
||||
|
||||
const emit = defineEmits()
|
||||
const showForm = ref(false)
|
||||
const formRenderView = ref()
|
||||
const rules = ref({})
|
||||
|
||||
const props = defineProps({
|
||||
formItems: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: () => {
|
||||
return "R"
|
||||
}
|
||||
},
|
||||
isPreview: {
|
||||
type: Boolean,
|
||||
default: () => {
|
||||
return false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
// const _value = ref(props.value)
|
||||
watch(() => props.value, (value) => {
|
||||
_value.value = value
|
||||
})
|
||||
watch(() => props.mode, (value) => {
|
||||
props.mode = value
|
||||
})
|
||||
|
||||
const validate = (call) => {
|
||||
let success = true
|
||||
formRenderView.value.validate(valid => {
|
||||
success = valid
|
||||
if (valid) {
|
||||
//校验成功再校验内部
|
||||
for (let i = 0; i < props.formItems.length; i++) {
|
||||
if (props.formItems[i].name === 'TableList') {
|
||||
// let formRef = currentInstance.ctx.$refs[`sub-item_${this.formItems[i].id}`]
|
||||
// if (formRef && Array.isArray(formRef) && formRef.length > 0) {
|
||||
// formRef[0].validate(subValid => {
|
||||
// success = subValid
|
||||
// })
|
||||
// if (!success) {
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
call(success)
|
||||
});
|
||||
}
|
||||
|
||||
const loadFormConfig = (formItems) => {
|
||||
formItems.forEach(item => {
|
||||
if (item.name === 'SpanLayout') {
|
||||
loadFormConfig(item.props.items)
|
||||
} else {
|
||||
_value.value[item.id] = props.value[item.id]
|
||||
// this.$set(_value, item.id, props.value[item.id])
|
||||
if (props.isPreview) {
|
||||
item['perm'] = props.mode
|
||||
}
|
||||
if (item.perm === 'E') {
|
||||
if (item.props.required) {
|
||||
rules.value[item.id] = [{
|
||||
type: item.valueType === 'Array' ? 'array' : undefined,
|
||||
required: true,
|
||||
message: `请填写${item.title}`, trigger: 'blur'
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
loadFormConfig(props.formItems)
|
||||
|
||||
defineExpose({
|
||||
validate
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
179
src/views/workflow/form/components/AmountInput.vue
Normal file
179
src/views/workflow/form/components/AmountInput.vue
Normal file
@@ -0,0 +1,179 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-input size="medium" disabled :placeholder="placeholder"/>
|
||||
<div style="margin-top: 15px" v-show="showChinese">
|
||||
<span>大写:</span>
|
||||
<span class="chinese">{{chinese}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-input-number style="width: 100%;" :min="0" controls-position="right" :precision="precision" size="medium" clearable v-model="_value" :placeholder="placeholder"/>
|
||||
<div v-show="showChinese">
|
||||
<span>大写:</span>
|
||||
<span class="chinese">{{chinese}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div style="display: flex;">
|
||||
<span>{{_value}}</span>
|
||||
<span v-show="showChinese" style="margin-left: 30px;">
|
||||
<span>大写:</span>
|
||||
<span class="chinese">{{chinese}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入金额'
|
||||
},
|
||||
showChinese: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
precision: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
// emit('update:value', value)
|
||||
})
|
||||
|
||||
const chinese = computed(()=>{
|
||||
return convertCurrency(_value.value)
|
||||
})
|
||||
|
||||
const convertCurrency = (money) => {
|
||||
//汉字的数字
|
||||
const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
//基本单位
|
||||
const cnIntRadice = ['', '拾', '佰', '仟'];
|
||||
//对应整数部分扩展单位
|
||||
const cnIntUnits = ['', '万', '亿', '兆'];
|
||||
//对应小数部分单位
|
||||
const cnDecUnits = ['角', '分', '毫', '厘'];
|
||||
//整数金额时后面跟的字符
|
||||
const cnInteger = '整';
|
||||
//整型完以后的单位
|
||||
const cnIntLast = '元';
|
||||
//最大处理的数字
|
||||
let maxNum = 999999999999999.9999;
|
||||
//金额整数部分
|
||||
let integerNum;
|
||||
//金额小数部分
|
||||
let decimalNum;
|
||||
//输出的中文金额字符串
|
||||
let chineseStr = '';
|
||||
//分离金额后用的数组,预定义
|
||||
let parts;
|
||||
if (money === '') {
|
||||
return '';
|
||||
}
|
||||
money = parseFloat(money);
|
||||
if (money >= maxNum) {
|
||||
//超出最大处理数字
|
||||
return '';
|
||||
}
|
||||
if (money === 0) {
|
||||
chineseStr = cnNums[0] + cnIntLast + cnInteger;
|
||||
return chineseStr;
|
||||
}
|
||||
//转换为字符串
|
||||
money = money.toString();
|
||||
if (money.indexOf('.') === -1) {
|
||||
integerNum = money;
|
||||
decimalNum = '';
|
||||
} else {
|
||||
parts = money.split('.');
|
||||
integerNum = parts[0];
|
||||
decimalNum = parts[1].substr(0, 4);
|
||||
}
|
||||
//获取整型部分转换
|
||||
if (parseInt(integerNum, 10) > 0) {
|
||||
var zeroCount = 0;
|
||||
var IntLen = integerNum.length;
|
||||
for (let i = 0; i < IntLen; i++) {
|
||||
let n = integerNum.substr(i, 1);
|
||||
let p = IntLen - i - 1;
|
||||
let q = p / 4;
|
||||
let m = p % 4;
|
||||
if (n == '0') {
|
||||
zeroCount++;
|
||||
} else {
|
||||
if (zeroCount > 0) {
|
||||
chineseStr += cnNums[0];
|
||||
}
|
||||
//归零
|
||||
zeroCount = 0;
|
||||
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
|
||||
}
|
||||
if (m == 0 && zeroCount < 4) {
|
||||
chineseStr += cnIntUnits[q];
|
||||
}
|
||||
}
|
||||
chineseStr += cnIntLast;
|
||||
}
|
||||
//小数部分
|
||||
if (decimalNum !== '') {
|
||||
let decLen = decimalNum.length;
|
||||
for (let i = 0; i < decLen; i++) {
|
||||
let n = decimalNum.substr(i, 1);
|
||||
if (n !== '0') {
|
||||
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chineseStr === '') {
|
||||
chineseStr += cnNums[0] + cnIntLast + cnInteger;
|
||||
} else if (decimalNum === '') {
|
||||
chineseStr += cnInteger;
|
||||
}
|
||||
return chineseStr;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chinese{
|
||||
color: #afadad;
|
||||
font-size: smaller;
|
||||
}
|
||||
.el-input__inner{
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
176
src/views/workflow/form/components/AmountInputback.vue
Normal file
176
src/views/workflow/form/components/AmountInputback.vue
Normal file
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
{{value}}-----------------
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-input size="medium" disabled :placeholder="placeholder"/>
|
||||
<div style="margin-top: 15px" v-show="showChinese">
|
||||
<span>大写:</span>
|
||||
<span class="chinese">{{chinese}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-input-number style="width: 100%;" :min="0" controls-position="right" :precision="precision" size="medium" clearable v-model="_value" :placeholder="placeholder"/>
|
||||
<div v-show="showChinese">
|
||||
<span>大写:</span>
|
||||
<span class="chinese">{{chinese}}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div style="display: flex;">
|
||||
<span>{{_value}}</span>
|
||||
<span v-show="showChinese" style="margin-left: 30px;">
|
||||
<span>大写:</span>
|
||||
<span class="chinese">{{chinese}}</span>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入金额'
|
||||
},
|
||||
showChinese: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
precision: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
console.log(val,"组件")
|
||||
emit("update:value", val);
|
||||
}
|
||||
})
|
||||
|
||||
const chinese = computed(()=>{
|
||||
return convertCurrency(props.value)
|
||||
})
|
||||
|
||||
const convertCurrency = (money) => {
|
||||
console.log("zhuanhuan ",money)
|
||||
//汉字的数字
|
||||
const cnNums = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
|
||||
//基本单位
|
||||
const cnIntRadice = ['', '拾', '佰', '仟'];
|
||||
//对应整数部分扩展单位
|
||||
const cnIntUnits = ['', '万', '亿', '兆'];
|
||||
//对应小数部分单位
|
||||
const cnDecUnits = ['角', '分', '毫', '厘'];
|
||||
//整数金额时后面跟的字符
|
||||
const cnInteger = '整';
|
||||
//整型完以后的单位
|
||||
const cnIntLast = '元';
|
||||
//最大处理的数字
|
||||
let maxNum = 999999999999999.9999;
|
||||
//金额整数部分
|
||||
let integerNum;
|
||||
//金额小数部分
|
||||
let decimalNum;
|
||||
//输出的中文金额字符串
|
||||
let chineseStr = '';
|
||||
//分离金额后用的数组,预定义
|
||||
let parts;
|
||||
if (money === '') {
|
||||
return '';
|
||||
}
|
||||
money = parseFloat(money);
|
||||
if (money >= maxNum) {
|
||||
//超出最大处理数字
|
||||
return '';
|
||||
}
|
||||
if (money === 0) {
|
||||
chineseStr = cnNums[0] + cnIntLast + cnInteger;
|
||||
return chineseStr;
|
||||
}
|
||||
//转换为字符串
|
||||
money = money.toString();
|
||||
if (money.indexOf('.') === -1) {
|
||||
integerNum = money;
|
||||
decimalNum = '';
|
||||
} else {
|
||||
parts = money.split('.');
|
||||
integerNum = parts[0];
|
||||
decimalNum = parts[1].substr(0, 4);
|
||||
}
|
||||
//获取整型部分转换
|
||||
if (parseInt(integerNum, 10) > 0) {
|
||||
var zeroCount = 0;
|
||||
var IntLen = integerNum.length;
|
||||
for (let i = 0; i < IntLen; i++) {
|
||||
let n = integerNum.substr(i, 1);
|
||||
let p = IntLen - i - 1;
|
||||
let q = p / 4;
|
||||
let m = p % 4;
|
||||
if (n == '0') {
|
||||
zeroCount++;
|
||||
} else {
|
||||
if (zeroCount > 0) {
|
||||
chineseStr += cnNums[0];
|
||||
}
|
||||
//归零
|
||||
zeroCount = 0;
|
||||
chineseStr += cnNums[parseInt(n)] + cnIntRadice[m];
|
||||
}
|
||||
if (m == 0 && zeroCount < 4) {
|
||||
chineseStr += cnIntUnits[q];
|
||||
}
|
||||
}
|
||||
chineseStr += cnIntLast;
|
||||
}
|
||||
//小数部分
|
||||
if (decimalNum !== '') {
|
||||
let decLen = decimalNum.length;
|
||||
for (let i = 0; i < decLen; i++) {
|
||||
let n = decimalNum.substr(i, 1);
|
||||
if (n !== '0') {
|
||||
chineseStr += cnNums[Number(n)] + cnDecUnits[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chineseStr === '') {
|
||||
chineseStr += cnNums[0] + cnIntLast + cnInteger;
|
||||
} else if (decimalNum === '') {
|
||||
chineseStr += cnInteger;
|
||||
}
|
||||
return chineseStr;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.chinese{
|
||||
color: #afadad;
|
||||
font-size: smaller;
|
||||
}
|
||||
.el-input__inner{
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
69
src/views/workflow/form/components/DateTime.vue
Normal file
69
src/views/workflow/form/components/DateTime.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'" >
|
||||
<el-date-picker size="medium" disabled :type="type" :placeholder="placeholder"></el-date-picker>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-date-picker v-model="_value" :value-format="format" size="medium" clearable :type="type" :placeholder="placeholder"></el-date-picker>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
{{_value}}
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
format:{
|
||||
type: String,
|
||||
default: 'YYYY-MM-DD HH:mm'
|
||||
},
|
||||
placeholder:{
|
||||
type: String,
|
||||
default: '请选择日期时间'
|
||||
}
|
||||
})
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
const type = computed(()=>{
|
||||
switch (props.format){
|
||||
case 'YYYY': return 'year';
|
||||
case 'YYYY-MM': return 'month';
|
||||
case 'YYYY-MM-DD': return 'date';
|
||||
case 'YYYY-MM-DD HH:mm': return 'datetime';
|
||||
default: return 'datetime';
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
136
src/views/workflow/form/components/DateTimeRange.vue
Normal file
136
src/views/workflow/form/components/DateTimeRange.vue
Normal file
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-date-picker size="medium" v-model="_value" disabled :type="type" :start-placeholder="placeholder[0]"
|
||||
:end-placeholder="placeholder[1]"/>
|
||||
<div v-if="showLength" class="length">
|
||||
<span>时长:</span>
|
||||
<span>{{ timeLength }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-date-picker v-model="_value" size="medium" clearable :value-format="format" :type="type"
|
||||
:start-placeholder="placeholder[0]" :end-placeholder="placeholder[1]"/>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<!-- <div v-if="type==='daterange'">-->
|
||||
<!-- <span style="float:left;">{{_value[0]}}</span>-->
|
||||
<!-- <span style="padding:0 9px">至</span>-->
|
||||
<!-- <span>{{ _value[1] }}</span>-->
|
||||
<!-- </div>-->
|
||||
<div>
|
||||
<span style="float:left;">{{ _value[0] }}</span>
|
||||
<span style="padding:0 9px">至</span>
|
||||
<span>{{ _value[1] }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<div v-if="showLength" class="length">
|
||||
<span>时长:</span>
|
||||
<span>{{timeLengthFiled}}</span>
|
||||
<!-- <span>{{type==='daterange'? parseInt(timeLength)+1: timeLength }}</span>-->
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
// import {timeLength} from '../utils/date'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: "E"
|
||||
},
|
||||
format: {
|
||||
type: String,
|
||||
default: "YYYY-MM-DD HH:mm"
|
||||
},
|
||||
placeholder: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return ["开始时间", "结束时间"];
|
||||
}
|
||||
},
|
||||
showLength: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
length: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const type = computed(()=>{
|
||||
switch (props.format) {
|
||||
case "YYYY-MM-DD":
|
||||
return "daterange";
|
||||
case "YYYY-MM-DD HH:mm":
|
||||
return "datetimerange";
|
||||
default:
|
||||
return "daterange";
|
||||
}
|
||||
})
|
||||
|
||||
const timeLengthFiled = computed(()=>{
|
||||
//求时长算法
|
||||
if (Array.isArray(this.value)) {
|
||||
// let start = moment(this.value[0]).format(this.format.replaceAll("dd", "DD"));
|
||||
// let end = moment(this.value[1]).format(this.format.replaceAll("dd", "DD"));
|
||||
if (this.value[0] === this.value[1]) {
|
||||
return "0 (时长为0,请确认)";
|
||||
}
|
||||
return "先选择时间哦";
|
||||
// return timeLength(this.value[0],this.value[1])
|
||||
} else {
|
||||
return "先选择时间哦";
|
||||
}
|
||||
})
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.length {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.length:nth-child(2) {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.el-date-editor--datetimerange.el-input__inner {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
</style>
|
||||
99
src/views/workflow/form/components/DeptPicker.vue
Normal file
99
src/views/workflow/form/components/DeptPicker.vue
Normal file
@@ -0,0 +1,99 @@
|
||||
<template>
|
||||
<div style="max-width: 350px">
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-button disabled icon="SetUp" type="primary" size="mini" round> 选择部门</el-button>
|
||||
<span class="placeholder"> {{ placeholder }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-button icon="SetUp" type="primary" size="mini" round @click="selectDept"> 选择部门</el-button>
|
||||
<org-picker type="dept" :multiple="multiple" ref="deptPicker" :v-model="deptList" @ok="selected"/>
|
||||
<span class="placeholder"> {{ placeholder }}</span>
|
||||
<div style="margin-top: 5px">
|
||||
<el-tag size="mini" style="margin: 5px" closable v-for="(dept, i) in deptList" :key="i" @close="delDept(i)">
|
||||
{{ dept.label }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<el-tag size="mini" style="margin: 5px" v-for="(dept, i) in deptList" :key="i" >
|
||||
{{ dept.label }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import OrgPicker from "../../process/common/DeptPicker.vue";
|
||||
import {computed, defineProps} from "vue";
|
||||
|
||||
const showOrgSelect = ref(false)
|
||||
const deptPicker = ref()
|
||||
const deptList = ref([])
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择部门'
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
})
|
||||
const emit = defineEmits(["input"])
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
emit("input", val);
|
||||
}
|
||||
})
|
||||
|
||||
const selectDept = () => {
|
||||
//弹出部门选择器,选择部门
|
||||
deptPicker.value.showDeptPicker()
|
||||
}
|
||||
const selected = (select) => {
|
||||
let userInfoList = []
|
||||
for (let val of select) {
|
||||
let userInfo = {
|
||||
value: val.value,
|
||||
label: val.label
|
||||
}
|
||||
userInfoList.push(userInfo)
|
||||
}
|
||||
deptList.value = userInfoList
|
||||
}
|
||||
const delDept = (i) => {
|
||||
deptList.value.splice(i, 1)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.placeholder {
|
||||
margin-left: 10px;
|
||||
color: #adabab;
|
||||
font-size: smaller;
|
||||
}
|
||||
</style>
|
||||
29
src/views/workflow/form/components/Description.vue
Normal file
29
src/views/workflow/form/components/Description.vue
Normal file
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div :style="{'color': color}">
|
||||
<el-icon><Warning/></el-icon>
|
||||
<span> {{ placeholder }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#868686'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '只是一段说明文字'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
104
src/views/workflow/form/components/FileUpload.vue
Normal file
104
src/views/workflow/form/components/FileUpload.vue
Normal file
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-button size="small" icon="Link" round>选择文件</el-button>
|
||||
<ellipsis :row="1" :content="placeholder + sizeTip" hoverTip slot="tip"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-upload :file-list="_value" action="#" :limit="maxSize" with-credentials :multiple="maxSize > 0"
|
||||
:data="uploadParams"
|
||||
:auto-upload="false" :before-upload="beforeUpload">
|
||||
<el-button size="small" icon="Link" round>选择文件</el-button>
|
||||
<ellipsis :row="1" :content="placeholder + sizeTip" hoverTip slot="tip"/>
|
||||
</el-upload>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
{{ _value }}
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,computed,ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
import Ellipsis from '../../process/common/Ellipsis.vue'
|
||||
const disabled = ref(false)
|
||||
const uploadParams = ref({})
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择附件'
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
maxNumber: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
fileTypes: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(["input"])
|
||||
const sizeTip = computed(() => {
|
||||
if (props.fileTypes.length > 0) {
|
||||
return ` | 只允许上传[${String(props.fileTypes).replaceAll(",", "、")}]格式的文件,且单个附件不超过${props.maxSize}MB`
|
||||
}
|
||||
return props.maxSize > 0 ? ` | 单个附件不超过${props.maxSize}MB` : ''
|
||||
|
||||
})
|
||||
const _value = computed( {
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
emit("input", val);
|
||||
}
|
||||
})
|
||||
|
||||
const beforeUpload = (file) => {
|
||||
const alows = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'];
|
||||
if (alows.indexOf(file.type) === -1) {
|
||||
ElMessage.warning("存在不支持的图片格式")
|
||||
} else if (this.maxSize > 0 && file.size / 1024 / 1024 > this.maxSize) {
|
||||
ElMessage.warning(`单张图片最大不超过 ${this.maxSize}MB`)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
const handleRemove = (file, fileList) => {
|
||||
console.log(file, fileList);
|
||||
}
|
||||
const handlePictureCardPreview = (file) => {
|
||||
console.log(file)
|
||||
}
|
||||
const handleDownload = (file) => {
|
||||
console.log(file)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
207
src/views/workflow/form/components/ImageUpload.vue
Normal file
207
src/views/workflow/form/components/ImageUpload.vue
Normal file
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<div class="design">
|
||||
<el-icon><Plus /></el-icon>
|
||||
</div>
|
||||
<p>{{ placeholder }} {{ sizeTip }}</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-upload :file-list="_value"
|
||||
:limit="maxSize" with-credentials
|
||||
:multiple="maxSize > 0"
|
||||
:data="uploadParams"
|
||||
list-type="picture-card"
|
||||
:action="uploadFileUrl"
|
||||
:headers="headers"
|
||||
:auto-upload="true"
|
||||
:on-success="handleUploadSuccess"
|
||||
:before-upload="beforeUpload">
|
||||
<i slot="default" class="el-icon-plus"></i>
|
||||
<div slot="file" slot-scope="{file}">
|
||||
<img class="el-upload-list__item-thumbnail" :src="file.url" alt="">
|
||||
<label class="el-upload-list__item-status-label">
|
||||
<i class="el-icon-upload-success el-icon-check"></i>
|
||||
</label>
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
|
||||
<i class="el-icon-zoom-in"></i>
|
||||
</span>
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleDownload(file)">
|
||||
<i class="el-icon-download"></i>
|
||||
</span>
|
||||
<span v-if="!disabled" class="el-upload-list__item-delete" @click="handleRemove(file)">
|
||||
<i class="el-icon-delete"></i>
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div slot="tip">{{ placeholder }} {{ sizeTip }}</div>
|
||||
</el-upload>
|
||||
<el-dialog :visible.sync="dialogVisible" center append-to-body>
|
||||
<div>
|
||||
<img width="100%" :src="dialogImageUrl" alt="" style="z-index: 3435">
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div v-for="(item , index) in _value" :key="index">
|
||||
<el-image
|
||||
style="width: 100px; height: 100px"
|
||||
:src="item.url"
|
||||
:preview-src-list="[item.url]">
|
||||
</el-image>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {deleteFile} from '@/api/workflow/process-file.js'
|
||||
import {defineProps, defineEmits} from 'vue'
|
||||
import { getToken } from '@/utils/auth'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
|
||||
const baseURL = import.meta.env.VITE_BASE_URL
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择图片'
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 5
|
||||
},
|
||||
maxNumber: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
enableZip: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
const fileList = ref([])
|
||||
const disabled = ref(false)
|
||||
const uploadFileUrl = ref(baseURL + "/workflow/process/file")
|
||||
const headers = reactive({
|
||||
authorization: getToken()
|
||||
})
|
||||
|
||||
const uploadParams = ref([])
|
||||
const dialogImageUrl = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const imageUrl = ref([])
|
||||
|
||||
|
||||
const sizeTip = computed(() => {
|
||||
return props.maxSize > 0 ? `| 每张图不超过${props.maxSize}MB` : ''
|
||||
})
|
||||
|
||||
|
||||
const init = () => {
|
||||
fileList.value = _value
|
||||
}
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
const beforeUpload = (file) => {
|
||||
const alows = ['image/jpeg', 'image/png', 'image/gif', 'image/jpg'];
|
||||
if (alows.indexOf(file.type) === -1) {
|
||||
ElMessage.warning("存在不支持的图片格式")
|
||||
} else if (props.maxSize > 0 && file.size / 1024 / 1024 > props.maxSize) {
|
||||
ElMessage.warning(`单张图片最大不超过 ${props.maxSize}MB`)
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const handleUploadSuccess = (res, file) => {
|
||||
if (res.code !== 1000) {
|
||||
ElMessage.error("上传失败")
|
||||
}
|
||||
let data = res.data
|
||||
fileList.value.push(data)
|
||||
emit("input", fileList)
|
||||
console.log(res, file, fileList)
|
||||
}
|
||||
|
||||
const handleRemove = (file) => {
|
||||
deleteFile(file.id).then(res => {
|
||||
if (res.code === 1000) {
|
||||
ElMessage.success("删除成功")
|
||||
fileList.value.splice(fileList.value().findIndex((item) => item.id === file.id), 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handlePictureCardPreview = (file) => {
|
||||
dialogVisible.value = true;
|
||||
dialogImageUrl.value = file.url;
|
||||
}
|
||||
|
||||
const handleDownload = (file) => {
|
||||
console.log(file);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.design {
|
||||
i {
|
||||
padding: 10px;
|
||||
font-size: xx-large;
|
||||
background: white;
|
||||
border: 1px dashed #8c8c8c;
|
||||
}
|
||||
}
|
||||
|
||||
.el-upload--picture-card {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
line-height: 87px;
|
||||
}
|
||||
|
||||
.el-upload-list__item {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
|
||||
.el-upload-list__item-actions {
|
||||
& > span + span {
|
||||
margin: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
80
src/views/workflow/form/components/MultipleSelect.vue
Normal file
80
src/views/workflow/form/components/MultipleSelect.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-select class="max-fill" v-if="!expanding" size="medium" multiple v-model="_value" disabled :placeholder="placeholder" filterable/>
|
||||
<el-checkbox-group v-else v-model="_value">
|
||||
<el-checkbox disabled v-for="(op, index) in options" :key="index" :label="op">{{op}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-select class="max-fill" v-if="!expanding" v-model="_value" multiple size="medium" clearable :placeholder="placeholder" filterable>
|
||||
<el-option v-for="(op, index) in options" :key="index" :value="op" :label="op"></el-option>
|
||||
</el-select>
|
||||
<el-checkbox-group v-else v-model="_value">
|
||||
<el-checkbox v-for="(op, index) in options" :key="index" :label="op">{{op}}</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div v-for="(item,index) in _value" :key="index" >
|
||||
<span style="float:left;padding-right: 9px">{{item}}</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
placeholder:{
|
||||
type: String,
|
||||
default: '请选择选项'
|
||||
},
|
||||
value:{
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
expanding:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
options:{
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
61
src/views/workflow/form/components/NumberInput.vue
Normal file
61
src/views/workflow/form/components/NumberInput.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-input-number size="medium" disabled :placeholder="placeholder"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-input-number style="width: 100%;" controls-position="right" v-model="_value" size="medium" clearable :placeholder="placeholder"/>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<span>{{_value}}</span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value:{
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder:{
|
||||
type: String,
|
||||
default: '请输入数值'
|
||||
}
|
||||
})
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-input__inner{
|
||||
text-align: left;
|
||||
}
|
||||
</style>
|
||||
90
src/views/workflow/form/components/RatePicker.vue
Normal file
90
src/views/workflow/form/components/RatePicker.vue
Normal file
@@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-rate
|
||||
disabled
|
||||
:show-score="showScore"
|
||||
:allow-half="enableHalf"
|
||||
:text-color="color"
|
||||
text-color="#ff9900"
|
||||
score-template="{value}">
|
||||
</el-rate>
|
||||
<p>{{ placeholder }}</p>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-rate
|
||||
v-model="star"
|
||||
:show-score="showScore"
|
||||
:allow-half="enableHalf"
|
||||
:text-color="color"
|
||||
score-template="{value}">
|
||||
</el-rate>
|
||||
<p>{{ placeholder }}</p>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<el-rate
|
||||
v-model="star"
|
||||
disabled
|
||||
:show-score="showScore"
|
||||
:allow-half="enableHalf"
|
||||
:text-color="color"
|
||||
score-template="{value}">
|
||||
</el-rate>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, defineEmits, defineProps} from "vue";
|
||||
const emit = defineEmits(["input"])
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#f0a732'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请打分!'
|
||||
},
|
||||
enableHalf: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
showScore: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
})
|
||||
const star=ref(null)
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
emit("input", val);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
</style>
|
||||
75
src/views/workflow/form/components/SelectInput.vue
Normal file
75
src/views/workflow/form/components/SelectInput.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-select class="max-fill" v-if="!expanding" size="medium" v-model="_value" disabled :placeholder="placeholder" filterable/>
|
||||
<el-radio-group v-model="_value" v-else>
|
||||
<el-radio disabled v-for="(op, index) in options" :key="index" :label="op">{{op}}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-select class="max-fill" v-if="!expanding" v-model="_value" size="medium" clearable :placeholder="placeholder" filterable>
|
||||
<el-option v-for="(op, index) in options" :key="index" :value="op" :label="op"></el-option>
|
||||
</el-select>
|
||||
<el-radio-group v-model="_value" v-else>
|
||||
<el-radio v-for="(op, index) in options" :key="index" :label="op">{{op}}</el-radio>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<span>{{ _value }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}, value:{
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder:{
|
||||
type: String,
|
||||
default: '请选择选项'
|
||||
},
|
||||
expanding:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
options:{
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
118
src/views/workflow/form/components/SignPanel.vue
Normal file
118
src/views/workflow/form/components/SignPanel.vue
Normal file
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-icon ><EditPen/></el-icon>
|
||||
请签名
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<div v-if="!_value">
|
||||
<el-button icon="EditPen" @click="doSign">请签字</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div>
|
||||
<el-image fit="contain" class="sign-border-preview" style="width: 200px; height: 100px" @click="doSign"
|
||||
:src="_value"/>
|
||||
</div>
|
||||
<div>
|
||||
点击签名重签
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div v-if="_value">
|
||||
<el-image fit="contain" style="width: 200px; height: 100px" :src="_value"/>
|
||||
</div>
|
||||
<div>
|
||||
请签名
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<el-dialog title="请使用鼠标签字" width="800px" :visible.sync="sign.open" @close="signClose" append-to-body>
|
||||
<div class="sign-border">
|
||||
<!-- <vue-esign v-if="sign.open" ref="sign" :width="800" :height="300" :isCrop="isCrop"-->
|
||||
<!-- :lineWidth="sign.lineWidth" :lineColor="sign.lineColor"-->
|
||||
<!-- :bgColor.sync="sign.bgColor"/>-->
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<el-button size="mini" @click="signClose">取消</el-button>
|
||||
<el-button size="mini" type="primary" @click="handleGenerate">确认</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import vueEsign from 'vue-esign'
|
||||
|
||||
import {computed, defineProps,ref} from "vue";
|
||||
// import EditTable from "../../../rapid/gen/editTable";
|
||||
|
||||
const emit = defineEmits(["input"])
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
isCrop: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
lineColor: {
|
||||
type: String,
|
||||
default: '#000000'
|
||||
}
|
||||
})
|
||||
const sign = ref({
|
||||
open: false,
|
||||
lineWidth: 6,
|
||||
bgColor: ''
|
||||
})
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(val) {
|
||||
emit("input", val);
|
||||
}
|
||||
})
|
||||
const signClose = () => {
|
||||
sign.value.open = false
|
||||
sign.value.reset()
|
||||
}
|
||||
const doSign = () => {
|
||||
sign.value.open = true
|
||||
}
|
||||
const handleGenerate = () => {
|
||||
sign.value.generate().then(res => {
|
||||
_value.value = res
|
||||
sign.value.open = false
|
||||
}).catch(err => {
|
||||
alert(err) // 画布没有签字时会执行这里 'Not Signned'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sign-border {
|
||||
border: 1px dashed #2b2b2b;
|
||||
}
|
||||
|
||||
.sign-border-preview {
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
|
||||
.sign-border-preview:hover {
|
||||
border: 1px dashed #1989fa;
|
||||
}
|
||||
</style>
|
||||
238
src/views/workflow/form/components/SpanLayout.vue
Normal file
238
src/views/workflow/form/components/SpanLayout.vue
Normal file
@@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<draggable class="l-drag-from" :list="_items" group="form"
|
||||
:options="{animation: 300, chosenClass:'choose', sort:true}"
|
||||
@start="spanLayoutStart" @end="drag = false">
|
||||
<template #item="{ element, index }">
|
||||
<div class="l-form-item" @click.stop="selectItem(element)"
|
||||
:style="getSelectedClass(element)">
|
||||
<div class="l-form-header">
|
||||
<p><span v-if="element.props.required">*</span>{{ element.title }}</p>
|
||||
<div class="l-option">
|
||||
<!--<i class="el-icon-copy-document" @click="copy"></i>-->
|
||||
<i class="el-icon-close" @click="delItem(index)"></i>
|
||||
</div>
|
||||
<form-design-render :config="element"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<div style="color: #c0bebe;text-align: center; width: 90%; padding: 5px;">☝ 拖拽控件到布局容器内部</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-row :gutter="20" v-for="(rows, rsi) in __items" :key="rsi + '_rows'">
|
||||
<el-col :span="24 / rows.length" v-for="(item, ri) in rows" :key="ri + '_row'">
|
||||
<el-form-item v-if="item.name !== 'SpanLayout' && item.name !== 'Description'" :prop="item.id"
|
||||
:label="item.title" :key="item.name + ri">
|
||||
<form-design-render v-model:value="_value[item.id]" :perm="item.perm" :mode="mode" :config="item"/>
|
||||
</el-form-item>
|
||||
<form-design-render v-else v-model:value="_value" :perm="item.perm" :mode="mode" :config="item"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import draggable from "vuedraggable";
|
||||
import FormDesignRender from '../FormDesignRender.vue'
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
|
||||
const processStore = useProcessStore()
|
||||
import {defineProps, defineEmits, computed} from 'vue'
|
||||
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
default: null
|
||||
},
|
||||
items: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const select = ref();
|
||||
const drag = ref(false);
|
||||
const formConfig = ref({
|
||||
//数据字段
|
||||
data: {},
|
||||
//校验规则
|
||||
rules: {}
|
||||
});
|
||||
const form = ref({
|
||||
processDefinitionKey: '',
|
||||
deploymentName: "",
|
||||
logo: {},
|
||||
formItems: [],
|
||||
process: {},
|
||||
remark: ""
|
||||
});
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(() => props.value, (value) => {
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
const _items = computed({
|
||||
get() {
|
||||
return props.items;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(() => props.items, (value) => {
|
||||
_items.value = value
|
||||
})
|
||||
|
||||
const __items = computed(() => {
|
||||
let result = []
|
||||
for (let i = 0; i < props.items.length; i++) {
|
||||
if (i > 0 && i % 2 > 0) {
|
||||
result.push([props.items[i - 1], props.items[i]])
|
||||
}
|
||||
}
|
||||
if (result.length * 2 < props.items.length) {
|
||||
result.push([props.items[props.items.length - 1]])
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
||||
|
||||
const selectFormItem = computed({
|
||||
get() {
|
||||
return processStore.getSelectedFormItem()
|
||||
},
|
||||
set(val) {
|
||||
processStore.setSelectedFormItem(val)
|
||||
},
|
||||
})
|
||||
|
||||
const spanLayoutStart = () => {
|
||||
drag.value = true;
|
||||
selectFormItem.value = null
|
||||
}
|
||||
const selectItem = (cp) => {
|
||||
selectFormItem.value = cp
|
||||
}
|
||||
const getSelectedClass = (cp) => {
|
||||
return processStore.getSelectedFormItem() && processStore.getSelectedFormItem().id === cp.id ?
|
||||
'border-left: 4px solid #f56c6c' : ''
|
||||
}
|
||||
|
||||
const delItem = (index) => {
|
||||
ElMessageBox.confirm('删除组件将会连带删除包含该组件的条件以及相关设置,是否继续?', '提示', {
|
||||
confirmButtonText: '确 定',
|
||||
cancelButtonText: '取 消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
if (_items.value[index].name === 'SpanLayout') {
|
||||
//删除的是分栏则遍历删除分栏内所有子组件
|
||||
_items.value[index].props.items.forEach(item => {
|
||||
removeFormItemAbout(item)
|
||||
})
|
||||
_items.value[index].props.items.length = 0
|
||||
} else {
|
||||
removeFormItemAbout(_items[index])
|
||||
}
|
||||
_items.value.splice(index, 1)
|
||||
})
|
||||
}
|
||||
const removeFormItemAbout = async (item) => {
|
||||
processStore.nodeMap.forEach(node => {
|
||||
//搜寻条件,进行移除
|
||||
if (node.type === 'CONDITION') {
|
||||
node.props.groups.forEach(group => {
|
||||
let i = group.cids.remove(item.id)
|
||||
if (i > -1) {
|
||||
//从子条件移除
|
||||
group.conditions.splice(i, 1)
|
||||
}
|
||||
})
|
||||
}
|
||||
//搜寻权限,进行移除
|
||||
if (node.type === 'ROOT' || node.type === 'APPROVAL' || node.type === 'CC') {
|
||||
node.props.formPerms.removeByKey('id', item.id)
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
|
||||
.choose {
|
||||
border: 1px dashed #1890FF !important;
|
||||
}
|
||||
|
||||
.l-drag-from {
|
||||
min-height: 50px;
|
||||
background-color: rgb(245, 246, 246);
|
||||
|
||||
.l-form-item, li {
|
||||
cursor: grab;
|
||||
background: #ffffff;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebecee;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.l-form-header {
|
||||
font-size: small;
|
||||
color: #818181;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
p {
|
||||
position: relative;
|
||||
margin: 0 0 10px 0;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 3px;
|
||||
color: rgb(217, 0, 19);
|
||||
}
|
||||
}
|
||||
|
||||
.l-option {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
|
||||
i {
|
||||
font-size: large;
|
||||
cursor: pointer;
|
||||
color: #8c8c8c;
|
||||
padding: 5px;
|
||||
|
||||
&:hover {
|
||||
color: #1890FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
373
src/views/workflow/form/components/TableList.vue
Normal file
373
src/views/workflow/form/components/TableList.vue
Normal file
@@ -0,0 +1,373 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<draggable class="l-drag-from" :list="_columns" group="form"
|
||||
:options="{animation: 300, chosenClass:'choose', sort:true}"
|
||||
@start="drag = true; selectFormItem = null" @end="drag = false">
|
||||
<template #item="{ element, index }">
|
||||
<div class="l-form-item" @click.stop="selectItem(element)"
|
||||
:style="getSelectedClass(element)">
|
||||
<div class="l-form-header">
|
||||
<p><span v-if="element.props.required">*</span>{{ element.title }}</p>
|
||||
<div class="l-option">
|
||||
<el-icon size="25px" @click="delItem(index)">
|
||||
<Close/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<form-design-render :config="element"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</draggable>
|
||||
<div style="color: #c0bebe;text-align: center; width: 90%; padding: 5px;">☝ 拖拽控件到表格内部</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<div v-if="rowLayout">
|
||||
<el-table size="medium" :header-cell-style="{background:'#f5f7fa', padding:'3px 0'}" :border="showBorder"
|
||||
:data="_value" style="width: 763px" v-tabh>
|
||||
<el-table-column fixed type="index" label="序号" width="55"></el-table-column>
|
||||
<el-table-column :min-width="getMinWidth(column)" v-for="(column, index) in _columns" :key="index"
|
||||
:prop="column.id" :label="column.title" >
|
||||
<template #default="scope">
|
||||
<form-design-render :class="{'valid-error': showError(column, _value[scope.$index][column.id])}"
|
||||
v-model:value="_value[scope.$index][column.id]" :mode="mode" :config="column"/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" min-width="90" label="操作">
|
||||
<template #default="scope">
|
||||
<el-button size="mini" type="primary" @click="copyData(scope.$index, scope.row)" link>复制</el-button>
|
||||
<el-button size="mini" type="primary" @click="delRow(scope.$index, scope.row)" link>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-button size="small" icon="Plus" @click="addRow">{{ placeholder }}</el-button>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-form :rules="rules" :model="row" :ref="`table-form-${i}`" class="table-column" v-for="(row, i) in _value"
|
||||
:key="i">
|
||||
<div class="table-column-action">
|
||||
<span>第 {{ i + 1 }} 项</span>
|
||||
<el-icon size="25px" @click="delRow(i, row)">
|
||||
<Close/>
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-form-item v-for="(column, index) in _columns" :key="'column_' + index" :prop="column.id"
|
||||
:label="column.title">
|
||||
<form-design-render v-model="row[column.id]" :mode="mode" :config="column"/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button size="small" icon="Plus" @click="addRow">{{ placeholder }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div v-if="rowLayout">
|
||||
<el-table size="medium" :header-cell-style="{background:'#f5f7fa', padding:'3px 0'}" :border="showBorder"
|
||||
:data="_value" style="width: 763px" v-tabh>
|
||||
<el-table-column fixed type="index" label="序号" width="50"></el-table-column>
|
||||
<el-table-column :min-width="getMinWidth(column)" v-for="(column, index) in _columns" :key="index"
|
||||
:prop="column.id" :label="column.title">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import draggable from "vuedraggable";
|
||||
import {ValueType} from "../ComponentsConfigExport";
|
||||
import FormDesignRender from '../FormDesignRender.vue'
|
||||
import {useProcessStore} from '@/stores/processStore.js'
|
||||
import {computed, defineEmits, defineExpose, defineProps, ref} from "vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const processStore = useProcessStore()
|
||||
const emit = defineEmits(["input"])
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '添加数据'
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
showBorder: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
maxSize: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
rowLayout: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
})
|
||||
const select = ref(null)
|
||||
const drag = ref(false)
|
||||
const _value=ref([])
|
||||
|
||||
const init = () => {
|
||||
if (!Array.isArray(props.value)) {
|
||||
_value.value = []
|
||||
}
|
||||
}
|
||||
// const _value = computed({
|
||||
// get() {
|
||||
// return props.value;
|
||||
// },
|
||||
// set(value) {
|
||||
// console.log('成都督导',value)
|
||||
// props.value=value
|
||||
// }
|
||||
// })
|
||||
// watch(() => _value.value, (value) => {
|
||||
// _value.value = value
|
||||
// })
|
||||
// const rules = computed({
|
||||
// const rules = {}
|
||||
// this.columns.forEach(col => {
|
||||
// if (col.props.required) {
|
||||
// rules[col.id] = [{
|
||||
// type: col.valueType === 'Array' ? 'array' : undefined,
|
||||
// required: true,
|
||||
// message: `请填写${col.title}`, trigger: 'blur'
|
||||
// }]
|
||||
// }
|
||||
// })
|
||||
// })
|
||||
const _columns = computed({
|
||||
get() {
|
||||
return props.columns;
|
||||
},
|
||||
set(val) {
|
||||
this.columns = val;
|
||||
}
|
||||
})
|
||||
const selectFormItem = computed({
|
||||
get() {
|
||||
return processStore.selectFormItem
|
||||
},
|
||||
set(val) {
|
||||
processStore.selectFormItem = val
|
||||
},
|
||||
})
|
||||
const getMinWidth = (col) => {
|
||||
switch (col.name) {
|
||||
case 'DateTime':
|
||||
return '250px'
|
||||
case 'DateTimeRange':
|
||||
return '280px'
|
||||
case 'MultipleSelect':
|
||||
return '200px'
|
||||
default:
|
||||
return '150px'
|
||||
}
|
||||
}
|
||||
const showError = (col, val) => {
|
||||
console.log('showError', val)
|
||||
if (col.props.required) {
|
||||
switch (col.valueType) {
|
||||
case ValueType.dept:
|
||||
case ValueType.user:
|
||||
case ValueType.dateRange:
|
||||
case ValueType.array:
|
||||
return !(Array.isArray(val) && val.length > 0)
|
||||
default:
|
||||
return !this.isNotEmpty(val)
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
const copyData = (i, row) => {
|
||||
// _value.value.push(this.deepCopy(row))
|
||||
}
|
||||
const delRow = (i) => {
|
||||
_value.value.splice(i, 1)
|
||||
}
|
||||
const addRow = () => {
|
||||
console.log('添加', props.maxSize, _value.value)
|
||||
if (props.maxSize > 0 && _value.value.length >= props.maxSize) {
|
||||
console.log('限制大小')
|
||||
ElMessage.warning(`最多只能添加${props.maxSize}行`)
|
||||
} else {
|
||||
let row = {}
|
||||
_columns.value.map(col => {
|
||||
row[col.id] = undefined
|
||||
})
|
||||
_value.value.push(row)
|
||||
console.log('_value', _value.value)
|
||||
}
|
||||
}
|
||||
const delItem = (id) => {
|
||||
_columns.value.splice(id, 1)
|
||||
}
|
||||
const selectItem = (cp) => {
|
||||
selectFormItem.value = cp
|
||||
}
|
||||
const getSelectedClass = (cp) => {
|
||||
return selectFormItem.value && selectFormItem.value.id === cp.id ? 'border-left: 4px solid #f56c6c' : ''
|
||||
}
|
||||
const validate = (call) => {
|
||||
if (props.rowLayout) {
|
||||
let result = true
|
||||
for (let i = 0; i < props.columns.length; i++) {
|
||||
if (props.columns[i].props.required) {
|
||||
for (let j = 0; j < _value.value.length; j++) {
|
||||
result = !showError(props.columns[i], _value.value[j][props.columns[i].id])
|
||||
if (!result) {
|
||||
call(false)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
call(result)
|
||||
} else {
|
||||
let success = 0
|
||||
_value.value.forEach((v, i) => {
|
||||
console.log("i", i);
|
||||
// let formRef = this.$refs[`table-form-${i}`]
|
||||
// if (formRef && Array.isArray(formRef) && formRef.length > 0) {
|
||||
// formRef[0].validate(valid => {
|
||||
// if (valid) {
|
||||
// success++;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
})
|
||||
if (success === _value.value.length) {
|
||||
call(true)
|
||||
} else {
|
||||
call(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
init()
|
||||
defineExpose({
|
||||
validate
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.valid-error {
|
||||
.el-input__inner {
|
||||
border-color: #F56C6C;
|
||||
}
|
||||
}
|
||||
|
||||
.choose {
|
||||
border: 1px dashed var(--theme-primary) !important;
|
||||
}
|
||||
|
||||
.table-column {
|
||||
padding: 5px;
|
||||
margin-bottom: 10px;
|
||||
border-left: 3px solid #409eff;
|
||||
border-radius: 5px;
|
||||
background: #fafafa;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 0;
|
||||
|
||||
.el-form-item__label {
|
||||
height: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
.table-column-action {
|
||||
float: right;
|
||||
|
||||
span {
|
||||
color: #afafaf;
|
||||
margin-right: 10px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
i {
|
||||
color: #afafaf;
|
||||
padding: 5px;
|
||||
font-size: large;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #666666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.l-drag-from {
|
||||
min-height: 50px;
|
||||
background-color: rgb(245, 246, 246);
|
||||
|
||||
.l-form-item, li {
|
||||
cursor: grab;
|
||||
background: #ffffff;
|
||||
padding: 10px;
|
||||
border: 1px solid #ebecee;
|
||||
margin: 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.l-form-header {
|
||||
font-size: small;
|
||||
color: #818181;
|
||||
text-align: left;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
p {
|
||||
position: relative;
|
||||
margin: 0 0 10px 0;
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
left: -8px;
|
||||
top: 3px;
|
||||
color: rgb(217, 0, 19);
|
||||
}
|
||||
}
|
||||
|
||||
.l-option {
|
||||
position: absolute;
|
||||
top: -10px;
|
||||
right: -10px;
|
||||
|
||||
i {
|
||||
font-size: large;
|
||||
cursor: pointer;
|
||||
color: #8c8c8c;
|
||||
padding: 5px;
|
||||
|
||||
&:hover {
|
||||
color: var(--theme-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
57
src/views/workflow/form/components/TextInput.vue
Normal file
57
src/views/workflow/form/components/TextInput.vue
Normal file
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-input size="medium" disabled :placeholder="placeholder"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-input style="width: 100%;" size="medium" clearable v-model="_value" :placeholder="placeholder"/>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<span>{{ _value }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: "E"
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入内容"
|
||||
}
|
||||
})
|
||||
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
58
src/views/workflow/form/components/TextareaInput.vue
Normal file
58
src/views/workflow/form/components/TextareaInput.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-input size="medium" disabled :placeholder="placeholder" show-word-limit :rows="2" type="textarea"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-input size="medium" v-model="_value" clearable :maxlength="255" :placeholder="placeholder" show-word-limit
|
||||
:rows="3" type="textarea"/>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<span>{{ _value }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {defineProps,defineEmits} from 'vue'
|
||||
const emit = defineEmits()
|
||||
|
||||
const props = defineProps({
|
||||
mode:{
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: null
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请输入内容'
|
||||
}
|
||||
})
|
||||
const _value = computed({
|
||||
get() {
|
||||
return props.value;
|
||||
},
|
||||
set(value) {
|
||||
emit('update:value', value)
|
||||
}
|
||||
})
|
||||
watch(()=>props.value,(value)=>{
|
||||
_value.value = value
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
143
src/views/workflow/form/components/UserPicker.vue
Normal file
143
src/views/workflow/form/components/UserPicker.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div style="max-width: 350px">
|
||||
<template v-if="mode === 'DESIGN'">
|
||||
<el-button disabled icon="User" type="primary" size="mini" round>选择人员</el-button>
|
||||
<span class="placeholder"> {{ placeholder }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="perm === 'E'">
|
||||
<el-button icon="User" type="primary" size="mini" round @click="chooseUser">
|
||||
选择人员
|
||||
</el-button>
|
||||
<user-picker :multiple="multiple" ref="userPicker" title="请选择人员" v-model:value="userList" @ok="selected"/>
|
||||
<span class="placeholder"> {{ placeholder }}</span>
|
||||
<div style="display: flex;">
|
||||
<div class="userStyle" v-for="(user, i) in userList" :key="i">
|
||||
<span @click="delDept(i)">×</span>
|
||||
<el-avatar :src="user.avatar"/>
|
||||
<el-tooltip class="item" effect="dark" :content="user.name" placement="bottom-start">
|
||||
<span>{{ user.name }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="perm === 'R'">
|
||||
<div style="display: flex;">
|
||||
<div v-for="(user, i) in userList" :key="i" class="view_user">
|
||||
<el-avatar :src="user.avatar"/>
|
||||
<el-tooltip effect="dark" :content="user.name" placement="bottom-start">
|
||||
<span>{{ user.name }}</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import UserPicker from "../../process/common/UserPicker.vue";
|
||||
import {computed, defineProps} from "vue";
|
||||
const emit = defineEmits(["input"])
|
||||
const showPickerSelect = ref(false)
|
||||
const userPicker = ref()
|
||||
const userList = ref([])
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'DESIGN'
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
perm: {
|
||||
type: String,
|
||||
default: 'E'
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: '请选择人员'
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
const chooseUser=()=>{
|
||||
userPicker.value.showUserPicker()
|
||||
}
|
||||
watch(() => props.perm, (newVal, oldVal) => {
|
||||
console.log('newVal',newVal,userPicker.value)
|
||||
});
|
||||
|
||||
const selected = (select) => {
|
||||
let userInfoList = []
|
||||
for (let val of select) {
|
||||
let userInfo = {
|
||||
id: val.id,
|
||||
name: val.name,
|
||||
avatar: val.avatar,
|
||||
}
|
||||
userInfoList.push(userInfo)
|
||||
}
|
||||
userList.value = userInfoList
|
||||
console.log('select',userList.value)
|
||||
}
|
||||
const delDept = (i) => {
|
||||
userList.value.splice(i, 1)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.placeholder {
|
||||
margin-left: 10px;
|
||||
color: #adabab;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.userStyle {
|
||||
width: 45px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
|
||||
span:first-child {
|
||||
position: absolute;
|
||||
right: -3px;
|
||||
top: -11px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
span:last-child {
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden
|
||||
}
|
||||
}
|
||||
|
||||
.view_user {
|
||||
width: 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-right: 10px;
|
||||
|
||||
span {
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden
|
||||
}
|
||||
}
|
||||
</style>
|
||||
37
src/views/workflow/form/config/AmountInputConfig.vue
Normal file
37
src/views/workflow/form/config/AmountInputConfig.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="保留小数">
|
||||
<el-input-number controls-position="right" :precision="0" :max="3" :min="0" size="small" v-model="value.precision" placeholder="小数位数"/>
|
||||
位
|
||||
</el-form-item>
|
||||
<el-form-item label="展示大写">
|
||||
<el-switch v-model="value.showChinese"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "AmountInputConfig",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
38
src/views/workflow/form/config/DateTimeConfig.vue
Normal file
38
src/views/workflow/form/config/DateTimeConfig.vue
Normal file
@@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置日期提示"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期格式">
|
||||
<el-select size="small" v-model="value.format" filterable>
|
||||
<el-option value="yyyy" label="年"></el-option>
|
||||
<el-option value="yyyy-MM" label="年-月"></el-option>
|
||||
<el-option value="yyyy-MM-dd" label="年-月-日"></el-option>
|
||||
<el-option value="yyyy-MM-dd HH:mm" label="年-月-日 时:分"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DateTime",
|
||||
components: {},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
58
src/views/workflow/form/config/DateTimeRangeConfig.vue
Normal file
58
src/views/workflow/form/config/DateTimeRangeConfig.vue
Normal file
@@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="placeholder[0]" @change="placeholderChange" placeholder="开始日期提示"/>
|
||||
<el-input size="small" v-model="placeholder[1]" @change="placeholderChange" placeholder="结束日期提示"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="日期格式">
|
||||
<el-select size="small" v-model="value.format" filterable>
|
||||
<el-option value="yyyy" label="年"></el-option>
|
||||
<el-option value="yyyy-MM" label="年-月"></el-option>
|
||||
<el-option value="yyyy-MM-dd" label="年-月-日"></el-option>
|
||||
<el-option value="yyyy-MM-dd HH:mm" label="年-月-日 时:分"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="展示时长">
|
||||
<el-switch v-model="value.showLength"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DateTimeRangeConfig",
|
||||
components: {},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
placeholder:[
|
||||
"开始时间","结束时间"
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log("出发了",this.value.placeholder,!this.value.placeholder)
|
||||
if (undefined !== this.value.placeholder){
|
||||
this.placeholder = this.value.placeholder
|
||||
}else {
|
||||
this.$set(this.value,"placeholder",this.placeholder)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
placeholderChange(){
|
||||
this.value.placeholder = this.placeholder
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
33
src/views/workflow/form/config/DescriptionConfig.vue
Normal file
33
src/views/workflow/form/config/DescriptionConfig.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示内容">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示内容"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="文字颜色">
|
||||
<el-color-picker v-model="value.color" size="medium"></el-color-picker>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Description",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
48
src/views/workflow/form/config/FileUploadConfig.vue
Normal file
48
src/views/workflow/form/config/FileUploadConfig.vue
Normal file
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量限制">
|
||||
<tip slot="label" content="限制最大上传图片数量(为0则不限制)">数量限制</tip>
|
||||
<el-input-number class="max-fill" controls-position="right" :precision="0" size="small" v-model="value.maxNumber" placeholder="最多上传几张图片"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="大小限制">
|
||||
<tip slot="label" content="限制单个文件最大大小-MB(为0则不限制)">大小限制</tip>
|
||||
<el-input-number class="max-fill" controls-position="right" :precision="1" size="small" v-model="value.maxSize" placeholder="单个文件最大大小"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="类型限制">
|
||||
<tip slot="label" content="限制上传文件的后缀类型">类型限制</tip>
|
||||
<el-select size="small" style="width: 100%;" v-model="value.fileTypes" multiple
|
||||
filterable allow-create default-first-option clearable placeholder="允许上传文件的后缀格式,可设置多种"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="不可下载">
|
||||
<el-switch v-model="value.onlyRead"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "FileUploadConfig",
|
||||
components: {},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-form-item__label{
|
||||
padding: 0 12px 0 0;
|
||||
}
|
||||
</style>
|
||||
43
src/views/workflow/form/config/ImageUploadConfig.vue
Normal file
43
src/views/workflow/form/config/ImageUploadConfig.vue
Normal file
@@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="数量限制">
|
||||
<tip slot="label" content="限制最大上传图片数量(为0则不限制)">数量限制</tip>
|
||||
<el-input-number class="max-fill" controls-position="right" :precision="0" size="small" v-model="value.maxNumber" placeholder="最多上传几张图片"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="大小限制">
|
||||
<tip slot="label" content="限制单个图片最大大小-MB(为0则不限制)">大小限制</tip>
|
||||
<el-input-number class="max-fill" controls-position="right" :precision="1" size="small" v-model="value.maxSize" placeholder="单个文件最大大小"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="图片压缩">
|
||||
<el-switch v-model="value.enableZip"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ImageUploadConfig",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-form-item__label{
|
||||
padding: 0 12px 0 0;
|
||||
}
|
||||
</style>
|
||||
18
src/views/workflow/form/config/LocationConfig.vue
Normal file
18
src/views/workflow/form/config/LocationConfig.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Location",
|
||||
components: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
18
src/views/workflow/form/config/MoneyInputConfig.vue
Normal file
18
src/views/workflow/form/config/MoneyInputConfig.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MoneyInput",
|
||||
components: {},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
30
src/views/workflow/form/config/NumberInputConfig.vue
Normal file
30
src/views/workflow/form/config/NumberInputConfig.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "NumberInput",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
34
src/views/workflow/form/config/OrgPickerConfig.vue
Normal file
34
src/views/workflow/form/config/OrgPickerConfig.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否多选">
|
||||
<el-switch v-model="value.multiple"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "OrgPicker",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
39
src/views/workflow/form/config/RatePickerConfig.vue
Normal file
39
src/views/workflow/form/config/RatePickerConfig.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="最大分值">
|
||||
<el-input-number size="small" :min="1" v-model="value.max" />
|
||||
</el-form-item>
|
||||
<el-form-item label="允许半分">
|
||||
<el-switch v-model="value.enableHalf"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="显示分值">
|
||||
<el-switch v-model="value.showScore"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "RatePickerConfig",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
94
src/views/workflow/form/config/SelectInputConfig.vue
Normal file
94
src/views/workflow/form/config/SelectInputConfig.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="选项设置" class="options">
|
||||
<div slot="label" class="option-item-label">
|
||||
<span>选项设置</span>
|
||||
<el-button icon="el-icon-plus" type="primary" size="mini"
|
||||
@click="value.options.push('新选项')" link>新增选项</el-button>
|
||||
</div>
|
||||
<draggable :list="value.options" group="option" handler=".el-icon-rank" :options="dragOption">
|
||||
<div v-for="(op, index) in value.options" :key="index" class="option-item">
|
||||
<i class="el-icon-rank"></i>
|
||||
<el-input v-model="value.options[index]" size="medium" placeholder="请设置选项值" clearable>
|
||||
<el-button icon="el-icon-delete" slot="append" type="danger" size="medium"
|
||||
@click="value.options.splice(index, 1)"></el-button>
|
||||
</el-input>
|
||||
</div>
|
||||
</draggable>
|
||||
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form-item label="选项展开">
|
||||
<el-switch v-model="value.expanding"></el-switch>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from "vuedraggable";
|
||||
|
||||
export default {
|
||||
name: "SelectInputConfig",
|
||||
components: {draggable},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragOption:{
|
||||
animation: 300,
|
||||
sort: true
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.options{
|
||||
.el-form-item__label{
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
.el-icon-rank{
|
||||
padding-right: 5px;
|
||||
cursor: move;
|
||||
}
|
||||
.option-item{
|
||||
.el-input{
|
||||
width: 250px;
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.option-item-label{
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
button{
|
||||
float:right;
|
||||
}
|
||||
}
|
||||
|
||||
/*/deep/ .el-form-item {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-form-item__label {
|
||||
padding: 0;
|
||||
}
|
||||
.options{
|
||||
.el-icon-rank{
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
</style>
|
||||
30
src/views/workflow/form/config/SignPanelConfig.vue
Normal file
30
src/views/workflow/form/config/SignPanelConfig.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="是否裁剪">
|
||||
<el-switch v-model="value.isCrop"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SignPanelConfig",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
52
src/views/workflow/form/config/TableListConfig.vue
Normal file
52
src/views/workflow/form/config/TableListConfig.vue
Normal file
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="提醒添加记录的提示"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="最大行数">
|
||||
<tip slot="label" content="允许添加多少条记录(为0则不限制)">最大行数</tip>
|
||||
<el-input-number controls-position="right" :precision="0" :max="100" :min="0" size="small" v-model="value.maxSize" placeholder="限制条数"/>
|
||||
</el-form-item>
|
||||
<el-form-item label="布局方式">
|
||||
<el-radio name="layout" :label="true" v-model="value.rowLayout">按表格</el-radio>
|
||||
<el-radio name="layout" :label="false" v-model="value.rowLayout">按表单</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item label="展示合计">
|
||||
<el-switch v-model="value.showSummary"></el-switch>
|
||||
<el-select v-if="value.showSummary" style="width: 100%;" size="small" v-model="value.summaryColumns" multiple clearable placeholder="请选择合计项" filterable>
|
||||
<el-option :label="column.title" :value="column.id" v-for="column in columns" :key="column.id"/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="展示边框">
|
||||
<el-switch v-model="value.showBorder"></el-switch>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TableListConfig",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
columns(){
|
||||
return this.value.columns.filter(c => c.valueType === 'Number')
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
30
src/views/workflow/form/config/TextInputConfig.vue
Normal file
30
src/views/workflow/form/config/TextInputConfig.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TextInput",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
30
src/views/workflow/form/config/TextareaInputConfig.vue
Normal file
30
src/views/workflow/form/config/TextareaInputConfig.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form-item label="提示文字">
|
||||
<el-input size="small" v-model="value.placeholder" placeholder="请设置提示语"/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TextareaInput",
|
||||
components: {},
|
||||
props:{
|
||||
value:{
|
||||
type: Object,
|
||||
default: ()=>{
|
||||
return {}
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
27
src/views/workflow/form/utils/CustomUtil.js
Normal file
27
src/views/workflow/form/utils/CustomUtil.js
Normal file
@@ -0,0 +1,27 @@
|
||||
Array.prototype.remove = function (value) {
|
||||
let index = this.indexOf(value)
|
||||
if (index > -1) {
|
||||
this.splice(index, 1)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
//移除对象数组,匹配唯一key
|
||||
Array.prototype.removeByKey = function (key, val) {
|
||||
let index = this.findIndex(value => value[key] === val)
|
||||
if (index > -1) {
|
||||
this.splice(index, 1)
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
//对象数组转map
|
||||
Array.prototype.toMap = function (key) {
|
||||
let map = new Map()
|
||||
this.forEach(v => map.set(v[key], v))
|
||||
return map
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
118
src/views/workflow/form/utils/date.js
Normal file
118
src/views/workflow/form/utils/date.js
Normal file
@@ -0,0 +1,118 @@
|
||||
//时间转换为String类型
|
||||
const moment = require("moment");
|
||||
|
||||
function simpleDateFormat(pattern) {
|
||||
var fmt = new Object();
|
||||
fmt.pattern = pattern;
|
||||
|
||||
fmt.parse = function (source) {
|
||||
try {
|
||||
return new Date(source);
|
||||
} catch (e) {
|
||||
console.log("字符串 " + source + " 转时间格式失败!");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
fmt.format = function (date) {
|
||||
if (typeof (date) == "undefined" || date == null || date == "") {
|
||||
return "";
|
||||
}
|
||||
|
||||
try {
|
||||
date = new Date(date);
|
||||
} catch (e) {
|
||||
console.log("时间 " + date + " 格式化失败!");
|
||||
return "";
|
||||
}
|
||||
|
||||
var strTime = this.pattern;//时间表达式的正则
|
||||
|
||||
var o = {
|
||||
"M+": date.getMonth() + 1, //月份
|
||||
"d+": date.getDate(), //日
|
||||
"H+": date.getHours(), //小时
|
||||
"m+": date.getMinutes(), //分
|
||||
"s+": date.getSeconds(), //秒
|
||||
"q+": Math.floor((date.getMonth() + 3) / 3), //季度
|
||||
"S": date.getMilliseconds() //毫秒
|
||||
};
|
||||
|
||||
if (/(y+)/.test(strTime)) {
|
||||
strTime = strTime
|
||||
.replace(RegExp.$1, (date.getFullYear() + "")
|
||||
.substr(4 - RegExp.$1.length));
|
||||
}
|
||||
for (var k in o) {
|
||||
if (new RegExp("(" + k + ")").test(strTime)) {
|
||||
strTime = strTime.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
|
||||
}
|
||||
}
|
||||
|
||||
return strTime;
|
||||
};
|
||||
return fmt;
|
||||
}
|
||||
|
||||
//时间格式化为yyyy-MM-dd
|
||||
function simpleDateFormatByMoreLine(date) {
|
||||
var fmt = simpleDateFormat("yyyy-MM-dd");
|
||||
date = fmt.parse(date)
|
||||
return fmt.format(date)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dateBegin 开始时间
|
||||
* @param dateEnd 结束时间
|
||||
* @returns {string} 时间差 天 小时 分钟 秒
|
||||
*/
|
||||
function consumingTime(dateBegin, dateEnd) {
|
||||
// //如果时间格式是正确的,那下面这一步转化时间格式就可以不用了
|
||||
let submitTime = new Date(dateBegin)
|
||||
let endTime = new Date(dateEnd)
|
||||
var dateDiff = endTime - submitTime;//时间差的毫秒数
|
||||
var dayDiff = Math.floor(dateDiff / (24 * 3600 * 1000));//计算出相差天数
|
||||
var leave1 = dateDiff % (24 * 3600 * 1000) //计算天数后剩余的毫秒数
|
||||
var hours = Math.floor(leave1 / (3600 * 1000))//计算出小时数
|
||||
//计算相差分钟数
|
||||
var leave2 = leave1 % (3600 * 1000) //计算小时数后剩余的毫秒数
|
||||
var minutes = Math.floor(leave2 / (60 * 1000))//计算相差分钟数
|
||||
//计算相差秒数
|
||||
var leave3 = leave2 % (60 * 1000) //计算分钟数后剩余的毫秒数
|
||||
var seconds = Math.round(leave3 / 1000)
|
||||
return dayDiff + "天 " + hours + "小时 " + minutes + " 分钟" + seconds + " 秒";
|
||||
}
|
||||
|
||||
export function timeLength(start, dateEnd) {
|
||||
// //如果时间格式是正确的,那下面这一步转化时间格式就可以不用了
|
||||
let mstart = moment(start);
|
||||
let mend = moment(dateEnd);
|
||||
let years = mend.diff(start, "years");
|
||||
let months = mend.diff(start, "months");
|
||||
let days = mend.diff(start, "days");
|
||||
let hours = mend.diff(start, "hours");
|
||||
let minutes = mend.diff(start, "minutes");
|
||||
minutes = minutes % 60;
|
||||
hours = hours % 24;
|
||||
months = months % 12;
|
||||
//因为每月天不固定,所以天要特殊动态处理
|
||||
if (mstart.date() < mend.date()) {
|
||||
days = mend.date() - mstart.date();
|
||||
if (minutes > 0 || hours > 0) {
|
||||
days--;
|
||||
}
|
||||
}
|
||||
//处理超过俩月且天超过31
|
||||
if (days > 31 && mend.month() - mstart.month() >= 2) {
|
||||
//将日期推至上月求差
|
||||
days = mend.diff(mstart.add(mend.month() - mstart.month() - 1, "month"), "days");
|
||||
}
|
||||
return `${years > 0 ? years + "年 " : " "}` + `${months > 0 ? months + "个月 " : " "}` + `${days > 0 ? days + "天 " : " "}`
|
||||
+ `${hours > 0 ? hours + "小时 " : " "}` + `${minutes > 0 ? minutes + "分钟 " : " "}`;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatToYYYYMMDD: simpleDateFormatByMoreLine,
|
||||
consumingTime: consumingTime,
|
||||
timeLength: timeLength
|
||||
}
|
||||
Reference in New Issue
Block a user