<script lang="ts" setup>
|
import { computed, reactive, toRefs } from 'vue';
|
|
import { BasicModal, useModal, useModalInner } from '@jnpf/ui/modal';
|
import { buildBitUUID } from '@jnpf/utils';
|
|
import { useDebounceFn } from '@vueuse/core';
|
import { cloneDeep } from 'lodash-es';
|
import draggable from 'vuedraggable';
|
|
import { getDataInterfaceInfo } from '#/api/systemData/dataInterface';
|
import { getDictionaryTypeSelector } from '#/api/systemData/dictionary';
|
import { BatchOperate, InterfaceModal } from '#/components/CommonModal';
|
|
interface State {
|
activeData: any;
|
dicOptions: any[];
|
options: any[];
|
allOptions: any[];
|
}
|
|
const emit = defineEmits(['register', 'confirm']);
|
const dataTypeOptions = [
|
{ id: 'static', fullName: '静态数据' },
|
{ id: 'dictionary', fullName: '数据字典' },
|
{ id: 'dynamic', fullName: '数据接口' },
|
];
|
const valueOptions = [
|
{ id: 'id', fullName: 'id' },
|
{ id: 'enCode', fullName: 'enCode' },
|
];
|
const dateFormatOptions = [
|
{ id: 'yyyy', fullName: 'yyyy' },
|
{ id: 'yyyy-MM', fullName: 'yyyy-MM' },
|
{ id: 'yyyy-MM-dd', fullName: 'yyyy-MM-dd' },
|
{ id: 'yyyy-MM-dd HH:mm', fullName: 'yyyy-MM-dd HH:mm' },
|
{ id: 'yyyy-MM-dd HH:mm:ss', fullName: 'yyyy-MM-dd HH:mm:ss' },
|
];
|
const timeFormatOptions = [
|
{ id: 'HH:mm:ss', fullName: 'HH:mm:ss' },
|
{ id: 'HH:mm', fullName: 'HH:mm' },
|
];
|
const state = reactive<State>({
|
activeData: {
|
__config__: {},
|
},
|
dicOptions: [],
|
options: [],
|
allOptions: [],
|
});
|
const { activeData, dicOptions, options } = toRefs(state);
|
const [registerModal, { closeModal }] = useModalInner(init);
|
const [registerBatchOperate, { openModal }] = useModal();
|
|
const getRangeOptions = computed(() => {
|
const list: any = [
|
{ id: '1', fullName: '查询控件选中用户' },
|
{ id: '2', fullName: '查询控件选中用户及下属' },
|
{ id: '3', fullName: '查询控件选中用户及子孙下属' },
|
];
|
if (state.activeData.jnpfKey === 'posSelect') {
|
list[0].fullName = '查询控件选中岗位';
|
list[1].fullName = '查询控件选中岗位及子岗位';
|
list[2].fullName = '查询控件选中岗位及子孙岗位';
|
}
|
if (state.activeData.jnpfKey === 'organizeSelect') {
|
list[0].fullName = '查询控件选中组织';
|
list[1].fullName = '查询控件选中组织及子组织';
|
list[2].fullName = '查询控件选中组织及子孙组织';
|
}
|
return list;
|
});
|
|
const onDataTypeChange = () => {
|
state.activeData.options = [];
|
state.activeData.props.value = 'id';
|
state.activeData.props.label = 'fullName';
|
if (Reflect.has(state.activeData.props, 'children')) state.activeData.props.children = 'children';
|
state.activeData.__config__.dictionaryType = '';
|
state.activeData.__config__.propsUrl = '';
|
state.activeData.__config__.propsName = '';
|
state.activeData.__config__.templateJson = [];
|
};
|
const onDictionaryTypeChange = () => {
|
state.activeData.options = [];
|
};
|
const onPropsUrlChange = (val, row) => {
|
state.activeData.options = [];
|
if (!val) {
|
state.activeData.__config__.propsUrl = '';
|
state.activeData.__config__.propsName = '';
|
state.activeData.__config__.templateJson = [];
|
state.options = [];
|
state.allOptions = [];
|
return;
|
}
|
if (state.activeData.__config__.propsUrl === val) return;
|
const list = row.parameterJson ? JSON.parse(row.parameterJson) : [];
|
state.activeData.__config__.propsUrl = val;
|
state.activeData.__config__.propsName = row.fullName;
|
state.activeData.__config__.templateJson = list.map((o) => ({ ...o, relationField: '' }));
|
initFieldData();
|
};
|
const addSelectItem = () => {
|
const uuid = buildBitUUID();
|
state.activeData.options.push({
|
fullName: `选项${uuid}`,
|
id: uuid,
|
});
|
};
|
const onBatchOperateConfirm = (options) => {
|
state.activeData.options = options;
|
};
|
const debounceOnSearch = useDebounceFn(onSearch, 300);
|
|
function onSearch(searchText: string) {
|
state.options = state.allOptions.filter((o) => o.value.toLowerCase().includes(searchText.toLowerCase()));
|
}
|
function onFocus(searchText) {
|
onSearch(searchText);
|
}
|
function initFieldData() {
|
if (state.activeData.__config__.dataType !== 'dynamic' || !state.activeData.__config__.propsUrl) return (state.allOptions = []);
|
getDataInterfaceInfo(state.activeData.__config__.propsUrl).then((res) => {
|
const data = res.data;
|
const list = data.fieldJson ? JSON.parse(data.fieldJson) : [];
|
state.allOptions = list.map((o) => ({ ...o, value: o.defaultValue }));
|
});
|
}
|
function init(data) {
|
state.activeData = cloneDeep(data);
|
if (data.jnpfKey === 'select') {
|
getDicOptions();
|
initFieldData();
|
}
|
}
|
function getDicOptions() {
|
getDictionaryTypeSelector().then((res) => {
|
state.dicOptions = res.data.list;
|
});
|
}
|
function handleSubmit() {
|
emit('confirm', state.activeData);
|
closeModal();
|
}
|
</script>
|
<template>
|
<BasicModal v-bind="$attrs" @register="registerModal" title="组件属性配置" @ok="handleSubmit" destroy-on-close class="extra-config-modal">
|
<div class="extra-config-modal-body" :style="{ 'min-height': activeData.jnpfKey === 'select' ? '300px' : '150px' }">
|
<a-form :colon="false" label-align="left" :label-col="{ style: { width: '90px' } }">
|
<template v-if="activeData.jnpfKey === 'select'">
|
<a-form-item label="数据来源">
|
<jnpf-radio
|
v-model:value="activeData.__config__.dataType"
|
:options="dataTypeOptions"
|
option-type="button"
|
button-style="solid"
|
@change="onDataTypeChange" />
|
</a-form-item>
|
<div class="options-list" v-if="activeData.__config__.dataType === 'static'">
|
<draggable v-model="activeData.options" :animation="300" group="selectItem" handle=".option-drag" item-key="uuid">
|
<template #item="{ element, index }">
|
<div class="select-item">
|
<div class="select-line-icon option-drag">
|
<i class="icon-ym icon-ym-darg"></i>
|
</div>
|
<a-input v-model:value="element.fullName" placeholder="选项名" />
|
<a-input v-model:value="element.id" placeholder="选项值" />
|
<div class="close-btn select-line-icon" @click="activeData.options.splice(index, 1)">
|
<i class="icon-ym icon-ym-btn-clearn"></i>
|
</div>
|
</div>
|
</template>
|
</draggable>
|
</div>
|
<div class="add-btn" v-if="activeData.__config__.dataType === 'static'">
|
<a-button type="link" pre-icon="icon-ym icon-ym-btn-add" @click="addSelectItem" class="!px-0">添加选项</a-button>
|
<a-divider type="vertical" />
|
<a-button type="link" @click="openModal(true, { options: activeData.options })" class="!px-0">批量编辑</a-button>
|
</div>
|
<div v-if="activeData.__config__.dataType === 'dictionary'">
|
<a-form-item label="数据字典">
|
<jnpf-tree-select
|
:options="dicOptions"
|
v-model:value="activeData.__config__.dictionaryType"
|
placeholder="请选择"
|
last-level
|
allow-clear
|
@change="onDictionaryTypeChange" />
|
</a-form-item>
|
<a-form-item label="存储字段">
|
<jnpf-select v-model:value="activeData.props.value" placeholder="请选择" :options="valueOptions" />
|
</a-form-item>
|
</div>
|
<div v-if="activeData.__config__.dataType === 'dynamic'">
|
<a-form-item label="数据接口">
|
<InterfaceModal
|
:value="activeData.__config__.propsUrl"
|
:title="activeData.__config__.propsName"
|
popup-title="数据接口"
|
@change="onPropsUrlChange" />
|
</a-form-item>
|
<a-form-item label="存储字段">
|
<a-auto-complete
|
v-model:value="activeData.props.value"
|
placeholder="请输入"
|
:options="options"
|
@focus="onFocus(activeData.props.value)"
|
@search="debounceOnSearch(activeData.props.value)" />
|
</a-form-item>
|
<a-form-item label="回显字段">
|
<a-auto-complete
|
v-model:value="activeData.props.label"
|
placeholder="请输入"
|
:options="options"
|
@focus="onFocus(activeData.props.label)"
|
@search="debounceOnSearch(activeData.props.label)" />
|
</a-form-item>
|
</div>
|
</template>
|
<a-form-item label="日期类型" v-if="activeData.jnpfKey === 'datePicker'">
|
<jnpf-select v-model:value="activeData.format" placeholder="请选择" :options="dateFormatOptions" />
|
</a-form-item>
|
<a-form-item label="时间类型" v-if="activeData.jnpfKey === 'timePicker'">
|
<jnpf-select v-model:value="activeData.format" placeholder="请选择" :options="timeFormatOptions" />
|
</a-form-item>
|
<a-form-item label="" v-if="['organizeSelect', 'posSelect', 'userSelect'].includes(activeData.jnpfKey)">
|
<JnpfRadio v-model:value="activeData.selectRange" :options="getRangeOptions" direction="vertical" class="range-radio" />
|
</a-form-item>
|
</a-form>
|
<BatchOperate @register="registerBatchOperate" @confirm="onBatchOperateConfirm" />
|
</div>
|
</BasicModal>
|
</template>
|
<style lang="scss">
|
.extra-config-modal {
|
.extra-config-modal-body {
|
min-height: 150px;
|
padding-bottom: 20px;
|
|
.options-list {
|
max-height: 200px;
|
margin-bottom: -10px;
|
overflow-y: auto;
|
|
.scrollbar__wrap {
|
margin-bottom: 0 !important;
|
}
|
|
.select-item {
|
box-sizing: border-box;
|
display: flex;
|
border: 1px dashed var(--component-background);
|
|
& .ant-input + .ant-input {
|
margin-left: 4px;
|
}
|
|
.ant-select {
|
width: 100%;
|
}
|
|
& + .select-item {
|
margin-top: 4px;
|
}
|
|
&.sortable-chosen {
|
border: 1px dashed var(--primary-color);
|
}
|
|
.select-line-icon {
|
padding: 0 4px;
|
font-size: 22px;
|
line-height: 31px;
|
color: #606266;
|
|
.icon-ym-darg {
|
display: inline-block;
|
font-size: 20px;
|
line-height: 31px;
|
}
|
|
.icon-ym-btn-clearn {
|
font-size: 18px;
|
}
|
}
|
|
.close-btn {
|
display: flex;
|
align-items: center;
|
height: 32px;
|
color: var(--error-color);
|
cursor: pointer;
|
}
|
|
.option-drag {
|
cursor: move;
|
}
|
}
|
|
.jnpf-tree__name {
|
width: calc(100% - 60px);
|
}
|
}
|
|
.add-btn {
|
padding-left: 27px;
|
margin-top: 10px;
|
}
|
}
|
|
.range-radio {
|
.ant-radio-wrapper {
|
margin-bottom: 6px;
|
}
|
}
|
}
|
</style>
|