--添加测试模块
This commit is contained in:
4
src/components/CustomComponent/index.ts
Normal file
4
src/components/CustomComponent/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
import customComponent from './src/CustomComponent.vue';
|
||||
export const CustomComponent = withInstall(customComponent);
|
||||
|
||||
60
src/components/CustomComponent/src/CustomComponent.vue
Normal file
60
src/components/CustomComponent/src/CustomComponent.vue
Normal file
@ -0,0 +1,60 @@
|
||||
<template>
|
||||
<component :is="component" v-model:value="modelValue" v-bind="props"/>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch, defineAsyncComponent, onMounted} from 'vue';
|
||||
const props = defineProps({
|
||||
defaultValue: {
|
||||
type: Object
|
||||
},
|
||||
value: {
|
||||
type: Object
|
||||
},
|
||||
component: Object,
|
||||
formData: Object, //主表
|
||||
row: Object, //子表
|
||||
});
|
||||
const modelValue = ref(null);
|
||||
const component = ref(
|
||||
defineAsyncComponent({
|
||||
loader: () => props.component
|
||||
})
|
||||
);
|
||||
const emit = defineEmits(['update:value', 'change', 'blur']);
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
watch(
|
||||
() => props.defaultValue,
|
||||
(val) => {
|
||||
if(val){
|
||||
emit('update:value', val);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
watch(
|
||||
() => props.value,
|
||||
(val) => {
|
||||
modelValue.value = val;
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
|
||||
watch(
|
||||
modelValue,
|
||||
(val) => {
|
||||
emit('update:value', val);
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
@ -37,7 +37,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item :label="t('绑定字段')" required>
|
||||
<a-select v-model:value="item.bindField" :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select v-model:value="item.bindField" showSearch :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select-option v-for="(field, idx) in tableField" :value="field.name" :key="idx" />
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="(data.type == 'input' && !data.options.isSave) || (!noHaveField.includes(data.type) && data.type !== 'input')" :label="t('绑定字段')">
|
||||
<a-select v-model:value="data.bindField" :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select v-model:value="data.bindField" showSearch :placeholder="t('请选择表字段')" size="mini">
|
||||
<a-select-option v-for="(field, idx) in fieldsInfo" :key="idx" :value="field.name">
|
||||
{{ field.name }}
|
||||
<span>
|
||||
@ -467,6 +467,15 @@
|
||||
<a-form-item v-if="hasKey('span') && (!data.isSubFormChild || !data.isSingleFormChild)" label="标签宽度">
|
||||
<a-input-number v-model:value="data.options.span" :max="24" :min="0" addonAfter="/ 24" @change="handleSpanChange" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="hasKey('mode')" label="模式选择">
|
||||
<a-select
|
||||
ref="select"
|
||||
v-model:value="data.options.mode"
|
||||
>
|
||||
<a-select-option value="">单选</a-select-option>
|
||||
<a-select-option value="multiple">多选</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item v-if="hasKey('range')" :label="t('双滑块模式')">
|
||||
<a-switch v-model:checked="data.options.range" @change="handleSliderModeChange" />
|
||||
|
||||
@ -247,6 +247,7 @@ export const advanceComponents = [
|
||||
showSearch: false,
|
||||
clearable: false,
|
||||
disabled: false,
|
||||
mode: 'multiple',
|
||||
staticOptions: [
|
||||
{
|
||||
key: 1,
|
||||
|
||||
@ -24,6 +24,7 @@ import SelectDepartment from './components/SelectDepartment.vue';
|
||||
import SelectDepartmentV2 from './components/SelectDepartmentV2.vue';
|
||||
import SelectUser from './components/SelectUser.vue';
|
||||
import SelectUserV2 from './components/SelectUserV2.vue';
|
||||
import SelectUserShowTree from './components/SelectUserShowTree.vue';
|
||||
import CommonInfo from './components/CommonInfo.vue';
|
||||
import SelectArea from './components/SelectArea.vue';
|
||||
import AutoCodeRule from './components/AutoCodeRule.vue';
|
||||
@ -66,6 +67,7 @@ import { XjrDatePicker } from '/@/components/DatePicker';
|
||||
import { Slider } from '/@/components/Slider';
|
||||
import { CodeTextArea } from '/@/components/Input';
|
||||
import { OneForOne } from '/@/components/OneForOne';
|
||||
import { CustomComponent } from '/@/components/CustomComponent';
|
||||
import SubForm from './components/SubFormV2.vue';
|
||||
import ErpApply from './components/ErpApply.vue';
|
||||
import ErpUpload from './components/ErpUpload.vue';
|
||||
@ -102,6 +104,7 @@ componentMap.set('Rate', Rate);
|
||||
componentMap.set('DeptTree', SelectDepartment);
|
||||
componentMap.set('Dept', SelectDepartmentV2);
|
||||
componentMap.set('User', SelectUserV2);
|
||||
componentMap.set('UserTree', SelectUserShowTree);
|
||||
componentMap.set('Info', CommonInfo);
|
||||
componentMap.set('Area', SelectArea);
|
||||
componentMap.set('SubForm', SubForm);
|
||||
@ -145,6 +148,7 @@ componentMap.set('ErpApply', ErpApply);
|
||||
componentMap.set('ErpUpload', ErpUpload);
|
||||
componentMap.set('ErpCheck', ErpCheck);
|
||||
componentMap.set('AutoComplete', AutoComplete);
|
||||
componentMap.set('CustomComponent', CustomComponent);
|
||||
|
||||
export function add(compName: ComponentType, component: Component) {
|
||||
componentMap.set(compName, component);
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<div class="field-readonly">
|
||||
<div :class="isWordWrap ? '' : 'field-readonly'" :title="fieldValue">
|
||||
<div v-if="schema.component === 'RichTextEditor'" v-html="htmlValue"> </div>
|
||||
{{ fieldValue }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch } from 'vue';
|
||||
import { ref, watch, computed } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
value: String,
|
||||
@ -17,6 +17,14 @@
|
||||
const textComponents = ['Input', 'AutoCodeRule', 'DatePicker', 'TimePicker', 'Text', 'InputTextArea', 'InputNumber'];
|
||||
const fieldValue = ref(genFieldValue(props.value));
|
||||
const htmlValue = ref(getHtmlValue(props.value));
|
||||
const isWordWrap = computed(() => {
|
||||
const schema = props.schema;
|
||||
const { componentProps } = schema;
|
||||
if (componentProps?.wordWrap) {
|
||||
return componentProps.wordWrap;
|
||||
}
|
||||
return false;
|
||||
})
|
||||
|
||||
function parseRangeVal(val, component) {
|
||||
if (component !== 'RangePicker' || !val) {
|
||||
@ -75,8 +83,10 @@
|
||||
);
|
||||
</script>
|
||||
|
||||
<style>
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: pre-wrap;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
<template>
|
||||
<div class="depart-select" style="width: 100%" @click="show">
|
||||
<a-input v-model:value="departNames" :bordered="bordered" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
|
||||
<div v-if="disabled && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="departNames">
|
||||
{{departNames}}
|
||||
</div>
|
||||
<a-input v-model:value="departNames" v-if="disabledShowBorder || !disabled" :bordered="bordered" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
|
||||
<template v-if="prefix" #prefix>
|
||||
<Icon :icon="prefix" />
|
||||
</template>
|
||||
@ -81,7 +84,16 @@
|
||||
justCompany: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
wordWrap:{
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledShowBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formData:Object
|
||||
});
|
||||
const selectedNodes = ref([]);
|
||||
const loading = ref(true);
|
||||
@ -181,7 +193,11 @@
|
||||
visible.value = true;
|
||||
loading.value = true;
|
||||
if(props.defaultDeptField) {
|
||||
defaultDepts.value = props.row[props.defaultDeptField]
|
||||
if(props.row) {
|
||||
defaultDepts.value = props.row[props.defaultDeptField];
|
||||
}else{
|
||||
defaultDepts.value = props.formData[props.defaultDeptField];
|
||||
}
|
||||
}
|
||||
if (props.value) {
|
||||
if(props.isArray && !props.value.length) {
|
||||
@ -227,6 +243,11 @@
|
||||
}
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
.choose-dep-box {
|
||||
display: flex;
|
||||
height: 95%;
|
||||
|
||||
@ -1,21 +1,24 @@
|
||||
<template>
|
||||
<div class="user-select-list">
|
||||
<div class="user-select-item" v-for="item in data" @click="selectItem(item)">
|
||||
<div class="user-select-item-left" v-if="showDep && item.departmentPathName">
|
||||
{{ `${item.name}(${item.departmentPathName})` }}
|
||||
</div>
|
||||
<div class="user-select-item-left" v-else>
|
||||
{{ `${item.name}` }}
|
||||
</div>
|
||||
<div class="user-select-item-right">
|
||||
<div class="select-circle" :class="item.selected ? 'selected' : ''" v-if="!viewList">
|
||||
<check-outlined v-if="item.selected" />
|
||||
<template v-for="item in data">
|
||||
<div class="user-select-item" @click="selectItem(item)" :class="isDisabledItem(item) ? 'disabled-item' : ''">
|
||||
<div class="user-select-item-left" v-if="showDep && item.departmentPathName">
|
||||
{{ `${item.name}(${item.departmentPathName})` }}
|
||||
</div>
|
||||
<div class="delete-circle" v-if="canDel" @click="delItem(item)">
|
||||
<CloseOutlined />
|
||||
<div class="user-select-item-left" v-else>
|
||||
{{ `${item.name}` }}
|
||||
</div>
|
||||
<div class="user-select-item-right" v-if="!isDisabledItem(item)">
|
||||
<div class="select-circle" :class="item.selected ? 'selected' : ''" v-if="!viewList">
|
||||
<check-outlined v-if="item.selected" />
|
||||
</div>
|
||||
<div class="delete-circle" v-if="canDel" @click="delItem(item)">
|
||||
<CloseOutlined />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="empty-box" v-if="!data.length">
|
||||
<a-empty :image="simpleImage">
|
||||
<template #description>
|
||||
@ -58,8 +61,18 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledSelectList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
})
|
||||
function isDisabledItem(item) {
|
||||
return props.disabledSelectList.includes(item.id)
|
||||
}
|
||||
function selectItem(item) {
|
||||
if(isDisabledItem(item)) {
|
||||
return
|
||||
}
|
||||
emits('selectId', item.id)
|
||||
}
|
||||
function delItem(item) {
|
||||
@ -83,6 +96,9 @@ function delItem(item) {
|
||||
// margin: auto;
|
||||
}
|
||||
}
|
||||
.disabled-item {
|
||||
background: #e3e3e3;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -102,11 +118,14 @@ function delItem(item) {
|
||||
overflow: hidden; //超出隐藏
|
||||
text-overflow: ellipsis; //文本超出时显示省略号
|
||||
white-space: nowrap; //设置文本不换行
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-select-item-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 20px;
|
||||
|
||||
.select-circle {
|
||||
width: 16px;
|
||||
|
||||
143
src/components/Form/src/components/SelectUserShowTree.vue
Normal file
143
src/components/Form/src/components/SelectUserShowTree.vue
Normal file
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div class="selectUser-show-tree">
|
||||
<div class="content">
|
||||
<div class="user-list" v-if="treeData.length" :style="{height: height, width: width}">
|
||||
<a-tree v-model:expandedKeys="expandedKeys" :treeData="treeData" :key="treeKey" :fieldNames="{
|
||||
children: 'children',
|
||||
title: 'name',
|
||||
key: 'id'
|
||||
}">
|
||||
</a-tree>
|
||||
</div>
|
||||
<a-button style="margin-top: 10px" @click="showDialog" v-if="!disabled">选择用户</a-button>
|
||||
</div>
|
||||
<SelectUserV2
|
||||
ref="selectUser"
|
||||
v-model:value="selected"
|
||||
v-bind="filteredProps"
|
||||
justDialog
|
||||
@change="changeSelect"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import SelectUserV2 from './SelectUserV2.vue'
|
||||
import { getUserByDepartTree } from '/@/api/system/user';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
|
||||
const emits = defineEmits(['update:value', 'change']);
|
||||
const props = defineProps({
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
prefix: String,
|
||||
suffix: String,
|
||||
placeholder: String,
|
||||
readonly: Boolean,
|
||||
disabled: Boolean,
|
||||
size: String,
|
||||
justDialog: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
row: Object, // 行数据,在表格里用的到
|
||||
sepTextField: String, // 将文本表示存储在独立字段
|
||||
onlyUserCompany: { // 仅在登录用户公司范围内筛选
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
buttonShow: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultDeptField: { // 默认选择公司范围key值
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
justCompany: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isOnlyCurrentDepartment: { // 仅当前部门,无下级部门
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: '500px'
|
||||
},
|
||||
width: {
|
||||
type: String,
|
||||
default: '100%'
|
||||
}
|
||||
});
|
||||
|
||||
// 过滤掉value属性,避免冲突
|
||||
const filteredProps = computed(() => {
|
||||
const { value, ...rest } = props;
|
||||
return rest;
|
||||
});
|
||||
const treeKey = ref(1)
|
||||
|
||||
const selected = ref(props.value || [])
|
||||
const selectUser = ref()
|
||||
|
||||
const expandedKeys = ref([])
|
||||
|
||||
const treeData = ref([])
|
||||
|
||||
const changeSelect = async (ids, options) => {
|
||||
emits('update:value', ids)
|
||||
emits('change', ids, options)
|
||||
}
|
||||
|
||||
async function getTreeData(list) {
|
||||
let tree = await getUserByDepartTree(list)
|
||||
expandedKeys.value = []
|
||||
setTreeData(tree)
|
||||
treeData.value = tree
|
||||
treeKey.value = Date.now()
|
||||
}
|
||||
|
||||
const setTreeData = (list, indexList = []) => {
|
||||
list.forEach((item, index) => {
|
||||
let list = cloneDeep(indexList)
|
||||
list.push(index)
|
||||
item.id = list.join('-')
|
||||
expandedKeys.value.push(item.id)
|
||||
if(item.children && item.children.length){
|
||||
setTreeData(item.children, cloneDeep(list))
|
||||
} else if (item.users.length) {
|
||||
item.children = item.users
|
||||
}
|
||||
})
|
||||
}
|
||||
const showDialog = () => {
|
||||
selectUser.value.show()
|
||||
}
|
||||
// 监听props.value变化
|
||||
watch(() => props.value, (newVal) => {
|
||||
selected.value = newVal
|
||||
if(selected.value) {
|
||||
getTreeData(selected.value)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.selectUser-show-tree {
|
||||
.content{
|
||||
.user-list {
|
||||
overflow: auto;
|
||||
border: 1px solid #e5e5e5;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,7 +1,10 @@
|
||||
<template>
|
||||
<div :class="{ disabled }" class="form-select-user" @click="show">
|
||||
<a-button type="primary" v-if="buttonShow">{{ t('添加人员') }}</a-button>
|
||||
<a-input v-model:value="userNames" v-if="!buttonShow" :disabled="disabled" :placeholder="placeholder" :size="size" readonly>
|
||||
<a-button type="primary" v-if="buttonShow && !justDialog">{{ t('添加人员') }}</a-button>
|
||||
<div v-if="disabled && !buttonShow && !justDialog && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="userNames">
|
||||
{{userNames}}
|
||||
</div>
|
||||
<a-input v-model:value="userNames" v-if="!buttonShow && !justDialog && (disabledShowBorder || !disabled)" :placeholder="placeholder" :size="size" readonly>
|
||||
<template v-if="prefix" #prefix>
|
||||
<Icon :icon="prefix" />
|
||||
</template>
|
||||
@ -9,7 +12,7 @@
|
||||
<Icon :icon="suffix" />
|
||||
</template>
|
||||
</a-input>
|
||||
<ModalPanel :title="t('选择人员')" :visible="visible" :width="900" class="select-user-model" @close="close" @submit="submit">
|
||||
<ModalPanel :title="t('选择人员')" :visible="visible" :width="900" class="select-user-model" @close="close" @submit="submit" :confirmLoading="confirmLoading">
|
||||
<div class="select-user">
|
||||
<div class="select-user-left">
|
||||
<a-tabs v-model:activeKey="activeKey" :tabBarGutter="20">
|
||||
@ -29,20 +32,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="user-select-box">
|
||||
<div class="select-depart-all" @click="selectAll">
|
||||
<div class="select-depart-all" @click="selectAll" v-if="multiple">
|
||||
<span>全选</span>
|
||||
<div class="select-circle" :class="departAllSelected ? 'selected' : ''" v-if="!viewList">
|
||||
<check-outlined v-if="departAllSelected" />
|
||||
</div>
|
||||
</div>
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchDepartMemberList" emptyDescription="暂无人员" @selectId="changeDepMemberSelect"></SelectUserListV2>
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchDepartMemberList" emptyDescription="暂无人员" @selectId="changeDepMemberSelect" :disabledSelectList="disabledSelectList"></SelectUserListV2>
|
||||
<div v-if="false" class="user-select-pagination">
|
||||
<a-pagination v-model:current="searchDepartMemberParams.limit" :pageSize="searchDepartMemberParams.size" :total="searchDepartMemberTotal" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="activeKey === 'allPerson'" class="all-user-select-box">
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchAllMemberList" @selectId="changeMemberSelect" show-dep></SelectUserListV2>
|
||||
<SelectUserListV2 :multiple="multiple" :data="searchAllMemberList" @selectId="changeMemberSelect" show-dep :disabledSelectList="disabledSelectList"></SelectUserListV2>
|
||||
<div v-if="searchAllMemberTotal > 25" class="all-user-select-pagination">
|
||||
<a-form-item label="" name="pagination">
|
||||
<a-pagination
|
||||
@ -64,14 +67,14 @@
|
||||
<!-- <div class="selected-user-title sub-title">
|
||||
已选列表
|
||||
</div> -->
|
||||
<SelectUserListV2 :data="selectedMemberList" canDel emptyDescription="暂无已选择人员,<br> 请从左侧添加人员" viewList @delId="delMember"></SelectUserListV2>
|
||||
<SelectUserListV2 :data="selectedMemberList" canDel emptyDescription="暂无已选择人员,<br> 请从左侧添加人员" viewList @delId="delMember" :disabledSelectList="disabledSelectList"></SelectUserListV2>
|
||||
</div>
|
||||
</div>
|
||||
</ModalPanel>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { watch, ref } from 'vue';
|
||||
import { watch, ref, defineExpose } from 'vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { ModalPanel } from '/@/components/ModalPanel/index';
|
||||
@ -104,6 +107,10 @@
|
||||
readonly: Boolean,
|
||||
disabled: Boolean,
|
||||
size: String,
|
||||
justDialog: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@ -129,6 +136,26 @@
|
||||
isOnlyCurrentDepartment: { // 仅当前部门,无下级部门
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
submitClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabledShowBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
wordWrap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
lastSelectedDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
confirmLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
let timeoutId = null;
|
||||
@ -136,6 +163,7 @@
|
||||
const searchPlaceholder = '请输入姓名或工号搜索';
|
||||
const defaultDepts = ref('')
|
||||
const departAllSelected = ref(false)
|
||||
const disabledSelectList = ref([])
|
||||
if(props.onlyUserCompany) {
|
||||
const userStore = useUserStore();
|
||||
const userInfo = userStore.getUserInfo;
|
||||
@ -180,6 +208,9 @@
|
||||
watch(
|
||||
props,
|
||||
async () => {
|
||||
if(props.lastSelectedDisabled) {
|
||||
disabledSelectList.value = props.value.split(',')
|
||||
}
|
||||
if (props.value && !valChanged.value && props.sepTextField) {
|
||||
const idArr = props.value.split(',');
|
||||
let valStr = props.row[camelCaseString(props.sepTextField)];
|
||||
@ -196,7 +227,7 @@
|
||||
resetMemberList = cloneDeep(initValue);
|
||||
return;
|
||||
}
|
||||
if (props.value && !resetMemberList.length) {
|
||||
if (props.value && !Array.isArray(props.value) && !resetMemberList.length) {
|
||||
const list = await getUserMulti(props.value);
|
||||
selectedMemberList.value = list;
|
||||
resetMemberList = cloneDeep(list);
|
||||
@ -282,11 +313,17 @@
|
||||
async function getUserList(params) {
|
||||
return await getUserPageListNew(params);
|
||||
}
|
||||
function isDisabledItem(item) {
|
||||
return disabledSelectList.value.includes(item.id)
|
||||
}
|
||||
|
||||
function selectAll() {
|
||||
departAllSelected.value = !departAllSelected.value
|
||||
if(departAllSelected.value) {
|
||||
searchDepartMemberList.value.forEach(item => {
|
||||
if(isDisabledItem(item)) {
|
||||
return
|
||||
}
|
||||
if(!item.selected) {
|
||||
selectedMemberList.value.push(item)
|
||||
}
|
||||
@ -294,6 +331,9 @@
|
||||
})
|
||||
} else {
|
||||
searchDepartMemberList.value.forEach(item => {
|
||||
if(isDisabledItem(item)) {
|
||||
return
|
||||
}
|
||||
if(item.selected) {
|
||||
selectedMemberList.value = selectedMemberList.value.filter(m => m.id !== item.id)
|
||||
}
|
||||
@ -380,7 +420,9 @@
|
||||
emits('selectedId', ids);
|
||||
emits('change', ids, selectedMemberList.value);
|
||||
valChanged.value = false;
|
||||
close();
|
||||
if(props.submitClose) {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
function close() {
|
||||
@ -390,6 +432,10 @@
|
||||
searchDepartMemberList.value = [];
|
||||
searchAllMemberList.value = [];
|
||||
}
|
||||
defineExpose({
|
||||
show,
|
||||
close
|
||||
})
|
||||
</script>
|
||||
<style lang="less">
|
||||
.select-user-model {
|
||||
@ -407,6 +453,11 @@
|
||||
justify-content: space-between;
|
||||
color: rgba(144, 147, 153, 0.7);
|
||||
}
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
|
||||
.select-user {
|
||||
display: flex;
|
||||
|
||||
@ -52,22 +52,33 @@
|
||||
|
||||
<template v-else-if="column.key !== 'index'">
|
||||
<component
|
||||
v-show="column.width!==0"
|
||||
:is="componentMap.get(column.componentType)"
|
||||
:key="column.dataIndex + record['_key_']"
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:title="readonlySupport(column.componentType) ? record[column.dataIndex] : ''"
|
||||
:bordered="showComponentBorder"
|
||||
:index="index"
|
||||
:mainKey="mainKey"
|
||||
:row="record"
|
||||
v-bind="getComponentsProps(column.componentProps, column.dataIndex, record, index)"
|
||||
v-bind="{...getComponentsProps(column.componentProps, column.dataIndex, record, index), placeholder: getComponentsProps(column.componentProps, column.dataIndex, record, index).disabled ? '' : getComponentsProps(column.componentProps, column.dataIndex, record, index).placeholder}"
|
||||
@blur="onFieldBlur(column, record, index)"
|
||||
@change="onFieldChange(column, record, index)"
|
||||
/>
|
||||
</template>
|
||||
</FormItem>
|
||||
</template>
|
||||
<template v-if="column.key === 'action' && !disabled">
|
||||
<MinusCircleOutlined style="padding-bottom: 20px" @click="remove(record)" />
|
||||
<template v-if="column.key === 'action'">
|
||||
<template v-if="column.actions?.length > 0">
|
||||
<div class="tag-wrapper">
|
||||
<template v-for="action in column.actions">
|
||||
<a-tag class="custom-tag" v-if="showButton(action.show,{record,records:data,formModel,disabled})" @click="action.type === 'delete' ? remove(record) : action.click({record,records:data,formModel,disabled})" :color="action?.color ? action.color : 'blue'">{{action.label}}</a-tag>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else-if="!disabled">
|
||||
<MinusCircleOutlined style="padding-bottom: 20px" @click="remove(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@ -92,6 +103,7 @@
|
||||
:isSubFormUse="true"
|
||||
:params="{ itemId }"
|
||||
popupType="preload"
|
||||
:backPagination="backPagination"
|
||||
@submit="renderSubFormList"
|
||||
/>
|
||||
</FormItemRest>
|
||||
@ -182,6 +194,8 @@
|
||||
},
|
||||
// 是否开启分页
|
||||
showPagination: Boolean,
|
||||
//后端分页
|
||||
backPagination: Boolean,
|
||||
/**
|
||||
* 选数据按钮名称
|
||||
*/
|
||||
@ -197,7 +211,8 @@
|
||||
//add after hooks
|
||||
addAfter: Function,
|
||||
//表头合并数据
|
||||
multipleHeads: { type: Array as PropType<MutipleHeadInfo[]> }
|
||||
multipleHeads: { type: Array as PropType<MutipleHeadInfo[]> },
|
||||
backPagination: { type: Boolean }
|
||||
});
|
||||
const data = ref<Recordable[]>([]);
|
||||
|
||||
@ -207,14 +222,18 @@
|
||||
const headColums = ref<MutipleHeadInfo[]>([]); // 多表头
|
||||
const originHeads = ref<MutipleHeadInfo[]>([]); // 多表头源数据
|
||||
const columns = ref<SubFormColumn[]>(props.columns);
|
||||
const allColumns = ref([])
|
||||
|
||||
// 注入表单数据
|
||||
const formModel = inject<any>('formModel', null);
|
||||
function readonlySupport(name) {
|
||||
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker)$/.test(name);
|
||||
}
|
||||
|
||||
const onFieldChange = (column, row, rowIndex) => {
|
||||
const evt = column?.componentProps?.events?.change;
|
||||
if (evt) {
|
||||
evt({ column, row, rowIndex, formModel });
|
||||
evt({ column, row, rowIndex, formModel, columns: headColums.length > 0 ? headColums : columns });
|
||||
}
|
||||
};
|
||||
|
||||
@ -235,7 +254,7 @@
|
||||
}
|
||||
|
||||
function addDataKey(rows) {
|
||||
rows.forEach((row) => {
|
||||
rows?.forEach((row) => {
|
||||
if (!row['_key_']) {
|
||||
row['_key_'] = buildUUID();
|
||||
}
|
||||
@ -255,6 +274,7 @@
|
||||
});
|
||||
}
|
||||
setColWidth(columns);
|
||||
allColumns.value = cloneDeep(columns.value)
|
||||
columns.value = filterColum(columns.value);
|
||||
nextTick(() => {
|
||||
//处理多表头
|
||||
@ -271,6 +291,7 @@
|
||||
watch(
|
||||
() => props.columns,
|
||||
(val) => {
|
||||
allColumns.value = cloneDeep(val)
|
||||
columns.value = filterColum(val);
|
||||
setColWidth(columns);
|
||||
}
|
||||
@ -382,11 +403,20 @@
|
||||
emit('update:value', unref(data));
|
||||
};
|
||||
|
||||
const showButton=(show,obj)=>{
|
||||
if(show===undefined||show===null||show===''||show===true||show==='true'){
|
||||
return true;
|
||||
}else if(isFunction(show)){
|
||||
return show(obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const renderSubFormList = async (list) => {
|
||||
list?.forEach((x) => {
|
||||
const dataObj = {};
|
||||
columns.value?.map((item) => {
|
||||
if (!item?.dataIndex) return;
|
||||
allColumns.value?.map((item) => {
|
||||
if (!item?.dataIndex || !item.componentProps.isShow) return;
|
||||
dataObj[item.dataIndex as string] = item.componentProps?.prestrainField ? x[item.componentProps.prestrainField] : null;
|
||||
});
|
||||
|
||||
@ -652,4 +682,14 @@
|
||||
.tbl-toolbar {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.tag-wrapper{
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.custom-tag{
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -168,6 +168,10 @@
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
/**
|
||||
* 按钮组 [{name: '按钮', click: function(){}}]
|
||||
*/
|
||||
buttons: Array,
|
||||
// 是否开启分页
|
||||
showPagination: Boolean,
|
||||
/**
|
||||
@ -197,6 +201,7 @@
|
||||
const headColums = ref<MutipleHeadInfo[]>([]); // 多表头
|
||||
const originHeads = ref<MutipleHeadInfo[]>([]); // 多表头源数据
|
||||
const columns = ref<SubFormColumn[]>(props.columns);
|
||||
const allColumns = ref([])
|
||||
|
||||
// 注入表单数据
|
||||
const formModel = inject<any>('formModel', null);
|
||||
@ -224,6 +229,9 @@
|
||||
}
|
||||
|
||||
function addDataKey(rows) {
|
||||
if (!rows) {
|
||||
return [];
|
||||
}
|
||||
rows.forEach((row) => {
|
||||
if (!row['_key_']) {
|
||||
row['_key_'] = Math.random();
|
||||
@ -244,6 +252,7 @@
|
||||
});
|
||||
}
|
||||
setColWidth(columns);
|
||||
allColumns.value = cloneDeep(columns.value)
|
||||
columns.value = filterColum(columns.value);
|
||||
nextTick(() => {
|
||||
//处理多表头
|
||||
@ -260,6 +269,7 @@
|
||||
watch(
|
||||
() => props.columns,
|
||||
(val) => {
|
||||
allColumns.value = cloneDeep(val)
|
||||
columns.value = filterColum(val);
|
||||
setColWidth(columns);
|
||||
}
|
||||
@ -327,6 +337,29 @@
|
||||
}
|
||||
});
|
||||
}
|
||||
const removeById = (id) => {
|
||||
data.value = deleteNodeById(data.value, id);
|
||||
};
|
||||
function deleteNodeById(tree, idToDelete) {
|
||||
function deepCloneAndFilter(nodes) {
|
||||
return nodes.map((node) => {
|
||||
if (node.id === idToDelete) {
|
||||
return null; // 当前节点要删除
|
||||
}
|
||||
|
||||
// 递归处理 children
|
||||
|
||||
const newChildren = node.children ? deepCloneAndFilter(node.children) : [];
|
||||
|
||||
return {
|
||||
...node,
|
||||
children: newChildren.length ? newChildren : undefined
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}
|
||||
|
||||
return deepCloneAndFilter(tree);
|
||||
}
|
||||
|
||||
const add = () => {
|
||||
//给各个组件赋默认值
|
||||
@ -364,17 +397,25 @@
|
||||
}
|
||||
};
|
||||
|
||||
const remove = (index) => {
|
||||
data.value.splice(index, 1);
|
||||
emit('change', unref(data));
|
||||
emit('update:value', unref(data));
|
||||
const remove = (record) => {
|
||||
let index;
|
||||
if (typeof record === 'number' || typeof record === 'string') {
|
||||
index = Number(record);
|
||||
} else {
|
||||
index = data.value.findIndex((r) => r._key_ === record._key_);
|
||||
}
|
||||
if (index !== -1 && index < data.value.length) {
|
||||
data.value.splice(index, 1);
|
||||
emit('change', unref(data));
|
||||
emit('update:value', unref(data));
|
||||
}
|
||||
};
|
||||
|
||||
const renderSubFormList = async (list) => {
|
||||
list?.forEach((x) => {
|
||||
const dataObj = {};
|
||||
columns.value?.map((item) => {
|
||||
if (!item?.dataIndex) return;
|
||||
allColumns.value?.map((item) => {
|
||||
if (!item?.dataIndex || !item.componentProps.isShow) return;
|
||||
dataObj[item.dataIndex as string] = item.componentProps?.prestrainField ? x[item.componentProps.prestrainField] : null;
|
||||
});
|
||||
|
||||
@ -562,7 +603,8 @@
|
||||
data,
|
||||
headColums,
|
||||
columns,
|
||||
renderSubFormList
|
||||
renderSubFormList,
|
||||
removeById
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -48,7 +48,7 @@
|
||||
</a-upload>
|
||||
</div>
|
||||
<a-upload
|
||||
:file-list="fileList"
|
||||
:file-list="fileListWithHeader"
|
||||
:maxCount="maxNumber"
|
||||
:accept="accept"
|
||||
:name="name"
|
||||
@ -62,7 +62,7 @@
|
||||
@preview="handlePreview"
|
||||
@click="handleClick"
|
||||
v-else
|
||||
>
|
||||
>
|
||||
<plus-outlined v-if="listType == 'picture-card'" />
|
||||
<div :style="style" v-else>
|
||||
<a-button :loading="loading" :disabled="loading" v-if="!disabled">
|
||||
@ -75,15 +75,45 @@
|
||||
</div>
|
||||
|
||||
<template #itemRender="{ file, actions }">
|
||||
<a-space class="file-space">
|
||||
<template v-if="file.__header&&showDownloadIcon">
|
||||
<div class="file-list-header" style="display: flex; align-items: center; padding: 4px 0;">
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="isAllSelected"
|
||||
@change="toggleSelectAll"
|
||||
style="margin-right: 8px;"
|
||||
/>全选
|
||||
<a-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!selectedIds.length"
|
||||
@click="handleBatchDownload"
|
||||
style="margin-left: 8px;"
|
||||
>批量打包下载</a-button>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-space class="file-space">
|
||||
<input
|
||||
v-if="showDownloadIcon"
|
||||
type="checkbox"
|
||||
:checked="selectedIds.includes(file.id)"
|
||||
@change="e => toggleSelectOne(file.id, e)"
|
||||
style="margin-right: 8px;"
|
||||
/>
|
||||
<PaperClipOutlined/>
|
||||
<span class="file-name-span" @click="actions.preview">{{ file.name }}</span>
|
||||
<a-tooltip v-if="showDownloadIcon" title="下载"><span @click="actions.download" class="file-outlined-span"><DownloadOutlined /></span></a-tooltip>
|
||||
<a-tooltip v-if="!disabled && showRemoveIcon" title="删除"><span @click="actions.remove" class="file-outlined-span"><DeleteOutlined /></span></a-tooltip>
|
||||
<a-tooltip v-if="'.doc,.docx,.xls,.xlsx,.pdf'.includes(file.fileType)" title="编辑文档">
|
||||
<span @click="editFile(file)" class="file-outlined-span"><EditOutlined /></span>
|
||||
<a-tooltip v-if="showDownloadIcon" title="下载">
|
||||
<span @click="actions.download" class="file-outlined-span"><DownloadOutlined /></span>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
<a-tooltip v-if="!disabled && showRemoveIcon" title="删除">
|
||||
<span @click="actions.remove" class="file-outlined-span"><DeleteOutlined /></span>
|
||||
</a-tooltip>
|
||||
<a-tooltip v-if="'.doc,.docx,.xls,.xlsx,.pdf'.includes(file.fileType)" title="编辑文档">
|
||||
<span @click="editFile(file)" class="file-outlined-span"><EditOutlined /></span>
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</a-upload>
|
||||
@ -110,11 +140,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { nextTick, ref, watch } from 'vue';
|
||||
import { nextTick, ref, watch, computed } from 'vue';
|
||||
import { Upload } from 'ant-design-vue';
|
||||
import { UploadOutlined, PlusOutlined, DownloadOutlined, DeleteOutlined, EditOutlined, PaperClipOutlined } from '@ant-design/icons-vue';
|
||||
import { useMessage } from '/@/hooks/web/useMessage';
|
||||
import {deleteSingleFile, getAppToken, getFileList, getOnlineEditUrl} from '/@/api/system/file';
|
||||
import {deleteSingleFile, getAppToken, getFileList, getOnlineEditUrl, getZipFiles} from '/@/api/system/file';
|
||||
import { downloadByUrl } from '/@/utils/file/download';
|
||||
import { uploadMultiApi } from '/@/api/sys/upload';
|
||||
import Icon from '/@/components/Icon/index';
|
||||
@ -122,8 +152,12 @@
|
||||
import { getAppEnvConfig } from '/@/utils/env';
|
||||
import WebOfficeSDK from "/@/assets/libs/open-jssdk-v0.1.3.es.js";
|
||||
import {getToken} from "/@/utils/auth";
|
||||
import { useRoute } from 'vue-router';
|
||||
const route = useRoute();
|
||||
const { VITE_GLOB_UPLOAD_ALERT_TIP } = getAppEnvConfig();
|
||||
|
||||
const { createSuccessModal, } = useMessage();
|
||||
|
||||
const props = defineProps({
|
||||
value: String,
|
||||
maxNumber: Number,
|
||||
@ -346,6 +380,52 @@
|
||||
previewVisible.value = false;
|
||||
previewTitle.value = '';
|
||||
};
|
||||
|
||||
const selectedIds = ref<string[]>([]);
|
||||
const isAllSelected = computed(() => fileList.value.length > 0 && selectedIds.value.length === fileList.value.length);
|
||||
const fileListWithHeader = computed(() => {
|
||||
// 只在有文件时插入头部
|
||||
if (fileList.value.length) {
|
||||
return [{ __header: true, uid: '__header__' }, ...fileList.value];
|
||||
}
|
||||
return fileList.value;
|
||||
});
|
||||
function toggleSelectAll(e: Event) {
|
||||
const checked = (e.target as HTMLInputElement).checked;
|
||||
selectedIds.value = checked ? fileList.value.map(f => f.id) : [];
|
||||
}
|
||||
function toggleSelectOne(id: string, e: Event) {
|
||||
const checked = (e.target as HTMLInputElement).checked;
|
||||
if (checked) {
|
||||
selectedIds.value = [...selectedIds.value, id];
|
||||
} else {
|
||||
selectedIds.value = selectedIds.value.filter(item => item !== id);
|
||||
}
|
||||
}
|
||||
async function handleBatchDownload() {
|
||||
if (!selectedIds.value.length) return;
|
||||
// getZipFiles 返回下载url
|
||||
let formName = '';
|
||||
try {
|
||||
formName = route.query.formName as string || '';
|
||||
// 获取当前页面得form name
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
const res = await getZipFiles({ fileIds: selectedIds.value.join(',') , insertionFileName: formName});
|
||||
if (!res) {
|
||||
notification.error({
|
||||
message: 'Tip',
|
||||
description: '批量下载失败,请稍后重试!',
|
||||
});
|
||||
return;
|
||||
} else if (res.type === 'async') {
|
||||
createSuccessModal({ title: 'Tip', content: res.msg });
|
||||
return;
|
||||
} else if (res.type === 'synced') {
|
||||
downloadByUrl({ url: res.url, fileName: res.name || 'files.zip' });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.list-upload {
|
||||
|
||||
@ -127,6 +127,7 @@ export type ComponentType =
|
||||
| 'DeptTree'
|
||||
| 'Dept'
|
||||
| 'User'
|
||||
| 'UserTree'
|
||||
| 'Info'
|
||||
| 'Area'
|
||||
| 'SubForm'
|
||||
@ -158,6 +159,7 @@ export type ComponentType =
|
||||
| 'ErpCheck'
|
||||
| 'FormView'
|
||||
| 'XjrIframe'
|
||||
| 'CustomComponent'
|
||||
| 'TableLayout';
|
||||
|
||||
/**
|
||||
|
||||
@ -1,15 +1,34 @@
|
||||
<template>
|
||||
<a-modal
|
||||
ref="modalRef"
|
||||
:visible="visible"
|
||||
:title="title"
|
||||
:maskClosable="false"
|
||||
:width="hasLeftSlot ? 1200 : width || 600"
|
||||
:okText="t('确定')"
|
||||
:cancelText="t('取消')"
|
||||
@ok="$emit('submit')"
|
||||
@cancel="$emit('close')"
|
||||
:confirmLoading="confirmLoading"
|
||||
@ok="handleOk"
|
||||
@cancel="handleCancel"
|
||||
:getContainer="draggable ? undefined : 'body'"
|
||||
:wrap-style="draggable ? { overflow: 'hidden' } : {}"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
|
||||
<template #title>
|
||||
<div
|
||||
ref="modalTitleRef"
|
||||
:style="{ width: '100%', cursor: draggable ? 'move' : 'default' }"
|
||||
>
|
||||
{{ title }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #modalRender="{ originVNode }">
|
||||
<div :style="draggable ? transformStyle : {}">
|
||||
<component :is="originVNode" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="content">
|
||||
<div :class="['left', isDeptSelect ? 'left-box' : '']" v-if="hasLeftSlot">
|
||||
<slot name="left"></slot>
|
||||
@ -21,19 +40,99 @@
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { computed, useSlots, ref, watch } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
import { useDraggable } from '@vueuse/core';
|
||||
const { t } = useI18n();
|
||||
defineEmits(['submit', 'close']);
|
||||
defineProps({
|
||||
|
||||
const emit = defineEmits(['submit', 'close']);
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
width: Number,
|
||||
visible: { type: Boolean, default: false },
|
||||
isDeptSelect: { type: Boolean, default: false },
|
||||
confirmLoading: { type: Boolean, default: false },
|
||||
draggable: { type: Boolean, default: false }
|
||||
});
|
||||
|
||||
const modalRef = ref(null);
|
||||
const modalTitleRef = ref(null);
|
||||
const hasLeftSlot = computed(() => {
|
||||
return !!useSlots().left;
|
||||
});
|
||||
|
||||
const startX = ref(0);
|
||||
const startY = ref(0);
|
||||
const startedDrag = ref(false);
|
||||
const transformX = ref(0);
|
||||
const transformY = ref(0);
|
||||
const preTransformX = ref(0);
|
||||
const preTransformY = ref(0);
|
||||
const dragRect = ref({ left: 0, right: 0, top: 0, bottom: 0 });
|
||||
|
||||
const { x, y, isDragging } = useDraggable(modalTitleRef, {
|
||||
enabled: computed(() => props.draggable)
|
||||
});
|
||||
|
||||
const handleOk = () => {
|
||||
emit('submit');
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
emit('close');
|
||||
};
|
||||
|
||||
watch([x, y], () => {
|
||||
if (!props.draggable || !startedDrag.value) {
|
||||
startX.value = x.value;
|
||||
startY.value = y.value;
|
||||
const bodyRect = document.body.getBoundingClientRect();
|
||||
if (modalTitleRef.value) {
|
||||
const titleRect = modalTitleRef.value.getBoundingClientRect();
|
||||
dragRect.value.right = bodyRect.width - titleRect.width;
|
||||
dragRect.value.bottom = bodyRect.height - titleRect.height;
|
||||
}
|
||||
preTransformX.value = transformX.value;
|
||||
preTransformY.value = transformY.value;
|
||||
}
|
||||
startedDrag.value = true;
|
||||
});
|
||||
|
||||
// 监听拖拽状态变化
|
||||
watch(isDragging, (newVal) => {
|
||||
if (!newVal) {
|
||||
startedDrag.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(newVisible) => {
|
||||
if (!newVisible) {
|
||||
transformX.value = 0;
|
||||
transformY.value = 0;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const transformStyle = computed(() => {
|
||||
if (!props.draggable) return {};
|
||||
|
||||
if (startedDrag.value) {
|
||||
transformX.value =
|
||||
preTransformX.value +
|
||||
Math.min(Math.max(dragRect.value.left, x.value), dragRect.value.right) -
|
||||
startX.value;
|
||||
transformY.value =
|
||||
preTransformY.value +
|
||||
Math.min(Math.max(dragRect.value.top, y.value), dragRect.value.bottom) -
|
||||
startY.value;
|
||||
}
|
||||
|
||||
return {
|
||||
transform: `translate(${transformX.value}px, ${transformY.value}px)`,
|
||||
};
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.content {
|
||||
@ -49,6 +148,7 @@
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
<template>
|
||||
<div :class="{ disabled }" class="multiple-popup">
|
||||
<div v-if="disabled && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="popupValue">
|
||||
{{popupValue}}
|
||||
</div>
|
||||
<a-input
|
||||
v-model:value="popupValue"
|
||||
v-if="disabledShowBorder || !disabled"
|
||||
:addonAfter="addonAfter"
|
||||
:addonBefore="addonBefore"
|
||||
:bordered="bordered"
|
||||
@ -35,6 +39,7 @@
|
||||
:mainKey="mainKey"
|
||||
:params="params"
|
||||
:popupType="popupType"
|
||||
:popupTitle="popupTitle"
|
||||
:subTableIndex="index"
|
||||
:valueField="valueField"
|
||||
:backPagination="backPagination"
|
||||
@ -68,6 +73,7 @@
|
||||
|
||||
const props = defineProps({
|
||||
popupType: { type: String },
|
||||
popupTitle: { type: String },
|
||||
value: { type: String },
|
||||
labelField: { type: String, default: 'label' },
|
||||
valueField: { type: String, default: 'value' },
|
||||
@ -247,3 +253,11 @@
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<a-modal centered :width="1250" :visible="props.multipleDialog" :title="title" :destroyOnClose="true"
|
||||
@ok="submitDialog" @cancel="closeDialog" :okText="t('确认')" :cancelText="t('取消')" :bodyStyle="{ padding: '20px' }">
|
||||
@ok="submitDialog" @cancel="closeDialog" :okText="t('确认')" :cancelText="t('取消')" :bodyStyle="{ padding: '20px' }" v-loading="loading">
|
||||
<a-row :gutter="12" style="margin-bottom: 10px">
|
||||
<a-col :span="8">
|
||||
<a-input v-model:value="state.searchText" :placeholder="t('请输入要查询的关键字')" />
|
||||
@ -28,10 +28,10 @@
|
||||
<a-table :dataSource="state.dataSourceList" :columns="state.sourceColumns" :row-selection="{
|
||||
selectedRowKeys: state.selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
}" :pagination="paginationProps" :scroll="{ y: '420px' }" />
|
||||
}" :pagination="paginationProps" :scroll="{ y: '420px' }" :loading="loading"/>
|
||||
</a-tab-pane>
|
||||
<a-tab-pane key="2" :tab="t('已选记录')" force-render>
|
||||
<a-table :dataSource="selectedList" :columns="state.selectedColumns" :scroll="{ y: '400px' }">
|
||||
<a-table :dataSource="selectedList" :columns="state.selectedColumns" :scroll="{ y: '400px' }" :loading="loading">
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'delete'">
|
||||
<Icon icon="ant-design:delete-outlined" color="#f56c6c" @click="deleteSelected(record, index)"
|
||||
@ -41,7 +41,7 @@
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
<a-table v-else-if="popupType === 'associate'" :dataSource="state.dataSourceList" :columns="state.sourceColumns"
|
||||
<a-table v-else-if="popupType === 'associate'" :dataSource="state.dataSourceList" :columns="state.sourceColumns" :loading="loading"
|
||||
:pagination="paginationProps" :row-selection="{
|
||||
selectedRowKeys: state.selectedRowKeys,
|
||||
onChange: onSelectChange,
|
||||
@ -64,6 +64,7 @@ const { t } = useI18n();
|
||||
const props = defineProps({
|
||||
multipleDialog: { type: Boolean },
|
||||
popupType: { type: String },
|
||||
popupTitle: { type: String },
|
||||
dataSourceOptions: { type: Array },
|
||||
params: {
|
||||
type: [Array, Object, String, Number],
|
||||
@ -105,6 +106,7 @@ onMounted(async () => {
|
||||
await getDatasourceList(1);
|
||||
}
|
||||
});
|
||||
const loading = ref(false)
|
||||
|
||||
const state = reactive({
|
||||
selectedRowKeys: [] as any[],
|
||||
@ -127,6 +129,9 @@ const paginationProps = reactive({
|
||||
const formModel = inject<any>('formModel', null);
|
||||
const isCamelCase = inject<boolean>('isCamelCase', false);
|
||||
const title = computed(() => {
|
||||
if(props.popupTitle){
|
||||
return t(props.popupTitle);
|
||||
};
|
||||
switch (props.popupType) {
|
||||
case 'multiple':
|
||||
return t('多选弹层-选择记录');
|
||||
@ -214,6 +219,9 @@ const resetSearch = () => {
|
||||
};
|
||||
const closeDialog = () => {
|
||||
emit('update:multipleDialog', false);
|
||||
selectedList.value = [];
|
||||
state.selectedRowKeys = [];
|
||||
state.dataSourceList = [];
|
||||
};
|
||||
|
||||
const submitDialog = () => {
|
||||
@ -291,12 +299,18 @@ const setFormModel = (isNull?) => {
|
||||
let bindField = !isCamelCase ? item.bindField : camelCaseString(item.bindField);
|
||||
dataObj[bindField as string] = item.prestrainField ? x[item.prestrainField] : null;
|
||||
});
|
||||
if (formModel[table]) formModel[table].push(dataObj);
|
||||
if (formModel[table]) {
|
||||
formModel[table].push(dataObj)
|
||||
} else {
|
||||
formModel[table] = []
|
||||
formModel[table].push(dataObj)
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const getDatasourceList = async (limit = 1) => {
|
||||
loading.value = true;
|
||||
paginationProps.current = limit;
|
||||
let api;
|
||||
if (props.datasourceType) {
|
||||
@ -342,8 +356,8 @@ const getDatasourceList = async (limit = 1) => {
|
||||
}
|
||||
}
|
||||
}
|
||||
loading.value = false;
|
||||
if (!api || !isFunction(api)) return;
|
||||
state.dataSourceList = [];
|
||||
try {
|
||||
let dataParams = {};
|
||||
const pageParams = { order: 'desc', size: 10, limit, keyword: state.searchText };
|
||||
@ -359,9 +373,11 @@ const getDatasourceList = async (limit = 1) => {
|
||||
pageParams,
|
||||
);
|
||||
}
|
||||
loading.value = true;
|
||||
const res = await api(dataParams);
|
||||
state.dataSourceList = res.list;
|
||||
paginationProps.total = Number(res.total);
|
||||
loading.value = false;
|
||||
} catch (error) {
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
<span class="node-name">{{node.activityName}}</span>
|
||||
<a-switch :checked="node.chooseNode" v-if="!node.hiddenNode" style="margin-left: 10px;" @change="agreeNodeChange(node)"></a-switch>
|
||||
</div>
|
||||
<a-form-item :required="(flowNextNodes.length === 1 || node.chooseNode)" v-if="_action === 'agree' && !isEnd" :label="'审批人'">
|
||||
<a-form-item :required="(flowNextNodes.length === 1 || node.chooseNode)" v-if="(_action === 'agree' || _action == 'disagree') && !isEnd" :label="'审批人'">
|
||||
<a-select v-show="node.chooseAssign" v-model:value="node.assignees" :options="node.nextAssignees" :disabled="loading"
|
||||
:placeholder="'审批人'" max-tag-count="responsive"
|
||||
:mode="node.isChooseMulti? 'multiple' : ''"
|
||||
@ -30,10 +30,12 @@
|
||||
<a-select-option v-for="(item, index) in rejectNodeList" :key="index" :value="item.activityId">{{ item.activityName }}</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<template v-for="node in rejectNodeList">
|
||||
<a-form-item v-if="_action === 'reject'&&rejectNodeId===node.activityId" label="审批人">
|
||||
<a-select v-show="node.chooseAssign" v-model:value="node.assignees" :options="node.nextAssignees" :disabled="loading"
|
||||
:placeholder="'请选择' + node.activityName + '的审批人'" max-tag-count="responsive" mode="multiple"
|
||||
<template v-for="node in rejectNodeList" :key="node.activityId">
|
||||
<a-form-item required v-if="_action === 'reject'&&rejectNodeId===node.activityId" label="审批人">
|
||||
<a-select v-show="node.chooseAssign" v-model:value="node.assignees" :options="node.nextAssignees"
|
||||
:placeholder="'请选择' + node.activityName + '的审批人'" max-tag-count="responsive"
|
||||
:disabled="loading"
|
||||
:mode="node.rejectIsChooseMulti? 'multiple' : ''"
|
||||
:filterOption="search"
|
||||
></a-select>
|
||||
<span v-show="!node.chooseAssign">{{ getAssigneeText(node) }}</span>
|
||||
@ -81,7 +83,7 @@
|
||||
}
|
||||
|
||||
function getNextNodesName() {
|
||||
return flowNextNodes.value.length > 1 ? '多个节点,请选择流向节点' : flowNextNodes?.value[0]?.activityName;
|
||||
return flowNextNodes.value.length > 1 ? '多个节点,请确认流向节点' : flowNextNodes?.value[0]?.activityName;
|
||||
}
|
||||
|
||||
function toggleDialog({ isClose, action, callback, rejectCancel, processId, taskId, nextNodes } = {}) {
|
||||
@ -251,7 +253,7 @@
|
||||
}
|
||||
rejectNodeList.value.forEach((nNode) => {
|
||||
if(nNode.activityId==rejectNodeId.value){
|
||||
nextTaskUser[nNode.activityId] = isEnd.value ? '' : nNode.assignees.join(',');
|
||||
nextTaskUser[nNode.activityId] = isEnd.value ? '' : (typeof(nNode.assignees) == 'string' ? nNode.assignees : nNode.assignees.join(','));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<a-modal :mask-closable="false" :title="dialogTitle" :visible="isOpen" :width="500" class="geg" centered>
|
||||
<a-modal :mask-closable="false" :title="dialogTitle" :visible="isOpen" :width="500" class="geg" centered @cancel="onClickCancel">
|
||||
<template #footer>
|
||||
<a-button :disabled="loading" @click="onClickCancel">取消</a-button>
|
||||
<a-button :loading="loading" type="primary" @click="onClickOK">确定</a-button>
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
<template>
|
||||
<a-select v-model:value="selectedValue" :filter-option="handleFilterOption" :mode="mode" :options="getOptions" :placeholder="placeholder" allowClear v-bind="$attrs" @change="handleChange" @dropdown-visible-change="handleFetch">
|
||||
<div>
|
||||
<div v-if="disabled && !disabledShowBorder" :class="wordWrap ? '' : 'field-readonly'" :title="departNames">
|
||||
{{departNames}}
|
||||
</div>
|
||||
<a-select v-model:value="selectedValue" :filter-option="handleFilterOption" :mode="mode" :options="getOptions" :placeholder="placeholder" allowClear v-bind="$attrs" @change="handleChange" @dropdown-visible-change="handleFetch" v-else>
|
||||
<template v-for="item in Object.keys($slots)" #[item]="data">
|
||||
<slot :name="item" v-bind="data || {}"></slot>
|
||||
</template>
|
||||
@ -13,6 +17,7 @@
|
||||
</span>
|
||||
</template>
|
||||
</a-select>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, computed, unref, watch, inject, onMounted, watchEffect } from 'vue';
|
||||
@ -63,7 +68,15 @@
|
||||
mainKey: String,
|
||||
index: Number,
|
||||
sepTextField: String, // 独立存储文本部分
|
||||
row: Object // 行数据,在明细表里生效
|
||||
row: Object, // 行数据,在明细表里生效
|
||||
wordWrap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
disabledShowBorder: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['options-change', 'change', 'update:value'],
|
||||
setup(props, { emit }) {
|
||||
@ -80,6 +93,7 @@
|
||||
const valChanged = ref(false);
|
||||
// label分开存储时,第一次懒加载会同时处罚两次fetch,所以这里延迟执行
|
||||
const fetch = debounce(_fetch, 200, { leading: false, trailing: true });
|
||||
const selectName = ref('')
|
||||
|
||||
const getOptions = computed(() => {
|
||||
const { labelField, valueField, numberToString } = props;
|
||||
@ -156,6 +170,7 @@
|
||||
() => props.value,
|
||||
() => {
|
||||
selectedValue.value = ((typeof props.value === 'string' && !!props.value ? props.value?.split(',') : props.value) || undefined) as any;
|
||||
updateSepTextField(Array.isArray(selectedValue.value) ? selectedValue.value : [selectedValue.value]);
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
@ -163,15 +178,16 @@
|
||||
);
|
||||
|
||||
function updateSepTextField(arr) {
|
||||
if (!props.sepTextField || !props.row) {
|
||||
return;
|
||||
}
|
||||
const options = unref(getOptions);
|
||||
const txtArr = options
|
||||
.filter((opt) => {
|
||||
return arr.includes(opt.value);
|
||||
})
|
||||
.map((item) => item.label);
|
||||
selectName.value = txtArr.join(',')
|
||||
if (!props.sepTextField || !props.row) {
|
||||
return;
|
||||
}
|
||||
props.row[camelCaseString(props.sepTextField)] = txtArr.join(',');
|
||||
}
|
||||
|
||||
@ -260,7 +276,6 @@
|
||||
emit('update:value', val);
|
||||
emit('change', val, args);
|
||||
selectedValue.value = props.value === undefined ? val : (((typeof props.value === 'string' && !!props.value ? props.value?.split(',') : props.value) || undefined) as any);
|
||||
updateSepTextField(Array.isArray(value) ? value : [value]);
|
||||
}
|
||||
|
||||
return {
|
||||
@ -276,3 +291,11 @@
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.field-readonly {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -52,12 +52,13 @@
|
||||
import { PostInfo } from '/@/api/system/post/model';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['change']);
|
||||
const emits = defineEmits(['change','selectedNodes']);
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
selectedIds: Array<string>;
|
||||
multiple?: Boolean;
|
||||
needNode?: Boolean;
|
||||
}>(),
|
||||
{
|
||||
selectedIds: () => {
|
||||
@ -66,6 +67,9 @@
|
||||
disabledIds: () => {
|
||||
return [];
|
||||
},
|
||||
needNode: () => {
|
||||
return false;
|
||||
},
|
||||
},
|
||||
);
|
||||
let data: {
|
||||
@ -106,7 +110,10 @@
|
||||
data.visible = true;
|
||||
}
|
||||
function submit() {
|
||||
emits('change', data.selectedIds);
|
||||
emits('change', data.selectedIds, data.selectedList);
|
||||
if(props.needNode && props.needNode === true) {
|
||||
emits('selectedNodes', data.selectedList);
|
||||
}
|
||||
close();
|
||||
}
|
||||
function close() {
|
||||
@ -136,6 +143,7 @@
|
||||
data.selectedList = [];
|
||||
} else {
|
||||
data.selectedIds = [item.id];
|
||||
data.selectedList = [item]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,9 @@
|
||||
});
|
||||
|
||||
const getSchemas = computed<FormSchema[]>(() => {
|
||||
return (unref(getProps).schemas as any) || unref(schemaRef);
|
||||
let schemas = (unref(getProps).schemas as any) || unref(schemaRef)
|
||||
getComponent(schemas);
|
||||
return schemas;
|
||||
});
|
||||
|
||||
// Get the basic configuration of the form
|
||||
@ -110,7 +112,7 @@
|
||||
}
|
||||
|
||||
function getColWidth(schema: any) {
|
||||
const compProps = schema.componentProps;
|
||||
const compProps = schema?.componentProps;
|
||||
if (compProps?.responsive) {
|
||||
if (compProps.respNewRow) {
|
||||
return 24; // 响应式布局下独立成行
|
||||
@ -125,7 +127,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return schema.colProps?.span;
|
||||
return schema?.colProps?.span;
|
||||
}
|
||||
|
||||
const debGetWrapSize = debounce(getWrapSize, 300);
|
||||
@ -148,7 +150,7 @@
|
||||
});
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema?.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema, itemValue: any): boolean {
|
||||
@ -175,22 +177,24 @@
|
||||
}
|
||||
|
||||
function getIfShow(schema: FormSchema, itemValue: any): boolean {
|
||||
const { ifShow } = schema;
|
||||
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(ifShow)) {
|
||||
isIfShow = ifShow;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
if (isFunction(ifShow)) {
|
||||
isIfShow = ifShow({
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
|
||||
if (isFunction(componentProps?.isShow)) {
|
||||
isShow = componentProps.isShow({
|
||||
values: itemValue,
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
return isIfShow;
|
||||
return isShow;
|
||||
}
|
||||
|
||||
const formModel = reactive<Recordable>(props.formModel);
|
||||
@ -218,8 +222,6 @@
|
||||
|
||||
const refreshFieldObj = ref<object>({});
|
||||
|
||||
getComponent(getSchemas.value);
|
||||
|
||||
function getComponent(component) {
|
||||
const layoutComponents = ['tab', 'grid', 'card'];
|
||||
component?.map((info) => {
|
||||
@ -253,8 +255,10 @@
|
||||
|
||||
function setComponentDefault(item) {
|
||||
if ((staticDataComponents.includes(item.component) && (item.componentProps as any)?.datasourceType === 'staticData') || (needDicDefaultValue.includes(item.type) && (item.componentProps as any)?.datasourceType === 'dic')) {
|
||||
let { defaultSelect } = item.componentProps as any;
|
||||
formModel[item.field] = defaultSelect;
|
||||
if(formModel[item.field]==undefined) {
|
||||
let { defaultSelect } = item.componentProps as any;
|
||||
formModel[item.field] = defaultSelect;
|
||||
}
|
||||
return;
|
||||
}
|
||||
let { defaultValue } = item;
|
||||
@ -277,7 +281,9 @@
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
formModel[item.field] = item.component === 'SubForm' ? [] : defaultValue;
|
||||
if(formModel[item.field]==undefined){
|
||||
formModel[item.field] = item.component === 'SubForm' ? [] : defaultValue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -413,7 +419,8 @@
|
||||
* 修改bug #5090
|
||||
* 为了保证表单赋值触发所有组件的change事件
|
||||
*/
|
||||
const executeEvent = (allSchemas: FormSchema[]) => {
|
||||
const executeEvent = (allSchemas: FormSchema[] = []) => {
|
||||
if(!allSchemas) return;
|
||||
for (const schema of allSchemas) {
|
||||
//如果是这几个组件 需要查询子级
|
||||
if (['Card', 'Tab', 'Grid'].includes(schema.component)) {
|
||||
@ -442,9 +449,9 @@
|
||||
try {
|
||||
if (typeof handler === 'string') {
|
||||
const event = new Function('schema', 'formModel', 'formActionType', 'extParams', handler);
|
||||
event(schema, formModel, formApi, { formData, allSchemas });
|
||||
event(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
} else if (typeof handler === 'function') {
|
||||
handler(schema, formModel, formApi, { formData, allSchemas });
|
||||
handler(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
@ -675,7 +682,7 @@
|
||||
schema.componentProps.style = { ...schema.componentProps.style, ...style };
|
||||
};
|
||||
|
||||
const formApi: FormActionType = {
|
||||
const formApi = {
|
||||
submit,
|
||||
validate,
|
||||
clearValidate,
|
||||
@ -694,9 +701,10 @@
|
||||
httpRequest,
|
||||
refreshAPI,
|
||||
changeStyle,
|
||||
setDefaultValue
|
||||
setDefaultValue,
|
||||
formModel
|
||||
};
|
||||
|
||||
//将表单方法 导出 给父组件使用。
|
||||
defineExpose<FormActionType>(formApi);
|
||||
defineExpose(formApi);
|
||||
</script>
|
||||
|
||||
@ -110,7 +110,7 @@
|
||||
}
|
||||
|
||||
function getColWidth(schema: any) {
|
||||
const compProps = schema.componentProps;
|
||||
const compProps = schema?.componentProps;
|
||||
if (compProps?.responsive) {
|
||||
if (compProps.respNewRow) {
|
||||
return 24; // 响应式布局下独立成行
|
||||
@ -125,7 +125,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
return schema.colProps?.span;
|
||||
return schema?.colProps?.span;
|
||||
}
|
||||
|
||||
const debGetWrapSize = debounce(getWrapSize, 300);
|
||||
@ -148,7 +148,7 @@
|
||||
});
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema, itemValue: any): boolean {
|
||||
@ -175,22 +175,15 @@
|
||||
}
|
||||
|
||||
function getIfShow(schema: FormSchema, itemValue: any): boolean {
|
||||
const { ifShow } = schema;
|
||||
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(ifShow)) {
|
||||
isIfShow = ifShow;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
if (isFunction(ifShow)) {
|
||||
isIfShow = ifShow({
|
||||
values: itemValue,
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
return isIfShow;
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
return isShow;
|
||||
}
|
||||
|
||||
const formModel = reactive<Recordable>(props.formModel);
|
||||
@ -413,7 +406,8 @@
|
||||
* 修改bug #5090
|
||||
* 为了保证表单赋值触发所有组件的change事件
|
||||
*/
|
||||
const executeEvent = (allSchemas: FormSchema[]) => {
|
||||
const executeEvent = (allSchemas: FormSchema[] = []) => {
|
||||
if(!allSchemas) return;
|
||||
for (const schema of allSchemas) {
|
||||
//如果是这几个组件 需要查询子级
|
||||
if (['Card', 'Tab', 'Grid'].includes(schema.component)) {
|
||||
@ -442,9 +436,9 @@
|
||||
try {
|
||||
if (typeof handler === 'string') {
|
||||
const event = new Function('schema', 'formModel', 'formActionType', 'extParams', handler);
|
||||
event(schema, formModel, formApi, { formData, allSchemas });
|
||||
event(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
} else if (typeof handler === 'function') {
|
||||
handler(schema, formModel, formApi, { formData, allSchemas });
|
||||
handler(schema, formModel, formApi, { formData, allSchemas, isPersonChange: false });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error', error);
|
||||
@ -672,12 +666,15 @@
|
||||
httpRequest,
|
||||
refreshAPI,
|
||||
changeStyle,
|
||||
setDefaultValue
|
||||
setDefaultValue,
|
||||
formModel
|
||||
};
|
||||
|
||||
//将表单方法 导出 给父组件使用。
|
||||
expose({
|
||||
...formApi
|
||||
...formApi,
|
||||
formModel,
|
||||
getSchemas
|
||||
});
|
||||
|
||||
return {
|
||||
|
||||
@ -31,6 +31,9 @@
|
||||
:labelAlign="formProps?.labelAlign"
|
||||
:name="schema.field"
|
||||
:wrapperCol="itemLabelWidthProp.wrapperCol"
|
||||
:style="{
|
||||
overflow:'hidden'
|
||||
}"
|
||||
>
|
||||
<component :is="formComponent(schema)" v-model:value="formModel![schema.field]" :disabled="getDisable" :size="formProps?.size" v-bind="schema.componentProps" />
|
||||
</FormItem>
|
||||
@ -84,7 +87,7 @@
|
||||
:wrapperCol="itemLabelWidthProp.wrapperCol"
|
||||
>
|
||||
<template v-if="getDisable && readonlySupport(schema.component)">
|
||||
<readonly :model="formModel" :schema="schema" />
|
||||
<readonly :schema="schema" :model="formModel"/>
|
||||
<component
|
||||
:is="componentMap.get(schema.component)"
|
||||
v-show="false"
|
||||
@ -164,11 +167,11 @@
|
||||
:validateTrigger="['blur', 'change']"
|
||||
:wrapperCol="itemLabelWidthProp.wrapperCol"
|
||||
>
|
||||
<template v-if="getDisable && readonlySupport(schema.component)">
|
||||
<template v-if="getDisable && readonlySupport(schema.component) && !getDisabledShowBorder">
|
||||
<readonly :schema="schema" :value="formModel![schema.field]" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<component :is="defaultComponent(schema)" :key="refreshFieldObj[schema.field]" v-model:value="formModel![schema.field]" :disabled="getDisable" :formData="formModel" :size="formProps?.size" v-bind="getComponentsProps" />
|
||||
<component :is="defaultComponent(schema)" :key="refreshFieldObj[schema.field]" v-model:value="formModel![schema.field]" :disabled="getDisable" :formData="formModel" :size="formProps?.size" v-bind="{...getComponentsProps, placeholder: getDisable ? '' : getComponentsProps.placeholder, disabledShowBorder: getDisabledShowBorder}" :title="readonlySupport(schema.component) ? formModel![schema.field] : ''"/>
|
||||
</template>
|
||||
</FormItem>
|
||||
</template>
|
||||
@ -218,7 +221,8 @@
|
||||
const tabActiveKey = inject<Ref<number>>('tabActiveKey', ref(0));
|
||||
const activeKey = ref<number>(0);
|
||||
const isCamelCase = inject<boolean>('isCamelCase', false);
|
||||
|
||||
// 注入整个表单的配置,formProps是个计算属性,不能修改,formData则来自每个业务的表单页面。
|
||||
const formData = inject('formData', { noInject: true });
|
||||
watch(
|
||||
() => tabActiveKey?.value,
|
||||
(val) => {
|
||||
@ -262,6 +266,17 @@
|
||||
return disabled;
|
||||
});
|
||||
|
||||
const getDisabledShowBorder = computed(() => {
|
||||
let disabledShowBorder = false
|
||||
if (getComponentsProps.value?.disabledShowBorder) {
|
||||
disabledShowBorder = true;
|
||||
}
|
||||
if(import.meta.env?.VITE_GLOB_READ_ONLY_BORDER_DISABLED) {
|
||||
disabledShowBorder = import.meta.env?.VITE_GLOB_READ_ONLY_BORDER_DISABLED == 'true'
|
||||
}
|
||||
return disabledShowBorder
|
||||
})
|
||||
|
||||
const getComponentsProps = computed(() => {
|
||||
let { componentProps = {} } = props.schema;
|
||||
|
||||
@ -356,9 +371,9 @@
|
||||
// console.log('formitem watch!!!!!!!!');
|
||||
//填值以后需要手动校验的组件
|
||||
const validateComponents = ['User', 'RichTextEditor', 'Upload', 'SelectMap'];
|
||||
if (validateComponents.includes(props.schema.component) && formModel![props.schema.field]) {
|
||||
if (validateComponents.includes(props.schema?.component) && formModel![props.schema?.field]) {
|
||||
setTimeout(() => {
|
||||
props.formApi?.validateFields([props.schema.field]);
|
||||
props.formApi?.validateFields([props.schema?.field]);
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
@ -394,32 +409,53 @@
|
||||
};
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema?.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function readonlySupport(name) {
|
||||
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker|InputTextArea)$/.test(name);
|
||||
return /^(Input|AutoCodeRule|DatePicker|Text|TimePicker|Range|RichTextEditor|TimeRangePicker|RangePicker)$/.test(name);
|
||||
}
|
||||
|
||||
function getShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isIfShow = show;
|
||||
}
|
||||
return isIfShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { componentProps, show } = schema as any;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
|
||||
if (isFunction(componentProps?.isShow)) {
|
||||
isShow = componentProps.isShow({
|
||||
values:formModel![schema.field],
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
|
||||
return isShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
|
||||
let isShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isShow = show;
|
||||
}
|
||||
if (isFunction(show)) {
|
||||
isShow = show({
|
||||
values:formModel![schema.field],
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
|
||||
isShow = isShow;
|
||||
return isShow;
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -53,6 +53,8 @@
|
||||
const tabActiveKey = inject<Ref<number>>('tabActiveKey', ref(0));
|
||||
const activeKey = ref<number>(0);
|
||||
const isCamelCase = inject<boolean>('isCamelCase', false);
|
||||
// 注入整个表单的配置,formProps是个计算属性,不能修改,formData则来自每个业务的表单页面。
|
||||
const formData = inject('formData', { noInject: true });
|
||||
watch(
|
||||
() => tabActiveKey?.value,
|
||||
(val) => {
|
||||
@ -122,7 +124,7 @@
|
||||
let field = camelCaseString(item);
|
||||
if (field) cloneFormModel[field] = cloneFormModel[item];
|
||||
}
|
||||
event(props.schema, isCamelCase ? cloneFormModel : formModel, props.formApi, {});
|
||||
event(props.schema, isCamelCase ? cloneFormModel : formModel, props.formApi, { formData });
|
||||
|
||||
if (isCamelCase) {
|
||||
for (let item in formModel) {
|
||||
@ -188,7 +190,7 @@
|
||||
// console.log('formitem watch!!!!!!!!');
|
||||
//填值以后需要手动校验的组件
|
||||
const validateComponents = ['User', 'RichTextEditor', 'Upload', 'SelectMap'];
|
||||
if (validateComponents.includes(props.schema.component) && formModel![props.schema.field]) {
|
||||
if (validateComponents.includes(props.schema?.component) && formModel![props.schema.field]) {
|
||||
setTimeout(() => {
|
||||
props.formApi?.validateFields([props.schema.field]);
|
||||
}, 100);
|
||||
@ -209,7 +211,7 @@
|
||||
};
|
||||
|
||||
function showComponent(schema) {
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema.type) : !noShowGenerateComponents.includes(schema.type);
|
||||
return props.isWorkFlow ? !noShowWorkFlowComponents.includes(schema?.type) : !noShowGenerateComponents.includes(schema?.type);
|
||||
}
|
||||
|
||||
function readonlySupport(name) {
|
||||
@ -217,24 +219,35 @@
|
||||
}
|
||||
|
||||
function getShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
let isIfShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isIfShow = show;
|
||||
}
|
||||
return isIfShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { componentProps, show } = schema as any;
|
||||
const { componentProps, show } = schema;
|
||||
let isShow = true;
|
||||
if (isBoolean(componentProps?.isShow)) {
|
||||
isShow = componentProps?.isShow;
|
||||
}
|
||||
// if (isBoolean(show)) {
|
||||
// isShow = show;
|
||||
// }
|
||||
return isShow;
|
||||
}
|
||||
|
||||
function getIsShow(schema: FormSchema): boolean {
|
||||
const { show } = schema;
|
||||
|
||||
let isShow = true;
|
||||
|
||||
if (isBoolean(show)) {
|
||||
isShow = show;
|
||||
}
|
||||
if (isFunction(show)) {
|
||||
isShow = show({
|
||||
values: itemValue,
|
||||
model: formModel!,
|
||||
schema: schema,
|
||||
field: schema.field
|
||||
});
|
||||
}
|
||||
|
||||
isShow = isShow;
|
||||
return isShow;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="visible">
|
||||
<div v-if="visible" class="system-form">
|
||||
<component
|
||||
:is="componentName"
|
||||
v-if="visible"
|
||||
@ -28,7 +28,10 @@
|
||||
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
|
||||
import { createFormEvent, loadFormEvent } from '/@/hooks/web/useFormEvent';
|
||||
import { changeFormJson } from '/@/hooks/web/useWorkFlowForm';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import {message} from "ant-design-vue";
|
||||
const userStore = useUserStore();
|
||||
const userInfo = userStore.getUserInfo;
|
||||
|
||||
const props = defineProps({
|
||||
systemComponent: {
|
||||
@ -131,7 +134,7 @@
|
||||
|
||||
onMounted(() => {
|
||||
visible.value = true;
|
||||
approvalData.value = inject("approvalData");
|
||||
approvalData.value = inject("approvalData");
|
||||
});
|
||||
|
||||
//
|
||||
@ -176,6 +179,13 @@
|
||||
async function getFieldsValue(){
|
||||
return SystemFormRef.value.getFieldsValue();
|
||||
}
|
||||
async function getFormModels() {
|
||||
try {
|
||||
return (SystemFormRef.value?.getFormModel && SystemFormRef.value?.getFormModel()) || await validate()
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
async function getValue(){
|
||||
let values = null;
|
||||
if(approvalData.value?.approvedResult === ApproveCode.FINISH){
|
||||
@ -198,40 +208,56 @@
|
||||
// 提交表单
|
||||
if (visible.value) {
|
||||
let id = await submit(saveRowKey);
|
||||
if(!id) {
|
||||
throw new Error(`提交表单失败`);
|
||||
|
||||
}
|
||||
let rowKey = getRowKey();
|
||||
values[rowKey] = id;
|
||||
values['_id'] = id;
|
||||
//重新查一遍
|
||||
let newValues=await SystemFormRef.value.setFormDataFromId(id,true);
|
||||
let newValues=await SystemFormRef.value.setFormDataFromId(id);
|
||||
if(newValues){
|
||||
values=newValues;
|
||||
} else {
|
||||
throw new Error(`获取表单失败`);
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
async function submit(saveRowKey) {
|
||||
let saveValId = '';
|
||||
let values = await SystemFormRef.value.validate();
|
||||
let rowKey = getRowKey();
|
||||
if (props.workflowConfig.formModel[rowKey]) {
|
||||
values[rowKey] = props.workflowConfig.formModel[rowKey];
|
||||
}
|
||||
if (values[rowKey]) {
|
||||
// 编辑
|
||||
await SystemFormRef.value.update({ values, rowId: values[rowKey] });
|
||||
saveValId = values[rowKey];
|
||||
} else {
|
||||
// 新增
|
||||
saveValId = await SystemFormRef.value.add(values);
|
||||
if (saveRowKey) {
|
||||
// 把rowKey写回去,新建流程的时候防止取消了重复提交
|
||||
props.workflowConfig.formModel[rowKey] = saveValId;
|
||||
try {
|
||||
let saveValId = '';
|
||||
let values = await SystemFormRef.value.validate();
|
||||
let rowKey = getRowKey();
|
||||
if (props.workflowConfig.formModel[rowKey]) {
|
||||
values[rowKey] = props.workflowConfig.formModel[rowKey];
|
||||
}
|
||||
if (values[rowKey]) {
|
||||
// 编辑
|
||||
let res = await SystemFormRef.value.update({ values, rowId: values[rowKey] });
|
||||
if(!res) {
|
||||
throw new Error(`提交表单失败`);
|
||||
}
|
||||
saveValId = values[rowKey];
|
||||
} else {
|
||||
// 新增
|
||||
saveValId = await SystemFormRef.value.add(values);
|
||||
if (saveRowKey) {
|
||||
// 把rowKey写回去,新建流程的时候防止取消了重复提交
|
||||
props.workflowConfig.formModel[rowKey] = saveValId;
|
||||
}
|
||||
}
|
||||
return saveValId;
|
||||
} catch(e) {
|
||||
}
|
||||
return saveValId;
|
||||
|
||||
}
|
||||
|
||||
async function setDisabledForm(isDisabled) {
|
||||
@ -245,13 +271,22 @@
|
||||
async function handleDelete(id) {
|
||||
let ret;
|
||||
try {
|
||||
if(!SystemFormRef.value?.handleDelete) {
|
||||
throw new Error(`表单未配置删除`);
|
||||
}
|
||||
ret = await SystemFormRef.value.handleDelete(id);
|
||||
} catch (e) {
|
||||
message.error('表单未配置删除');
|
||||
return null;
|
||||
throw new Error(e);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
function handleInnerFun(funcName) {
|
||||
if(!SystemFormRef.value?.[funcName]) {
|
||||
message.error(`表单未配置${funcName}方法`);
|
||||
return
|
||||
}
|
||||
SystemFormRef.value[funcName]()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
workflowSubmit,
|
||||
@ -263,7 +298,9 @@
|
||||
getFieldsValue,
|
||||
getIsOldSystem,
|
||||
setDisabledForm,
|
||||
handleDelete
|
||||
handleDelete,
|
||||
getFormModels,
|
||||
handleInnerFun
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
background-color: @component-background;
|
||||
|
||||
.ant-tree-treenode {
|
||||
align-items: center;
|
||||
align-items: center!important;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
|
||||
@ -39,6 +39,7 @@ import { ref, reactive, onMounted, } from 'vue';
|
||||
import Preview from '/@/views/workflow/design/Preview.vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
import { h } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@ -89,6 +90,25 @@ const configColumns = [
|
||||
title: t('备注'),
|
||||
dataIndex: 'remark',
|
||||
width: 180,
|
||||
customRender: ({ text }) => {
|
||||
return h(
|
||||
'a-tooltip',
|
||||
{ title: text || '' },
|
||||
[
|
||||
h('div', {
|
||||
style: {
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
wordBreak: 'break-all',
|
||||
cursor: 'pointer',
|
||||
}
|
||||
}, text || '')
|
||||
]
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: t('创建时间'),
|
||||
@ -147,6 +167,15 @@ const getHistoryList = async () => {
|
||||
if (item.chooseCount == 0) {
|
||||
item.chooseCount = '';//置空
|
||||
}
|
||||
// 渲染备注 由json返回
|
||||
if (item.jsonContent) {
|
||||
try {
|
||||
const remarkObj = JSON.parse(item.jsonContent || '{}');
|
||||
item.remark = remarkObj?.processConfig?.remark || '';
|
||||
} catch (e) {
|
||||
item.remark = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (error) {}
|
||||
|
||||
Reference in New Issue
Block a user