初始化版本,引入智慧食堂模块,第三方模块

This commit is contained in:
zjf committed 2024-12-06 18:48:18 +08:00
commit d22f88efd3
677 files changed
+69341

No files matched your search

+43
View File
@@ -0,0 +1,43 @@
<template>
<div><china-area v-model="form.props.value" :disabled="form.perm === 'R'" /></div>
</template>
<script lang="ts" setup>
const ChinaArea = defineAsyncComponent(() => import('/@/components/ChinaArea/index.vue'));
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+91
View File
@@ -0,0 +1,91 @@
<template>
<div>
<el-date-picker
class="formDate"
v-if="mode === 'D'"
:disabled="true"
type="date"
value-format="YYYY-MM-DD"
:placeholder="!form ? '' : form.placeholder"
/>
<el-date-picker
v-else
class="formDate"
value-format="YYYY-MM-DD"
v-model="form.props.value"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
type="date"
/>
</div>
</template>
<script lang="ts" setup>
import { defineExpose } from 'vue';
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.minLength) {
if (value.length < itemProps.minLength) {
return callback(new Error('长度不能小于' + itemProps.minLength));
}
}
if (itemProps.maxLength) {
if (value.length > itemProps.maxLength) {
return callback(new Error('长度不能大于' + itemProps.maxLength));
}
}
if (itemProps.regex) {
let regExp = new RegExp(itemProps.regex);
if (!regExp.test(value)) {
return callback(new Error(itemProps.regexDesc));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
<style scoped>
:deep(.formDate div.el-input__wrapper) {
width: 100% !important;
}
:deep(.formDate) {
width: 100% !important;
}
</style>
+91
View File
@@ -0,0 +1,91 @@
<template>
<div>
<el-date-picker
class="formDate"
v-if="mode === 'D'"
:disabled="true"
type="datetime"
value-format="YYYY-MM-DD HH:mm:ss"
:placeholder="!form ? '' : form.placeholder"
/>
<el-date-picker
v-else
class="formDate"
value-format="YYYY-MM-DD HH:mm:ss"
type="datetime"
v-model="form.props.value"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
/>
</div>
</template>
<script lang="ts" setup>
import { defineExpose } from 'vue';
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.minLength) {
if (value.length < itemProps.minLength) {
return callback(new Error('长度不能小于' + itemProps.minLength));
}
}
if (itemProps.maxLength) {
if (value.length > itemProps.maxLength) {
return callback(new Error('长度不能大于' + itemProps.maxLength));
}
}
if (itemProps.regex) {
let regExp = new RegExp(itemProps.regex);
if (!regExp.test(value)) {
return callback(new Error(itemProps.regexDesc));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
<style scoped>
:deep(.formDate div.el-input__wrapper) {
width: 100% !important;
}
:deep(.formDate) {
width: 100% !important;
}
</style>
+36
View File
@@ -0,0 +1,36 @@
<template>
<div>
<text>{{ !form ? '' : form.placeholder }}</text>
</div>
</template>
<script lang="ts" setup>
import { defineExpose } from 'vue';
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var checkConfig = (rule: any, value: any, callback: any) => {
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+72
View File
@@ -0,0 +1,72 @@
<template>
<div style="text-align: center; padding-top: 15px; background-color: var(--el-fill-color-light)">
<el-text>
<el-icon>
<Plus />
</el-icon>
{{ $t('flow.emptyComponent') }}
</el-text>
</div>
</template>
<script lang="ts" setup>
import { Check, Plus, Refresh } from '@element-plus/icons-vue';
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.minLength) {
if (value.length < itemProps.minLength) {
return callback(new Error('长度不能小于' + itemProps.minLength));
}
}
if (itemProps.maxLength) {
if (value.length > itemProps.maxLength) {
return callback(new Error('长度不能大于' + itemProps.maxLength));
}
}
if (itemProps.regex) {
let regExp = new RegExp(itemProps.regex);
if (!regExp.test(value)) {
return callback(new Error(itemProps.regexDesc));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+64
View File
@@ -0,0 +1,64 @@
<template>
<div>
<el-input v-if="mode === 'D'" :disabled="true" :placeholder="!form ? '' : form.placeholder" />
<el-input v-else v-model="form.props.value" :disabled="form.perm === 'R'" :placeholder="form.placeholder" />
</div>
</template>
<script lang="ts" setup>
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.minLength) {
if (value.length < itemProps.minLength) {
return callback(new Error('长度不能小于' + itemProps.minLength));
}
}
if (itemProps.maxLength) {
if (value.length > itemProps.maxLength) {
return callback(new Error('长度不能大于' + itemProps.maxLength));
}
}
if (itemProps.regex) {
let regExp = new RegExp(itemProps.regex);
if (!regExp.test(value)) {
return callback(new Error(itemProps.regexDesc));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+236
View File
@@ -0,0 +1,236 @@
<template>
<div>
<template v-if="mode === 'D'">
<draggable
v-model="targetList"
class="drag-content"
item-key="index"
:sort="true"
effect="dark"
:group="{ name: 'dragFormList', pull: false, put: true }"
>
<template #item="{ element }">
<div class="border okcomponent line" effect="dark" @click.stop="showConfigPanel(element.id)">
<el-icon v-if="element.type != 'Empty'" class="deleteIcon" @click.stop="deleteForm(element.id)">
<Delete />
</el-icon>
<el-form-item :label="step2Object[element.id]?.name" :required="step2Object[element.id]?.required">
<component style="width: 100%" :is="getFormWidget(element.type)" :id="element.id" v-model:form="step2Object[element.id]"></component>
</el-form-item>
</div>
</template>
</draggable>
</template>
<template v-else>
<el-card v-for="(item, index) in form.props.value" :key="index" style="margin-bottom: 10px">
<template #header>
<div class="card-header">
<span>编号:{{ index + 1 }}</span>
<el-button
v-if="form.props.value.length > 0 && form.perm != 'R'"
@click="deleteOneItem(index)"
class="button"
icon="Delete"
text
></el-button>
</div>
</template>
<form-render ref="formRenderRefLayout" :form-list="item"></form-render>
</el-card>
<div style="text-align: center; padding: 15px">
<el-button
type="primary"
@click="addOneItem"
:disabled="form.perm === 'R' || (form.props.max && form.props.value.length >= form.props.max)"
text
>添加明细
</el-button>
</div>
</template>
</div>
</template>
<script lang="ts" setup>
import draggable from 'vuedraggable';
import {Delete} from '@element-plus/icons-vue';
import {computed, ref} from 'vue';
import getFormName from '/@/views/flow/form/tools/utils/getFormWidget';
import FormRender from './render/FormRender.vue';
import {useFlowStore} from '../workflow/stores/flow';
const emit = defineEmits(['showPanel', 'addLayoutOneItem', 'deleteLayoutOneItem']);
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
return step2.filter((res) => res.id === props.id)[0];
});
const getFormWidget = (name: string) => {
//写的时候,组件的起名一定要与dragList中的element名字一模一样,不然会映射不上
return getFormName[name];
};
let targetList = computed({
get: () => {
let value = config.value?.props?.value;
if (value?.length == 0) {
return [
{
type: 'Empty',
name: '',
},
];
}
return value;
},
set: (v) => {
config.value.props.value = v.filter((res) => res.type != 'Layout' && res.type != 'Empty');
},
});
const addOneItem = () => {
emit('addLayoutOneItem', props.form.id);
};
const deleteOneItem = (index) => {
emit('deleteLayoutOneItem', props.form.id, index);
};
const showConfigPanel = (id) => {
emit('showPanel', config.value.props.value.filter((res) => res.id === id)[0]);
};
const deleteForm = (id) => {
config.value.props.value = config.value.props.value.filter((res) => res.id !== id);
emit('showPanel', undefined);
};
var step2Object = computed(() => {
var obj = {};
targetList.value.forEach((res) => (obj[res.id] = res));
return obj;
});
let props = defineProps({
mode: {
type: String,
default: 'D',
},
id: {
type: String,
default: '',
},
index: {
type: Number,
default: undefined,
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value.length == 0) {
return callback(new Error('明细【' + item.name + '】的数量不能为0'));
}
}
if (value.length == 0) {
return callback();
}
if (itemProps.min && value.length < itemProps.min) {
return callback(new Error('明细【' + item.name + '】的数量不能小于' + itemProps.min));
}
if (itemProps.max && value.length > itemProps.max) {
return callback(new Error('明细【' + item.name + '】的数量不能大于' + itemProps.max));
}
var result = [];
for (var fr of formRenderRefLayout.value) {
fr.validate(function (valid) {
result.push(valid);
});
}
setTimeout(() => {
if (result.indexOf(false) >= 0) {
callback(new Error('明细【' + item.name + '】下的表单验证失败'));
} else {
callback();
}
}, 1000);
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'change' });
}
return ruleArray;
};
const formRenderRefLayout = ref();
defineExpose({ getValidateRule });
</script>
<style scoped>
.okcomponent {
padding: 5px;
border: 1px solid;
border-radius: 5px;
margin-bottom: 10px;
}
@center_width: 360px;
.drag-content {
min-height: 100px;
padding: 10px 10px;
}
.deleteIcon {
position: absolute;
margin-left: calc(@center_width - 80px);
z-index: 20;
}
.deleteIcon:hover {
cursor: pointer;
color: palevioletred;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.text {
font-size: 14px;
}
.item {
margin-bottom: 18px;
}
.box-card {
width: 480px;
}
</style>
+185
View File
@@ -0,0 +1,185 @@
<template>
<div>
<el-input-number
style="width: 100%"
controls-position="right"
v-if="mode === 'D'"
:disabled="true"
:placeholder="!form ? '' : form.placeholder"
/>
<template v-else>
<el-input-number
style="width: 100%"
controls-position="right"
v-model="form.props.value"
:precision="form.props.radixNum"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
/>
<el-text> 大写: </el-text>
<el-text type="info">
{{ convertCurrency(form.props.value) }}
</el-text>
</template>
</div>
</template>
<script lang="ts" setup>
import other from "/@/utils/other";
function convertCurrency(money) {
if (money == null) {
return '';
}
//汉字的数字
var cnNums = new Array('零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖');
//基本单位
var cnIntRadice = new Array('', '拾', '佰', '仟');
//对应整数部分扩展单位
var cnIntUnits = new Array('', '万', '亿', '兆');
//对应小数部分单位
var cnDecUnits = new Array('角', '分', '毫', '厘');
//整数金额时后面跟的字符
var cnInteger = '整';
//整型完以后的单位
var cnIntLast = '元';
//最大处理的数字
var maxNum = 999999999999999.9999;
//金额整数部分
var integerNum;
//金额小数部分
var decimalNum;
//输出的中文金额字符串
var chineseStr = '';
//分离金额后用的数组,预定义
var parts;
// 传入的参数为空情况
if (money == '') {
return '';
}
money = parseFloat(money);
if (money >= maxNum) {
return '';
}
// 传入的参数为0情况
if (money == 0) {
chineseStr = cnNums[0] + cnIntLast + cnInteger;
return chineseStr;
}
// 转为字符串
money = money.toString();
// indexOf 检测某字符在字符串中首次出现的位置 返回索引值(从0 开始) -1 代表无
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) {
let zeroCount = 0;
let 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;
}
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
import { defineExpose } from 'vue';
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.radixNum) {
if (other.getNumberRadixNum(value) > itemProps.radixNum) {
return callback(new Error('小数位数不能大于' + itemProps.radixNum));
}
}
if (itemProps.min) {
if (value < itemProps.min) {
return callback(new Error('数值不能小于' + itemProps.min));
}
}
if (itemProps.max) {
if (value > itemProps.max) {
return callback(new Error('数值不能大于' + itemProps.max));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+97
View File
@@ -0,0 +1,97 @@
<template>
<div>
<el-select
v-if="mode==='D'"
:disabled="true"
:placeholder="!form?'':form.placeholder"
size="large">
</el-select>
<el-select v-else style="width: 100%"
multiple
collapse-tags
collapse-tags-tooltip
v-model="formValue"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
size="large"
>
<el-option
v-for="item in form.props.options"
:key="item.key"
:label="item.value"
:value="item.key"
/>
</el-select>
</div>
</template>
<script lang="ts" setup>
import {defineExpose,computed} from "vue";
var formValue = computed({
get() {
let value = props.form.props.value;
return (value&&value.length>0)?(value.map(res=>res.key)):undefined;
},
set(t) {
let filterElement = props.form.props.options.filter(res=>t.indexOf(res.key)>=0);
props.form.props.value = filterElement
}
})
let props = defineProps({
mode:{
type:String,
default:'D'
},
form: {
type: Object, default: () => {
}
}
});
const {proxy} = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value==undefined||value.length==0) {
return callback(new Error("请选择" + item.name))
}
}
if (value==undefined) {
return callback()
}
return callback()
}
let ruleArray = [{
validator: checkConfig, trigger: 'change'
}];
if (item.required) {
ruleArray.push(
{required: true, message: '请选择' + item.name, trigger: 'change'}
)
}
return ruleArray
}
defineExpose({getValidateRule});
</script>
+85
View File
@@ -0,0 +1,85 @@
<template>
<div>
<el-input-number
style="width: 100%"
controls-position="right"
v-if="mode === 'D'"
:disabled="true"
:placeholder="!form ? '' : form.placeholder"
/>
<el-input-number
v-else
style="width: 100%"
controls-position="right"
:precision="form.props.radixNum"
v-model="form.props.value"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
/>
</div>
</template>
<script lang="ts" setup>
import other from "/@/utils/other";
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
import { defineExpose } from 'vue';
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.radixNum) {
if (other.getNumberRadixNum(value) > itemProps.radixNum) {
return callback(new Error('小数位数不能大于' + itemProps.radixNum));
}
}
if (itemProps.min) {
if (value < itemProps.min) {
return callback(new Error('数值不能小于' + itemProps.min));
}
}
if (itemProps.max) {
if (value > itemProps.max) {
return callback(new Error('数值不能大于' + itemProps.max));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+45
View File
@@ -0,0 +1,45 @@
<template>
<div>
<div v-if="form.perm === 'R'" v-html="form.props.value" />
<editor v-else v-model:get-html="form.props.value" :placeholder="form.placeholder" />
</div>
</template>
<script lang="ts" setup>
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+51
View File
@@ -0,0 +1,51 @@
<template>
<div>
<el-button v-if="mode === 'D'" :disabled="true" circle icon="Plus"></el-button>
<select-show v-else :disabled="form.perm === 'R'" v-model:orgList="form.props.value" type="dept" :multiple="form.props.multi"></select-show>
</div>
</template>
<script lang="ts" setup>
import selectShow from '/@/components/OrgSelector/index.vue';
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value.length == 0) {
return callback(new Error('请选择' + item.name));
}
}
if (value.length == 0) {
return callback();
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'change',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请选择' + item.name, trigger: 'change' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+59
View File
@@ -0,0 +1,59 @@
<template>
<div>
<el-button v-if="mode === 'D'" :disabled="true" circle icon="Plus"></el-button>
<select-show
v-else
:disabled="form.perm === 'R'"
v-model:orgList="form.props.value"
type="user"
:multiple="form.props.multi"
:selectSelf="form.props.self"
></select-show>
</div>
</template>
<script lang="ts" setup>
import selectShow from '/@/components/OrgSelector/index.vue';
const props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
onMounted(() => {});
const { proxy } = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value.length == 0) {
return callback(new Error('请选择' + item.name));
}
}
if (value.length == 0) {
return callback();
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'change',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请选择' + item.name, trigger: 'change' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+33
View File
@@ -0,0 +1,33 @@
<template>
<div>
<text>{{ form?.props.value || form?.placeholder }}</text>
</div>
</template>
<script lang="ts" setup>
defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var checkConfig = (rule: any, value: any, callback: any) => {
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+75
View File
@@ -0,0 +1,75 @@
<template>
<div v-if="form.perm === 'R'">
<img :src="form.props.value" />
</div>
<div v-else>
<sign
ref="esginRef"
v-model:bg-color="bgColor"
:width="800"
:height="300"
:is-crop="isCrop"
:line-width="lineWidth"
:line-color="lineColor"
></sign>
<div class="flex justify-center">
<el-link type="primary" @click.stop="handleReset">清空</el-link>
<el-link type="primary" @click.stop="handleGenerate" class="ml-4">确认</el-link>
</div>
</div>
</template>
<script lang="ts" setup>
import { ElMessage } from 'element-plus';
const Sign = defineAsyncComponent(() => import('/@/components/Sign/index.vue'));
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var item = props.form;
let ruleArray = [];
if (item.required) {
ruleArray.push({
required: true,
trigger: 'blur',
message: '请先签名并确认',
});
}
return ruleArray;
};
const lineWidth = ref(6);
const lineColor = ref('#000000');
const bgColor = ref('');
const isCrop = ref(false);
const esginRef: any = ref(null);
const handleReset = () => {
esginRef.value.reset();
};
const handleGenerate = () => {
esginRef.value
.generate()
.then((res: any) => {
props.form.props.value = res;
ElMessage.success('签名生成成功,可提交流程发起审批');
})
.catch(() => {
ElMessage.warning('请先签名');
});
};
defineExpose({ getValidateRule });
</script>
<style scoped lang="less"></style>
+96
View File
@@ -0,0 +1,96 @@
<template>
<div>
<el-select
v-if="mode==='D'"
:disabled="true"
:placeholder="!form?'':form.placeholder"
size="large">
</el-select>
<el-select v-else style="width: 100%"
v-model="formValue"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
size="large"
>
<el-option
v-for="item in form.props.options"
:key="item.key"
:label="item.value"
:value="item.key"
/>
</el-select>
</div>
</template>
<script lang="ts" setup>
import {defineExpose,computed} from "vue";
var formValue = computed({
get() {
let value = props.form.props.value;
return (value&&value.length==1)?value[0].key:undefined;
},
set(t) {
let filterElement = props.form.props.options.filter(res=>res.key===t);
props.form.props.value = filterElement
}
})
let props = defineProps({
mode:{
type:String,
default:'D'
},
form: {
type: Object, default: () => {
}
}
});
const {proxy} = getCurrentInstance();
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value==undefined||value.length==0) {
return callback(new Error("请选择" + item.name))
}
}
if (value==undefined) {
return callback()
}
return callback()
}
let ruleArray = [{
validator: checkConfig, trigger: 'change'
}];
if (item.required) {
ruleArray.push(
{required: true, message: '请选择' + item.name, trigger: 'change'}
)
}
return ruleArray
}
defineExpose({getValidateRule});
</script>
+75
View File
@@ -0,0 +1,75 @@
<template>
<div>
<el-input v-if="mode === 'D'" :disabled="true" resize="none" type="textarea" :placeholder="!form ? '' : form.placeholder" />
<el-input
v-else
type="textarea"
:resize="form.perm === 'R' ? 'none' : 'vertical'"
v-model="form.props.value"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
/>
</div>
</template>
<script lang="ts" setup>
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const { proxy } = getCurrentInstance();
import { defineExpose } from 'vue';
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.minLength) {
if (value.length < itemProps.minLength) {
return callback(new Error('长度不能小于' + itemProps.minLength));
}
}
if (itemProps.maxLength) {
if (value.length > itemProps.maxLength) {
return callback(new Error('长度不能大于' + itemProps.maxLength));
}
}
if (itemProps.regex) {
let regExp = new RegExp(itemProps.regex);
if (!regExp.test(value)) {
return callback(new Error(itemProps.regexDesc));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
+90
View File
@@ -0,0 +1,90 @@
<template>
<div>
<el-time-picker
class="formDate"
arrow-control
v-if="mode === 'D'"
:disabled="true"
value-format="HH:mm:ss"
:placeholder="!form ? '' : form.placeholder"
/>
<el-time-picker
v-else
class="formDate"
arrow-control
v-model="form.props.value"
:disabled="form.perm === 'R'"
:placeholder="form.placeholder"
value-format="HH:mm:ss"
/>
</div>
</template>
<script lang="ts" setup>
import { defineExpose } from 'vue';
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var item = props.form;
var itemProps = item.props;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (!value) {
return callback(new Error('请填写' + item.name));
}
}
if (!value) {
return callback();
}
if (itemProps.minLength) {
if (value.length < itemProps.minLength) {
return callback(new Error('长度不能小于' + itemProps.minLength));
}
}
if (itemProps.maxLength) {
if (value.length > itemProps.maxLength) {
return callback(new Error('长度不能大于' + itemProps.maxLength));
}
}
if (itemProps.regex) {
let regExp = new RegExp(itemProps.regex);
if (!regExp.test(value)) {
return callback(new Error(itemProps.regexDesc));
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请填写' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
<style scoped>
:deep(.formDate div.el-input__wrapper) {
width: 100% !important;
}
:deep(.formDate) {
width: 100% !important;
}
</style>
+64
View File
@@ -0,0 +1,64 @@
<template>
<div>
<upload-file
v-model="form.props.value"
:disabled="form.perm === 'E'"
:fileSize="form.props.maxSize"
:limit="form.props.max"
:fileType="form.props.suffixArray"
/>
</div>
</template>
<script lang="ts" setup>
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
const item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value.length == 0) {
return callback(new Error('请上传' + item.name));
}
}
if (value.length == 0) {
return callback();
}
if (item.props.min && value.length < item.props.min) {
return callback(new Error('上传数量不能小于' + item.props.min));
}
if (item.props.suffixArray && item.props.suffixArray.length > 0) {
for (const name of value.split(',')) {
const suffix = name.substring(name.lastIndexOf('.') + 1);
if (item.props.suffixArray.indexOf(suffix) < 0) {
return callback(new Error('文件只支持' + item.props.suffixArray + '格式'));
}
}
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请上传' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
<style scoped lang="less"></style>
+55
View File
@@ -0,0 +1,55 @@
<template>
<div>
<upload-img
:disabled="form.perm === 'R'"
v-model:image-url="form.props.value"
:fileSize="form.props.maxSize"
:fileType="form.props.suffixArray"
/>
</div>
</template>
<script lang="ts" setup>
let props = defineProps({
mode: {
type: String,
default: 'D',
},
form: {
type: Object,
default: () => {},
},
});
const getValidateRule = () => {
var item = props.form;
var checkConfig = (rule: any, value: any, callback: any) => {
if (item.required) {
if (value.length == 0) {
return callback(new Error('请上传' + item.name));
}
}
if (value.length == 0) {
return callback();
}
if (value.length < item.props.min) {
return callback(new Error('上传数量不能小于' + item.props.min));
}
return callback();
};
let ruleArray = [
{
validator: checkConfig,
trigger: 'blur',
},
];
if (item.required) {
ruleArray.push({ required: true, message: '请上传' + item.name, trigger: 'blur' });
}
return ruleArray;
};
defineExpose({ getValidateRule });
</script>
<style scoped lang="less"></style>
+35
View File
@@ -0,0 +1,35 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
const config = computed(() => {
let step2 = flowStore.step2;
const idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (const item of list) {
let value = item.props.value;
const valueList = value.filter((res: any) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div></div>
</template>
+45
View File
@@ -0,0 +1,45 @@
<script setup lang="ts">
import { computed, defineExpose } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
let filterElement = step2.filter((res) => res.id === props.id)[0];
return filterElement;
});
const { proxy } = getCurrentInstance();
</script>
<template>
<div>
<el-form-item label="最小值">
<el-date-picker size="default" class="formDate" v-model="config.props.min" value-format="YYYY-MM-DD" type="date" />
</el-form-item>
<el-form-item label="最大值">
<el-date-picker size="default" class="formDate" v-model="config.props.max" value-format="YYYY-MM-DD" type="date" />
</el-form-item>
<el-form-item label="默认值">
<el-date-picker size="default" class="formDate" value-format="YYYY-MM-DD" v-model="config.props.value" type="date" />
</el-form-item>
</div>
</template>
<style scoped>
:deep(.formDate div.el-input__wrapper) {
width: 100% !important;
}
:deep(.formDate) {
width: 100% !important;
}
</style>
+45
View File
@@ -0,0 +1,45 @@
<script setup lang="ts">
import { computed, defineExpose } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
let filterElement = step2.filter((res) => res.id === props.id)[0];
return filterElement;
});
const { proxy } = getCurrentInstance();
</script>
<template>
<div>
<el-form-item label="最小值">
<el-date-picker size="default" class="formDate" v-model="config.props.min" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" />
</el-form-item>
<el-form-item label="最大值">
<el-date-picker size="default" class="formDate" v-model="config.props.max" value-format="YYYY-MM-DD HH:mm:ss" type="datetime" />
</el-form-item>
<el-form-item label="默认值">
<el-date-picker size="default" class="formDate" value-format="YYYY-MM-DD HH:mm:ss" v-model="config.props.value" type="datetime" />
</el-form-item>
</div>
</template>
<style scoped>
:deep(.formDate div.el-input__wrapper) {
width: 100% !important;
}
:deep(.formDate) {
width: 100% !important;
}
</style>
@@ -0,0 +1,50 @@
<script setup lang="ts">
import { computed, defineExpose } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
const { proxy } = getCurrentInstance();
const placeHolder = computed(() => {
return config.value.placeholder;
});
watch(
() => placeHolder.value,
(f) => {
config.value.props.value = f;
},
{ deep: true }
);
</script>
<template>
<div></div>
</template>
+51
View File
@@ -0,0 +1,51 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
const config = computed(() => {
let step2 = flowStore.step2;
const idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (const item of list) {
let value = item.props.value;
const valueList = value.filter((res: any) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div>
<el-form-item :label="$t('flow.minLength')">
<el-input-number :step="1" step-strictly v-model="config.props.minLength" style="width: 100%" controls-position="right" :min="1" :max="100" />
</el-form-item>
<el-form-item :label="$t('flow.maxLength')">
<el-input-number :step="1" step-strictly v-model="config.props.maxLength" style="width: 100%" controls-position="right" :min="1" :max="100" />
</el-form-item>
<el-form-item :label="$t('flow.regularExpression')">
<el-input placeholder="^\d+$" v-model="config.props.regex" />
</el-form-item>
<el-form-item :label="$t('flow.regularExpressionTip')">
<el-input :placeholder="$t('flow.inputErrorRegularExpressionTip')" v-model="config.props.regexDesc" />
</el-form-item>
<el-form-item :label="$t('flow.defaultTip')">
<el-input v-model="config.props.value" />
</el-form-item>
</div>
</template>
+45
View File
@@ -0,0 +1,45 @@
<script setup lang="ts">
import { computed, defineExpose } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
const { proxy } = getCurrentInstance();
</script>
<template>
<div>
<el-form-item label="最小数量">
<el-input-number v-model="config.props.min" style="width: 100%" controls-position="right" :min="1" :max="100" />
</el-form-item>
<el-form-item label="最大数量">
<el-input-number v-model="config.props.max" style="width: 100%" controls-position="right" :min="1" :max="100" />
</el-form-item>
</div>
</template>
+65
View File
@@ -0,0 +1,65 @@
<script setup lang="ts">
import { computed, defineExpose } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
const { proxy } = getCurrentInstance();
</script>
<template>
<div>
<el-form-item label="格式">
<el-checkbox v-model="config.props.showChinese" label="显示大写数字" />
<!-- <el-checkbox v-model="config.props.showThousandSymbol" label="显示千位分隔符" />-->
</el-form-item>
<el-form-item label="最小值">
<el-input-number v-model="config.props.min" style="width: 100%" controls-position="right" :min="1" :max="100000000000000" />
</el-form-item>
<el-form-item label="最大值">
<el-input-number v-model="config.props.max" style="width: 100%" controls-position="right" :min="1" :max="100000000000000" />
</el-form-item>
<el-form-item label="小数位数">
<el-input-number
:step="1"
step-strictly
v-model="config.props.radixNum"
style="width: 100%"
controls-position="right"
:min="0"
value-on-clear="min"
:max="10"
/>
</el-form-item>
<el-form-item label="默认值">
<el-input-number :precision="config.props.radixNum" controls-position="right" style="width: 100%" v-model="config.props.value" />
</el-form-item>
</div>
</template>
+129
View File
@@ -0,0 +1,129 @@
<script setup lang="ts">
import {computed, defineExpose} from "vue";
let props = defineProps({
id: {
type: String,
default: "",
},
});
import {useFlowStore} from "../../workflow/stores/flow";
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter(res => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter(res => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter(res => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
var options = computed(() => {
return config.value.props.options;
});
const {proxy} = getCurrentInstance();
import {Delete, Edit, Search, Share, Upload} from "@element-plus/icons-vue";
const addOption = () => {
options.value.push({
key: "",
value: "",
});
};
const deleteOption = (index) => {
options.value.splice(index, 1);
};
var formValue = computed({
get() {
let value = config.value.props.value;
return (value && value.length > 0) ? (value.map(res => res.key)) : undefined;
},
set(t) {
let filterElement = options.value.filter(res => t.indexOf(res.key) >= 0);
config.value.props.value = filterElement
}
})
</script>
<template>
<div v-if="config">
<el-form-item label="选项" required>
<div v-for="(item, index) in options" :key="index" class="class_option">
<div class="f1">
<el-input v-model="item.key" placeholder="选项值key"/>
</div>
<div class="f2">
<el-input v-model="item.value" placeholder="选项标签value"/>
</div>
<div class="f3">
<el-icon :size="20" @click.stop="deleteOption(index)">
<Delete/>
</el-icon>
</div>
</div>
<el-link type="primary" @click.stop="addOption">添加选项</el-link>
</el-form-item>
<el-form-item label="默认值">
<el-select
v-model="formValue"
multiple
collapse-tags
collapse-tags-tooltip
placeholder=""
style="width: 100%"
>
<el-option
v-for="item in options"
:key="item.key"
:label="item.value"
:value="item.key"
/>
</el-select>
</el-form-item>
</div>
</template>
<style scoped lang="scss">
$width-f1: 150px;
$width-f3: 40px;
.class_option {
display: flex;
flex-direction: row;
width: 100%;
.f1 {
width: $width-f1;
}
.f2 {
margin-left: 5px;
width: calc(100% - #{$width-f1} - #{$width-f3} - 5px);
}
.f3 {
width: $width-f3;
text-align: center;
padding-top: 5px;
height: 35.6px;
}
}
</style>
+61
View File
@@ -0,0 +1,61 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div>
<el-form-item label="单位">
<el-input v-model="config.props.unit" style="width: 100%" maxlength="10" />
</el-form-item>
<el-form-item label="最小值">
<el-input-number v-model="config.props.min" style="width: 100%" controls-position="right" :min="1" :max="100000000000000" />
</el-form-item>
<el-form-item label="最大值">
<el-input-number v-model="config.props.max" style="width: 100%" controls-position="right" :min="1" :max="100000000000000" />
</el-form-item>
<el-form-item label="小数位数">
<el-input-number
:step="1"
step-strictly
v-model="config.props.radixNum"
style="width: 100%"
controls-position="right"
:min="0"
value-on-clear="min"
:max="10"
/>
</el-form-item>
<el-form-item label="默认值">
<el-input-number :precision="config.props.radixNum" controls-position="right" style="width: 100%" v-model="config.props.value" />
</el-form-item>
</div>
</template>
+35
View File
@@ -0,0 +1,35 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
const config = computed(() => {
let step2 = flowStore.step2;
const idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (const item of list) {
let value = item.props.value;
const valueList = value.filter((res: any) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div></div>
</template>
+78
View File
@@ -0,0 +1,78 @@
<script setup lang="ts">
import employeesDialog from '/@/components/OrgSelector/employeesDialog.vue';
import orgItem from '/@/components/OrgSelector/orgItem.vue';
import { useFlowStore } from '../../workflow/stores/flow';
import { Plus } from '@element-plus/icons-vue';
import { ref } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
var defaultValue = computed({
get: () => {
return config.value.props.value;
},
set: (s: any[]) => {
config.value.props.value = s;
},
});
//校验
const validate = () => {
return true;
};
defineExpose({ validate });
let selectUserDialogVisible = ref(false);
const afterSelectUser = (data) => {
defaultValue.value = data;
selectUserDialogVisible.value = false;
};
</script>
<template>
<div>
<employees-dialog
v-model:visible="selectUserDialogVisible"
:data="defaultValue"
type="dept"
:multiple="config.props.multi"
@change="afterSelectUser"
/>
<el-form-item label="选择范围">
<el-checkbox v-model="config.props.multi" :disabled="defaultValue.length > 0" label="可选多个部门" />
</el-form-item>
<el-form-item label="默认值">
<el-button circle :icon="Plus" @click.stop="selectUserDialogVisible = true"></el-button>
<div style="width: 100%">
<org-item v-model:data="defaultValue" />
</div>
</el-form-item>
</div>
</template>
+82
View File
@@ -0,0 +1,82 @@
<script setup lang="ts">
import employeesDialog from '/@/components/OrgSelector/employeesDialog.vue';
import orgItem from '/@/components/OrgSelector/orgItem.vue';
import { useFlowStore } from '../../workflow/stores/flow';
import { Plus } from '@element-plus/icons-vue';
import { ref } from 'vue';
let props = defineProps({
id: {
type: String,
default: '',
},
});
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
var defaultValue = computed({
get: () => {
return config.value.props.value;
},
set: (s: any[]) => {
config.value.props.value = s;
},
});
const { proxy } = getCurrentInstance();
//校验
const validate = () => {
return true;
};
defineExpose({ validate });
let selectUserDialogVisible = ref(false);
const afterSelectUser = (data) => {
defaultValue.value = data;
selectUserDialogVisible.value = false;
};
</script>
<template>
<div>
<employees-dialog
v-model:visible="selectUserDialogVisible"
:data="defaultValue"
type="user"
:multiple="config.props.multi"
@change="afterSelectUser"
/>
<el-form-item label="选择范围">
<el-checkbox v-model="config.props.self" :disabled="defaultValue.length > 0" label="可选自己" />
<el-checkbox v-model="config.props.multi" :disabled="defaultValue.length > 0" label="可选多人" />
</el-form-item>
<el-form-item label="默认值">
<el-button circle :icon="Plus" @click.stop="selectUserDialogVisible = true"></el-button>
<div style="width: 100%">
<org-item v-model:data="defaultValue" />
</div>
</el-form-item>
</div>
</template>
+43
View File
@@ -0,0 +1,43 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
computed(() => {
return config.value?.placeholder;
});
</script>
<template>
<div>
<el-form-item :label="$t('flow.servicePrefix')">
<el-input :placeholder="$t('flow.servicePrefixTips')" v-model="config.props.prefix" />
</el-form-item>
</div>
</template>
+39
View File
@@ -0,0 +1,39 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
const config = computed(() => {
let step2 = flowStore.step2;
const idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (const item of list) {
let value = item.props.value;
const valueList = value.filter((res: any) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div>
<el-form-item label="默认值">
<el-input v-model="config.props.value" />
</el-form-item>
</div>
</template>
+128
View File
@@ -0,0 +1,128 @@
<script setup lang="ts">
import {computed, defineExpose} from "vue";
let props = defineProps({
id: {
type: String,
default: "",
},
});
import {useFlowStore} from "../../workflow/stores/flow";
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter(res => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter(res => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter(res => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
var options = computed(() => {
return config.value.props.options;
});
const {proxy} = getCurrentInstance();
import {Delete, Edit, Search, Share, Upload} from "@element-plus/icons-vue";
const addOption = () => {
options.value.push({
key: "",
value: "",
});
};
const deleteOption = (index) => {
options.value.splice(index, 1);
};
var formValue = computed({
get() {
let value = config.value.props.value;
return (value && value.length == 1) ? value[0].key : undefined;
},
set(t) {
let filterElement = options.value.filter(res => res.key === t);
config.value.props.value = filterElement
}
})
</script>
<template>
<div v-if="config">
<el-form-item label="选项" required>
<div v-for="(item, index) in options" :key="index" class="class_option">
<div class="f1">
<el-input v-model="item.key" placeholder="选项值key"/>
</div>
<div class="f2">
<el-input v-model="item.value" placeholder="选项标签value"/>
</div>
<div class="f3">
<el-icon :size="20" @click.stop="deleteOption(index)">
<Delete/>
</el-icon>
</div>
</div>
<el-link type="primary" @click.stop="addOption">添加选项</el-link>
</el-form-item>
<el-form-item label="默认值">
<el-select
v-model="formValue"
placeholder=""
style="width: 100%"
>
<el-option
v-for="item in options"
:key="item.key"
:label="item.value"
:value="item.key"
/>
</el-select>
</el-form-item>
</div>
</template>
<style scoped lang="scss">
$f1_width: 150px;
$f3_width: 40px;
.class_option {
display: flex;
flex-direction: row;
width: 100%;
.f1 {
width: $f1_width;
}
.f2 {
margin-left: 5px;
width: calc(100% - #{$f1_width} - #{$f3_width} - 5px);
}
.f3 {
width: $f3_width;
text-align: center;
padding-top: 5px;
height: 35.6px;
}
}
</style>
+50
View File
@@ -0,0 +1,50 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div>
<el-form-item :label="$t('flow.minLength')">
<el-input-number :step="1" step-strictly v-model="config.props.minLength" style="width: 100%" controls-position="right" :min="1" :max="100" />
</el-form-item>
<el-form-item :label="$t('flow.maxLength')">
<el-input-number :step="1" step-strictly v-model="config.props.maxLength" style="width: 100%" controls-position="right" :min="1" :max="100" />
</el-form-item>
<el-form-item :label="$t('flow.regularExpression')">
<el-input placeholder="^\d+$" v-model="config.props.regex" />
</el-form-item>
<el-form-item :label="$t('flow.regularExpressionTip')">
<el-input :placeholder="$t('flow.inputErrorRegularExpressionTip')" v-model="config.props.regexDesc" />
</el-form-item>
<el-form-item :label="$t('flow.defaultTip')">
<el-input v-model="config.props.value" />
</el-form-item>
</div>
</template>
+42
View File
@@ -0,0 +1,42 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
let filterElement = step2.filter((res) => res.id === props.id)[0];
return filterElement;
});
</script>
<template>
<div>
<el-form-item label="最小值">
<el-time-picker arrow-control size="default" class="formDate" v-model="config.props.min" value-format="HH:mm:ss" />
</el-form-item>
<el-form-item label="最大值">
<el-time-picker arrow-control size="default" class="formDate" v-model="config.props.max" value-format="HH:mm:ss" />
</el-form-item>
<el-form-item label="默认值">
<el-time-picker arrow-control size="default" class="formDate" value-format="HH:mm:ss" v-model="config.props.value" />
</el-form-item>
</div>
</template>
<style scoped>
:deep(.formDate div.el-input__wrapper) {
width: 100% !important;
}
:deep(.formDate) {
width: 100% !important;
}
</style>
+60
View File
@@ -0,0 +1,60 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div>
<el-form-item label="最小数量">
<el-input-number v-model="config.props.min" style="width: 100%" controls-position="right" :min="1" value-on-clear="min" :max="10" />
</el-form-item>
<el-form-item label="最大数量">
<el-input-number v-model="config.props.max" style="width: 100%" controls-position="right" :min="1" value-on-clear="max" :max="10" />
</el-form-item>
<el-form-item label="文件大小(MB)">
<el-input-number v-model="config.props.maxSize" style="width: 100%" controls-position="right" :min="0.01" value-on-clear="max" :max="10" />
</el-form-item>
<el-form-item label="文件后缀(jpg,jpeg,png)">
<el-select
v-model="config.props.suffixArray"
style="width: 100%"
multiple
filterable
allow-create
default-first-option
:reserve-keyword="false"
placeholder="请输入支持的后缀,回车确定"
>
</el-select>
</el-form-item>
</div>
</template>
@@ -0,0 +1,39 @@
<script setup lang="ts">
let props = defineProps({
id: {
type: String,
default: '',
},
});
import { useFlowStore } from '../../workflow/stores/flow';
let flowStore = useFlowStore();
var config = computed(() => {
let step2 = flowStore.step2;
var idObjList = step2.filter((res) => res.id === props.id);
if (idObjList.length > 0) {
return idObjList[0];
}
let list = step2.filter((res) => res.type === 'Layout');
for (var item of list) {
let value = item.props.value;
var valueList = value.filter((res) => res.id === props.id);
if (valueList.length > 0) {
return valueList[0];
}
}
return undefined;
});
</script>
<template>
<div>
<el-form-item label="文件大小(MB)">
<el-input-number v-model="config.props.maxSize" style="width: 100%" controls-position="right" :min="0.01" value-on-clear="max" :max="10" />
</el-form-item>
</div>
</template>
+263
View File
@@ -0,0 +1,263 @@
import { FormGroupVO } from './types';
const formGroupConfig: FormGroupVO[] = [
{
name: '文本',
type: 'Text',
formList: [
{
name: '流水号',
icon: 'Timer',
type: 'Sequence',
typeName: '说明',
placeholder: 'PREFIX2023072300001',
required: false,
props: {
value: '',
prefix: '',
},
},
{
name: '单行文本',
type: 'Input',
typeName: '单行文本',
icon: 'EditPen',
placeholder: '请填写',
required: false,
props: {
value: '',
},
},
{
name: '多行文本',
icon: 'More',
type: 'Textarea',
typeName: '多行文本',
placeholder: '请填写',
required: false,
props: {
value: '',
},
},
{
name: '富文本',
type: 'RichEditor',
typeName: '富文本',
icon: 'Notebook',
placeholder: '请填写',
required: false,
props: {
value: '',
},
},
{
name: '签名',
icon: 'EditPen',
type: 'Sign',
typeName: '说明',
placeholder: '请填写',
required: false,
props: {
value: '',
},
},
{
name: '说明',
icon: 'ChatLineSquare',
type: 'Description',
typeName: '说明',
placeholder: '请填写',
required: false,
props: {
value: '',
},
},
],
},
{
name: '数值',
type: 'Number',
formList: [
{
name: '数字',
type: 'Number',
icon: 'Edit',
typeName: '数字',
placeholder: '请填写',
required: false,
props: {
radixNum: 0,
},
},
{
name: '金额',
type: 'Money',
icon: 'Money',
typeName: '金额',
placeholder: '请填写',
required: false,
props: {
radixNum: 0,
},
},
],
},
{
name: '日期-时间',
type: 'DateTime',
formList: [
{
name: '日期',
type: 'Date',
icon: 'Calendar',
typeName: '日期',
placeholder: '请选择',
required: false,
props: {
radixNum: 0,
},
},
{
name: '日期时间',
type: 'DateTime',
icon: 'AlarmClock',
typeName: '日期时间',
placeholder: '请选择',
required: false,
props: {
radixNum: 0,
},
},
{
name: '时间',
type: 'Time',
icon: 'Timer',
typeName: '时间',
placeholder: '请选择',
required: false,
props: {
radixNum: 0,
},
},
],
},
{
name: '选项',
type: 'Option',
formList: [
{
name: '单选',
icon: 'Select',
type: 'SingleSelect',
typeName: '单选',
placeholder: '请选择',
required: false,
props: {
options: [
{
key: '',
value: '',
},
],
},
},
{
name: '多选',
type: 'MultiSelect',
typeName: '多选',
icon: 'Finished',
placeholder: '请选择',
required: false,
props: {
value: [],
options: [
{
key: '',
value: '',
},
],
},
},
{
name: '省市区',
type: 'Area',
typeName: '省市区',
icon: 'MapLocation',
placeholder: '请选择',
required: false,
props: {
value: '',
},
},
],
},
{
name: '其他',
type: 'Other',
formList: [
{
name: '图片',
type: 'UploadImage',
typeName: '图片',
icon: 'Picture',
placeholder: '请选择',
required: false,
props: {
value: '',
maxSize: 10,
suffixArray: ['image/jpeg', 'image/png', 'image/gif'],
},
},
{
name: '文件',
type: 'UploadFile',
typeName: '文件',
icon: 'Upload',
placeholder: '请选择',
required: false,
props: {
value: '',
min: 1,
max: 10,
maxSize: 10,
suffixArray: ['png', 'jpg', 'jpeg', 'doc', 'xls', 'ppt', 'txt', 'pdf', 'docx', 'xlsx', 'pptx'],
},
},
{
name: '用户',
type: 'SelectUser',
typeName: '用户',
placeholder: '请选择',
icon: 'User',
required: false,
props: {
multi: false,
self: false,
value: [],
},
},
{
name: '部门',
icon: 'OfficeBuilding',
type: 'SelectDept',
typeName: '部门',
placeholder: '请选择',
required: false,
props: {
multi: false,
self: false,
value: [],
},
},
],
},
];
export { formGroupConfig };
+47
View File
@@ -0,0 +1,47 @@
export interface FormGroupVO {
name: string;
formList: FormVO[];
}
export interface FormVO {
name: string | '';
id: String | '';
type: string | '';
typeName: string | '';
placeholder: string;
required: boolean;
icon: String;
props: FormConfigVO;
}
export interface FormConfigVO {
minLength: Number;
maxLength: Number;
value: any | '';
regex: string | '';
regexDesc: string | '';
min: Number;
max: Number;
radixNum: Number;
showChinese: boolean;
self: boolean;
multi: boolean;
showThousandSymbol: boolean;
fileList: any[];
unit: string;
options: FormConfigOptionVO[];
maxSize: Number;
suffixArray: String[];
prefix: string | '';
}
export interface FormConfigOptionVO {
key: String;
value: String;
}
export interface FormConfigUserVO {
id: string;
name: string;
type: string;
avatar: string;
}
+271
View File
@@ -0,0 +1,271 @@
<template>
<el-container>
<el-aside width="400px">
<div effect="dark">
<h4 style="text-align: center">{{ $t('flow.componentLibrary') }}</h4>
<div v-for="item in oriFormList" :key="item.id">
<h5 style="padding-left: 60px">{{ $i18n.locale === 'en' ? item.type : item.name }}</h5>
<draggable
v-model="item.formList"
ghost-class="ghost"
:force-fallback="true"
item-key="index"
:sort="false"
@end="dragEnd"
@start="drag = true"
class="leftItem"
:animation="300"
:group="{ name: 'dragFormList', pull: 'clone', put: false }"
:clone="cloneFunc"
>
<template #item="{ element }">
<div class="zj">
<el-button size="large" style="width: 100%" :icon="element.icon">{{ $i18n.locale === 'en' ? element.type : element.name }}</el-button>
</div>
</template>
</draggable>
</div>
</div>
</el-aside>
<el-main>
<div style="display: flex; flex-direction: row">
<div class="f11">
<el-form label-position="top">
<draggable
v-model="targetList"
class="drag-content"
item-key="index"
:sort="true"
effect="dark"
:group="{ name: 'dragFormList', pull: true, put: true }"
@change="dragChanged"
>
<template #item="{ element, index }">
<div class="border okcomponent line" effect="dark" @click.stop="showCurrentPageConfigPanel(element.id)">
<el-icon v-if="element.type != 'Empty'" class="deleteIcon" @click.stop="deleteForm(element.id)">
<Delete />
</el-icon>
<el-form-item :label="step2Object[element.id]?.name" :required="step2Object[element.id]?.required">
<component
style="width: 100%"
@showPanel="showPanel"
:index="index"
:is="getFormWidget(element.type)"
:id="element.id"
v-model:form="step2Object[element.id]"
></component>
</el-form-item>
</div>
</template>
</draggable>
</el-form>
</div>
<el-drawer v-model="drawer">
<template #header>
<div class="card-header">
{{ currentForm?.typeName }}
</div>
</template>
<el-form label-width="120px" label-position="top">
<el-form-item required label="字段ID">
<el-input v-model="currentForm.id" maxlength="50" />
</el-form-item>
<el-form-item required :label="$t('flow.title')">
<el-input v-model="currentForm.name" maxlength="10" />
</el-form-item>
<el-form-item :label="$t('flow.tips')" :required="currentForm?.type === 'Description'">
<el-input v-model="currentForm.placeholder" maxlength="50" />
</el-form-item>
<component :is="getFormConfigWidget(currentForm?.type)" :id="currentForm?.id" ref="currentFormConfigRef"></component>
<el-form-item :label="$t('flow.other')">
<el-checkbox v-model="currentForm.required" :label="$t('flow.required')" />
</el-form-item>
</el-form>
</el-drawer>
</div>
</el-main>
</el-container>
</template>
<script lang="ts" setup name="flow-form-designer">
import getFormName from '/@/views/flow/form/tools/utils/getFormWidget';
import getFormConfigName from '/@/views/flow/form/tools/utils/getFormConfigWidget';
import { formValidateDict } from '/@/views/flow/form/tools/utils/formValidate';
import { Delete } from '@element-plus/icons-vue';
import { useFlowStore } from '../workflow/stores/flow';
import draggable from 'vuedraggable';
import { FormGroupVO } from '/@/views/flow/form/const/types';
import { formGroupConfig } from '/@/views/flow/form/const/data';
import { computed } from 'vue';
import other from '/@/utils/other';
const drag = ref(false);
const currentForm = ref();
const drawer = ref(false);
let oriFormList = ref<FormGroupVO[]>(formGroupConfig);
const flowStore = useFlowStore();
const currentFormConfigRef = ref();
const step2List = computed(() => flowStore.step2);
const step2Object = computed(() => {
const obj = {};
step2List.value.forEach((res) => (obj[res.id] = res));
return obj;
});
const cloneFunc = (el) => {
el.id = other.getNonDuplicateID();
return el;
};
const deleteForm = (id) => {
const existForm = flowStore.step2.find((res) => res.id === id);
if (existForm?.type === 'Layout' && currentForm.value) {
//明细
const value = existForm.props.value;
const filter = value.filter((res) => res.id === currentForm.value.id);
if (filter.length > 0) {
currentForm.value = undefined;
}
}
flowStore.setStep2(flowStore.step2.filter((res) => res.id !== id));
if (currentForm.value && currentForm.value?.id === id) {
currentForm.value = undefined;
}
};
const showCurrentPageConfigPanel = (id) => {
currentForm.value = flowStore.step2.find((res) => res.id === id);
drawer.value = true;
};
const showPanel = (form) => {
currentForm.value = form;
};
const getFormWidget = (name) => {
//写的时候,组件的起名一定要与dragList中的element名字一模一样,不然会映射不上
return getFormName[name];
};
const getFormConfigWidget = (name) => {
//写的时候,组件的起名一定要与dragList中的element名字一模一样,不然会映射不上
return getFormConfigName[name];
};
const { proxy } = getCurrentInstance();
const dragEnd = (a) => {
drag.value = false;
oriFormList.value = other.deepClone(oriFormList.value);
};
const dragChanged = (a) => {};
const targetList = computed({
get: () => {
const value = flowStore.step2;
if (value?.length == 0) {
return [
{
type: 'Empty',
name: '',
},
];
}
return value;
},
set: (v) => {
const value = v.filter((res) => res.type != 'Empty');
flowStore.setStep2(value);
},
});
const validate = (f) => {
const err = [];
const formList = step2List.value;
if (formList.length == 0) {
err.push('表单不能为空');
}
console.log(formList);
for (let form of formList) {
const count = formList.filter((item) => item.id === form.id).length;
if (count > 1) {
err.push(`发现字段ID重复: ${form.id}`);
}
const formValidateDictElement = formValidateDict[form.type];
if (formValidateDictElement) {
const result = formValidateDictElement(form, proxy);
if (!result.valid) {
err.push(result.msg);
}
}
}
const newList = Array.from(new Set(formList.map((res) => res.name)));
if (newList.length < formList.length) {
err.push('表单名称不能重复');
}
f(err.length == 0, err);
};
defineExpose({ validate });
</script>
<style scoped lang="scss">
$width: 140px;
$f22_width: 400px;
$center_width: 360px;
.leftItem {
padding-left: 50px;
}
.zj {
display: inline-block;
width: $width;
margin: 5px;
}
.drag-content {
border: 1px solid;
width: $center_width;
border-radius: 10px;
padding: 10px 10px;
}
.f11 {
width: calc(100% - #{$f22_width});
}
.f22 {
width: $f22_width;
}
.okcomponent {
padding: 5px;
border-radius: 5px;
margin-bottom: 10px;
}
.deleteIcon {
margin-left: calc(#{$center_width} - 50px);
z-index: 20;
}
.deleteIcon:hover {
cursor: pointer;
color: palevioletred;
}
</style>
+97
View File
@@ -0,0 +1,97 @@
<script setup lang="ts">
import getFormName from '/@/views/flow/form/tools/utils/getFormWidget';
import { onMounted, reactive, ref } from 'vue';
import type { FormRules } from 'element-plus';
const getFormWidget = (name: string) => {
//写的时候,组件的起名一定要与dragList中的element名字一模一样,不然会映射不上
return getFormName[name];
};
const { proxy } = getCurrentInstance();
let props = defineProps({
formList: {
type: Object,
default: () => {},
},
});
onMounted(() => {
let formList = props.formList;
for (var item of formList) {
let id = item.id;
if (proxy.$refs['form' + id]?.length > 0) {
let validateRule = proxy.$refs['form' + id][0].getValidateRule();
rules[id] = validateRule;
}
}
});
const emit = defineEmits(['addLayoutOneItem', 'deleteLayoutOneItem']);
const addLayoutOneItem = (id) => {
emit('addLayoutOneItem', id);
};
const deleteLayoutOneItem = (id, index) => {
emit('deleteLayoutOneItem', id, index);
};
const ruleFormRef = ref();
const rules = reactive<FormRules>({});
const validate = (f) => {
ruleFormRef.value.validate((valid) => {
f(valid);
});
};
defineExpose({ validate });
const formValue = computed(() => {
var obj = {};
for (let item of props.formList) {
obj[item.id] = item.props.value;
if (item.type === 'Layout') {
let subList = item.props.value;
var d = [];
for (var array of subList) {
var v = {};
for (var subItem of array) {
let value = subItem.props.value;
v[subItem.id] = value;
}
d.push(v);
}
obj[item.id] = d;
}
}
return obj;
});
</script>
<template>
<el-form label-position="top" :rules="rules" :model="formValue" ref="ruleFormRef">
<template v-for="(item, index) in formList">
<el-form-item
v-if="item.perm != 'H'"
:label="item.name + (item.props.unit ? '(' + item.props.unit + ')' : '')"
:prop="!item ? '' : item.id"
:required="!item ? false : item.required"
>
<component
style="width: 100%"
@addLayoutOneItem="addLayoutOneItem"
@deleteLayoutOneItem="deleteLayoutOneItem"
:is="getFormWidget(item.type)"
mode="RUN"
:ref="'form' + item.id"
:form="item"
></component>
</el-form-item>
</template>
</el-form>
</template>
@@ -0,0 +1,101 @@
<script setup lang="ts">
import selectShow from '/@/components/OrgSelector/index.vue';
let props = defineProps({
nodeUser: {
type: Object,
dafault: () => {},
},
row: {
type: Array,
dafault: () => [],
},
disableSelect: {
type: Boolean,
default: false,
},
});
const { proxy } = getCurrentInstance();
import { Check, Plus, Refresh } from '@element-plus/icons-vue';
</script>
<template>
<div style="padding: 10px">
<el-timeline :reverse="false">
<el-timeline-item
v-for="(node, index) in row"
:key="index"
size="large"
:color="node.status != 2 ? (node.status == 1 ? 'pink' : 'green') : 'blue'"
:icon="node.status == 2 ? Check : node.status == 1 ? Plus : Refresh"
>
<template v-if="node.selectUser && (!nodeUser[node.id] || nodeUser[node.id]?.length == 0)">
<p style="color: red">
{{ node.name }}
<template v-if="node.placeholder && node.placeholder.length > 0">[{{ node.placeholder }}]</template>
</p>
</template>
<template v-else>
<p>
{{ node.name }}
<template v-if="node.placeholder && node.placeholder.length > 0">[{{ node.placeholder }}]</template>
</p>
</template>
<!-- 渲染用户头像列表-->
<div v-if="node.userVoList && node.userVoList.length > 0" style="display: flex; flex-direction: row; flex-wrap: wrap">
<div
class="box-card"
v-for="(item1, index1) in node.userVoList"
:key="index1"
style="margin-bottom: 10px; border: 0px solid red; width: 40px; text-align: center"
>
<div class="node-show">
<div style="overflow: hidden">
<div class="d1">
<div>
<upload-img v-model:image-url="item1.avatar" width="30px" height="30px"></upload-img>
</div>
<div style="font-size: 8px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; text-align: center">
{{ item1.name }}
</div>
</div>
</div>
</div>
</div>
</div>
<!-- 渲染审批评论-->
<template v-for="(item1, index1) in node.userVoList" :key="index1">
<template v-if="item1.approveDesc?.length > 0">
<div style="display: flex; flex-direction: row">
<div style="width: 40px; text-align: center">
<upload-img v-model:image-url="item1.avatar" disabled width="30px" height="30px"></upload-img>
</div>
<div style="height: 40px; line-height: 40px; font-size: 10px">
{{ item1.name }}
</div>
<div style="height: 40px; line-height: 40px; font-size: 10px">(添加了评论) {{ item1.showTime }}</div>
</div>
<div class="box-card" style="margin-bottom: 10px; padding: 5px; background-color: var(--el-fill-color-light)">
{{ item1.approveDesc }}
</div>
</template>
</template>
<!-- 选择用户-->
<template v-if="node.selectUser">
<select-show :disabled="disableSelect" v-model:orgList="nodeUser[node.id]" type="user" :multiple="node.multiple"></select-show>
</template>
<el-tabs v-if="node.branch.length > 0" type="border-card">
<el-tab-pane v-for="(node1, index1) in node.branch" :label="'分支' + (index1 + 1)" :name="index1 + ''" :key="index1">
<template v-if="node1.placeholder && node1.placeholder.length > 0">[{{ node1.placeholder }}]</template>
<div style="padding: 0px 5px">
<flow-node-format :node-user="nodeUser" :disableSelect="disableSelect" :row="node1.children"></flow-node-format>
</div>
</el-tab-pane>
</el-tabs>
</el-timeline-item>
</el-timeline>
</div>
</template>
@@ -0,0 +1,92 @@
<script setup lang="ts">
import FlowNodeFormat from '/@/views/flow/form/tools/FlowNodeFormat.vue';
import { formatStartNodeShow } from '/@/api/flow/task';
import { defineExpose, onMounted, ref, watch } from 'vue';
let props = defineProps({
flowId: {
type: String,
default: '',
},
disableSelect: {
type: Boolean,
default: false,
},
taskId: {
type: String,
default: '',
},
processInstanceId: {
type: String,
default: '',
},
formData: {
type: Object,
dafault: () => {},
},
selectUserNodeId: {
type: Array,
dafault: () => [],
},
});
const row = ref([]);
const queryData = (p) => {
var data = {
flowId: props.flowId,
processInstanceId: props.processInstanceId,
paramMap: p,
taskId: props.taskId,
};
formatStartNodeShow(data).then((res) => {
row.value = res.data;
});
};
watch(
() => props.formData,
(val) => {
setTimeout(function () {
if (new Date().getTime() - formDataChangeTime.value > 500) {
formDataChangeTime.value = new Date().getTime();
queryData(val);
}
}, 600);
}
);
const formDataChangeTime = ref();
onMounted(() => {
formDataChangeTime.value = new Date().getTime();
queryData({});
});
const validate = () => {
for (var k of props.selectUserNodeId) {
var d = nodeUser.value[k];
if (d && d.length > 0) {
} else {
return false;
}
}
return true;
};
const nodeUser = ref({});
const formatSelectNodeUser = () => {
var obj = {};
for (var k of props.selectUserNodeId) {
var d = nodeUser.value[k];
obj[k + '_assignee_select'] = d;
}
return obj;
};
defineExpose({ validate, formatSelectNodeUser });
</script>
<template>
<flow-node-format :row="row" :node-user="nodeUser" :disableSelect="disableSelect" ref="flowNodeFormatRef"></flow-node-format>
</template>
+158
View File
@@ -0,0 +1,158 @@
<template>
<div>
<el-dialog v-model="dialogTableVisible" title="发起流程" width="800px" destroy-on-close>
<el-row>
<el-col :span="12">
<el-form label-position="top">
<form-render
@addLayoutOneItem="addLayoutOneItem"
ref="formRenderRef"
@deleteLayoutOneItem="deleteLayoutOneItem"
:form-list="currentOpenFlowForm"
></form-render>
</el-form>
<div class="flex justify-center mt-4">
<el-button @click="dialogTableVisible = false">取消</el-button>
<el-button type="primary" @click="submitProcess"> 提交 </el-button>
</div>
</el-col>
<el-col :span="12">
<flow-node-format
:formData="formValue"
:selectUserNodeId="selectUserNodeId"
:flow-id="currentOpenFlow.flowId"
ref="flowNodeFormatRef"
></flow-node-format>
</el-col>
</el-row>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import FormRender from '/@/views/flow/form/render/FormRender.vue';
import FlowNodeFormat from '/@/views/flow/form/tools/FlowNodeFormatData.vue';
import { FormVO } from '/@/api/flow/form/types';
import { getFlowDetail, startFlow } from '/@/api/flow/flow';
import other from '/@/utils/other';
import { ElMessage } from 'element-plus';
const dialogTableVisible = ref<Boolean>(false);
const currentOpenFlowForm = ref<FormVO[]>([]);
const currentOpenFlow = ref<FormVO[]>([]);
const formRenderRef = ref();
const submitProcess = () => {
let validate = flowNodeFormatRef.value.validate();
if (!validate) {
ElMessage.warning('请选择节点执行人');
return;
}
let param = flowNodeFormatRef.value.formatSelectNodeUser();
currentOpenFlowForm.value.forEach((res) => (param[res.id] = res.props.value));
{
for (var item of currentOpenFlowForm.value) {
param[item.id] = item.props.value;
if (item.type === 'Layout') {
let subList = item.props.value;
var d = [];
for (var array of subList) {
var v = {};
for (var subItem of array) {
let value = subItem.props.value;
v[subItem.id] = value;
}
d.push(v);
}
param[item.id] = d;
}
}
}
var data = {
flowId: currentOpenFlow.value.flowId,
paramMap: param,
};
formRenderRef.value.validate(function (valid) {
if (valid) {
startFlow(data).then((res) => {
ElMessage.success('提交成功');
dialogTableVisible.value = false;
});
}
});
};
const handle = (row) => {
currentOpenFlow.value = row;
startProcess(row);
};
defineExpose({ handle });
const selectUserNodeId = ref<String[]>([]);
const startProcess = (f) => {
getFlowDetail(f.flowId).then((res) => {
const { data } = res;
const { formItems } = data;
let formIteamJsonArray = JSON.parse(formItems);
for (var fi of formIteamJsonArray) {
if (fi.type === 'Layout') {
var arr = [];
let value = fi.props.value;
arr.push(value);
fi.props.value = arr;
fi.props.oriForm = other.deepClone(value);
}
}
currentOpenFlowForm.value = formIteamJsonArray;
selectUserNodeId.value = data.selectUserNodeId;
dialogTableVisible.value = true;
});
};
const formValue = computed(() => {
var obj = {};
for (var item of currentOpenFlowForm.value) {
obj[item.id] = item.props.value;
}
return obj;
});
//明细的添加一个
const addLayoutOneItem = (id) => {
for (var item of currentOpenFlowForm.value) {
if (item.id !== id) {
continue;
}
let value = item.props.value;
let oriForm = item.props.oriForm;
value.push(other.deepClone(oriForm));
item.props.value = value;
}
};
const deleteLayoutOneItem = (id, index) => {
for (var item of currentOpenFlowForm.value) {
if (item.id !== id) {
continue;
}
item.props.value.splice(index, 1);
}
};
const flowNodeFormatRef = ref();
</script>
@@ -0,0 +1,244 @@
import other from "/@/utils/other";
export function inputValidate(configValue: any, proxy: any) {
let minLength = configValue.props.minLength;
let maxLength = configValue.props.maxLength;
let value = configValue.props.value;
let regex = configValue.props.regex;
let regexDesc = configValue.props.regexDesc;
if (minLength && maxLength && maxLength < minLength) {
return {
valid: false,
msg: configValue.name + ':长度设置错误',
};
}
if (regex) {
if (!regexDesc) {
return {
valid: false,
msg: configValue.name + ':请填写正则表达式提示语',
};
}
}
if (value) {
if (minLength) {
if (value.length < minLength) {
return {
valid: false,
msg: configValue.name + ':默认值长度不能小于' + minLength,
};
}
}
if (maxLength) {
if (value.length > maxLength) {
return {
valid: false,
msg: configValue.name + ':默认值长度不能大于' + maxLength,
};
}
}
if (regex) {
var reg = new RegExp(regex, 'g');
if (!reg.test(value)) {
return {
valid: false,
msg: configValue.name + ':默认值不符合正则表达式',
};
}
}
}
return {
valid: true,
};
}
export function descriptionValidate(configValue: any, proxy: any) {
let placeHolder = configValue.placeholder;
if (!placeHolder) {
return {
valid: false,
msg: configValue.name + ':请设置提示',
};
}
return {
valid: true,
};
}
export function numberValidate(configValue: any, proxy: any) {
let min = configValue.props.min;
let max = configValue.props.max;
let defaultValue = configValue.props.value;
let radixNum = configValue.props.radixNum;
if (min && max && max < min) {
return {
valid: false,
msg: configValue.name + ':值范围设置错误',
};
}
if (radixNum) {
if (min) {
let num = other.getNumberRadixNum(min);
if (num > radixNum) {
return {
valid: false,
msg: configValue.name + ':最小值小数点位数不能超过' + radixNum,
};
}
}
if (max) {
let num = other.getNumberRadixNum(max);
if (num > radixNum) {
return {
valid: false,
msg: configValue.name + ':最大值小数点位数不能超过' + radixNum,
};
}
}
}
if (defaultValue) {
if (radixNum) {
let num = other.getNumberRadixNum(defaultValue);
if (num > radixNum) {
return {
valid: false,
msg: configValue.name + ':默认值小数点位数不能超过' + radixNum,
};
}
}
if (min) {
if (defaultValue < min) {
return {
valid: false,
msg: configValue.name + ':默认值不能小于' + min,
};
}
}
if (max) {
if (defaultValue > max) {
return {
valid: false,
msg: configValue.name + ':默认值不能大于' + max,
};
}
}
}
return {
valid: true,
};
}
export function layoutValidate(configValue: any, proxy: any) {
let min = configValue.props.min;
let max = configValue.props.max;
if (min && max && max < min) {
return {
valid: false,
msg: configValue.name + ':数量范围设置错误',
};
}
let value = configValue.props.value;
if (value.length == 0) {
return {
valid: false,
msg: configValue.name + ':内部表单不能为空',
};
}
for (var item of value) {
let formValidateDictElement = formValidateDict[item.type];
if (formValidateDictElement) {
let result = formValidateDictElement(item, proxy);
if (!result.valid) {
return {
valid: false,
msg: configValue.name + ':' + result.msg,
};
}
}
}
return {
valid: true,
};
}
export function selectValidate(configValue: any, proxy: any) {
var options = configValue.props.options;
if (!options) {
options = [];
}
if (options.length < 1) {
return {
valid: false,
msg: configValue.name + ':请设置选项',
};
}
{
var keyList = options.map((w) => w.key);
let newList = Array.from(new Set(keyList));
if (keyList.length > newList.length) {
return {
valid: false,
msg: configValue.name + ':选项值不能重复',
};
}
}
{
let length = options.filter((res) => !res.key || !res.value).length;
if (length > 0) {
return {
valid: false,
msg: configValue.name + ':选项不能为空',
};
}
}
{
var keyList = options.map((w) => w.value);
let newList = Array.from(new Set(keyList));
if (keyList.length > newList.length) {
return {
valid: false,
msg: configValue.name + ':选项标签不能重复',
};
}
}
return {
valid: true,
};
}
export function fileValidate(configValue: any, proxy: any) {
let min = configValue.props.min;
let max = configValue.props.max;
if (min && max && max < min) {
return {
valid: false,
msg: configValue.name + ':数量设置错误',
};
}
return {
valid: true,
};
}
export let formValidateDict = {
Input: inputValidate,
Textarea: inputValidate,
Number: numberValidate,
Money: numberValidate,
Description: descriptionValidate,
SingleSelect: selectValidate,
MultiSelect: selectValidate,
UploadFile: fileValidate,
Layout: layoutValidate,
UploadImage: fileValidate,
};
@@ -0,0 +1,10 @@
const gets = {} as any;
const modules = import.meta.glob('../../config/*.vue', { eager: true });
for (let each in modules) {
const name = modules[each].default.__name;
gets[name] = (modules[each] as any).default;
}
export default gets;
@@ -0,0 +1,14 @@
const gets = {} as any;
const modules = import.meta.glob('../../*.vue', { eager: true });
async function processModules() {
for (let each in modules) {
const module = await modules[each];
const name = module.default.__name;
gets[name] = module.default;
}
}
processModules();
export default gets;