初始代码
This commit is contained in:
61
src/views/common/dynamicDataReport/index.vue
Normal file
61
src/views/common/dynamicDataReport/index.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper bg-white">
|
||||
<iframe :src="state.url" width="100%" height="100%" frameborder="0" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
import { getToken } from '@/utils/auth';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getDataReportInfo } from '@/api/onlineDev/dataReport';
|
||||
|
||||
interface State {
|
||||
url: string;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'dynamicDataReport' });
|
||||
defineEmits(['register']);
|
||||
const { report } = useGlobSetting();
|
||||
const state = reactive<State>({
|
||||
url: '',
|
||||
});
|
||||
|
||||
function init() {
|
||||
const route = useRoute();
|
||||
const id = route.meta.relationId;
|
||||
if (!id) return;
|
||||
let targetUrl = `${report}/preview.html?id=${id}&token=${getToken()}&page=1&from=menu`;
|
||||
getDataReportInfo(id).then(res => {
|
||||
let item = {};
|
||||
if (res.data?.searchForm?.components && Array.isArray(res.data.searchForm.components)) {
|
||||
listQuery(res.data.searchForm.components, item);
|
||||
for (let key in item) {
|
||||
let item1 = '&' + key + '=' + item[key];
|
||||
targetUrl += item1;
|
||||
}
|
||||
}
|
||||
state.url = targetUrl;
|
||||
});
|
||||
}
|
||||
function listQuery(list, callback) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
let arrayList = [];
|
||||
if (item.hasOwnProperty('cols') && Array.isArray(item.cols)) {
|
||||
arrayList = arrayList.concat(item.cols);
|
||||
}
|
||||
if (item.hasOwnProperty('children') && Array.isArray(item.children)) {
|
||||
arrayList = arrayList.concat(item.children);
|
||||
}
|
||||
if (item.bindParameter && item.defaultValue) {
|
||||
callback[item.bindParameter] = item.defaultValue;
|
||||
}
|
||||
listQuery(arrayList, callback);
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
127
src/views/common/dynamicDictionary/Form.vue
Normal file
127
src/views/common/dynamicDictionary/Form.vue
Normal file
@@ -0,0 +1,127 @@
|
||||
<template>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
|
||||
<BasicForm @register="registerForm" />
|
||||
</BasicModal>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import {
|
||||
getDictionaryDataTypeSelector,
|
||||
getDictionaryDataInfo as getInfo,
|
||||
createDictionaryData as create,
|
||||
updateDictionaryData as update,
|
||||
} from '@/api/systemData/dictionary';
|
||||
import { ref, unref, computed } from 'vue';
|
||||
import { BasicModal, useModalInner } from '@/components/Modal';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
const emit = defineEmits(['register', 'reload']);
|
||||
const [registerForm, { setFieldsValue, resetFields, validate, updateSchema }] = useForm({
|
||||
schemas: [
|
||||
{
|
||||
field: 'parentId',
|
||||
label: '项目上级',
|
||||
defaultValue: '0',
|
||||
component: 'TreeSelect',
|
||||
componentProps: { placeholder: '请选择', showSearch: true },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'fullName',
|
||||
label: '字典名称',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'enCode',
|
||||
label: '字典编码',
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: '请输入', maxlength: 50 },
|
||||
rules: [{ required: true, trigger: 'blur', message: '必填' }],
|
||||
},
|
||||
{
|
||||
field: 'sortCode',
|
||||
label: '排序',
|
||||
defaultValue: 0,
|
||||
component: 'InputNumber',
|
||||
componentProps: { min: 0, max: 999999 },
|
||||
},
|
||||
{
|
||||
field: 'enabledMark',
|
||||
label: '状态',
|
||||
defaultValue: 1,
|
||||
component: 'Switch',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
label: '说明',
|
||||
component: 'Textarea',
|
||||
componentProps: { placeholder: '请输入', rows: 3 },
|
||||
},
|
||||
],
|
||||
});
|
||||
const [registerModal, { closeModal, changeLoading, changeOkLoading }] = useModalInner(init);
|
||||
const id = ref('');
|
||||
const isTree = ref(0);
|
||||
const typeId = ref('');
|
||||
const treeData = ref([]);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const baseStore = useBaseStore();
|
||||
|
||||
const getTitle = computed(() => (!unref(id) ? t('common.addText') : t('common.editText')));
|
||||
|
||||
function init(data) {
|
||||
changeLoading(true);
|
||||
resetFields();
|
||||
id.value = data.id;
|
||||
isTree.value = data.isTree;
|
||||
typeId.value = data.typeId;
|
||||
updateSchema({ field: 'parentId', componentProps: { disabled: !unref(isTree) } });
|
||||
getDictionaryDataTypeSelector(data.typeId, data.isTree, data.id).then(res => {
|
||||
treeData.value = res.data.list;
|
||||
updateSchema([
|
||||
{
|
||||
field: 'parentId',
|
||||
componentProps: { options: treeData.value },
|
||||
},
|
||||
]);
|
||||
if (id.value) {
|
||||
getInfo(id.value).then(res => {
|
||||
setFieldsValue(res.data);
|
||||
typeId.value = res.data.dictionaryTypeId;
|
||||
changeLoading(false);
|
||||
});
|
||||
} else {
|
||||
setFieldsValue({ parentId: res.data.list[0].id });
|
||||
changeLoading(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
const values = await validate();
|
||||
if (!values) return;
|
||||
changeOkLoading(true);
|
||||
const query = {
|
||||
...values,
|
||||
id: id.value,
|
||||
dictionaryTypeId: typeId.value,
|
||||
};
|
||||
const formMethod = id.value ? update : create;
|
||||
formMethod(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
changeOkLoading(false);
|
||||
baseStore.setDictionaryList();
|
||||
closeModal();
|
||||
emit('reload');
|
||||
})
|
||||
.catch(() => {
|
||||
changeOkLoading(false);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
116
src/views/common/dynamicDictionary/index.vue
Normal file
116
src/views/common/dynamicDictionary/index.vue
Normal file
@@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable" :searchInfo="searchInfo" :tableSetting="tableSetting">
|
||||
<template #tableTitle>
|
||||
<a-button type="primary" preIcon="icon-ym icon-ym-btn-add" @click="addOrUpdateHandle()">{{ t('common.addText') }}</a-button>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'enabledMark'">
|
||||
<a-tag :color="record.enabledMark == 1 ? 'success' : 'error'">{{ record.enabledMark == 1 ? '启用' : '禁用' }}</a-tag>
|
||||
</template>
|
||||
<template v-if="column.key === 'action'">
|
||||
<TableAction :actions="getTableActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form @register="registerForm" @reload="reload" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getDictionaryDataList, delDictionaryData } from '@/api/systemData/dictionary';
|
||||
import { BasicTable, useTable, TableAction, BasicColumn, ActionItem } from '@/components/Table';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import Form from './Form.vue';
|
||||
|
||||
defineOptions({ name: 'dynamic-dictionary' });
|
||||
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const baseStore = useBaseStore();
|
||||
const route = useRoute();
|
||||
const [registerForm, { openModal: openFormModal }] = useModal();
|
||||
|
||||
const columns: BasicColumn[] = [
|
||||
{ title: '名称', dataIndex: 'fullName' },
|
||||
{ title: '编码', dataIndex: 'enCode' },
|
||||
{ title: '排序', dataIndex: 'sortCode', width: 70, align: 'center' },
|
||||
{ title: '状态', dataIndex: 'enabledMark', width: 70, align: 'center' },
|
||||
];
|
||||
const searchInfo = reactive({
|
||||
typeId: '',
|
||||
isTree: 0,
|
||||
});
|
||||
const tableSetting = reactive({
|
||||
expand: false,
|
||||
});
|
||||
const [registerTable, { reload }] = useTable({
|
||||
api: getDictionaryDataList,
|
||||
columns,
|
||||
immediate: false,
|
||||
pagination: false,
|
||||
isTreeTable: true,
|
||||
useSearchForm: true,
|
||||
formConfig: {
|
||||
schemas: [
|
||||
{
|
||||
field: 'keyword',
|
||||
label: t('common.keyword'),
|
||||
component: 'Input',
|
||||
componentProps: { placeholder: t('common.enterKeyword'), submitOnPressEnter: true },
|
||||
colProps: { span: 6 },
|
||||
},
|
||||
],
|
||||
},
|
||||
actionColumn: {
|
||||
width: 100,
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
},
|
||||
});
|
||||
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [
|
||||
{
|
||||
label: t('common.editText'),
|
||||
onClick: addOrUpdateHandle.bind(null, record.id),
|
||||
},
|
||||
{
|
||||
label: t('common.delText'),
|
||||
color: 'error',
|
||||
modelConfirm: {
|
||||
onOk: handleDelete.bind(null, record.id),
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
function addOrUpdateHandle(id = '') {
|
||||
openFormModal(true, {
|
||||
id,
|
||||
...searchInfo,
|
||||
});
|
||||
}
|
||||
function handleDelete(id) {
|
||||
delDictionaryData(id).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
baseStore.setDictionaryList();
|
||||
reload();
|
||||
});
|
||||
}
|
||||
onMounted(() => {
|
||||
const { meta } = route;
|
||||
searchInfo.typeId = meta.relationId as string;
|
||||
searchInfo.isTree = (meta.isTree as number) || 0;
|
||||
tableSetting.expand = !!searchInfo.isTree;
|
||||
searchInfo.typeId && reload();
|
||||
reload();
|
||||
});
|
||||
</script>
|
||||
144
src/views/common/dynamicModel/form/FormPopup.vue
Normal file
144
src/views/common/dynamicModel/form/FormPopup.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<BasicPopup v-bind="$attrs" @register="registerPopup" :show-back-icon="false" :show-cancel-btn="false" :title="config.fullName">
|
||||
<template #insertToolbar>
|
||||
<a-button type="primary" @click="handleSubmit" :loading="btnLoading">{{ getOkText }}</a-button>
|
||||
<a-button type="warning" class="ml-10px" @click="handleReset">{{ t('common.resetText') }}</a-button>
|
||||
</template>
|
||||
<div class="p-10px" :style="{ margin: '0 auto', width: formConf.fullScreenWidth || '100%' }">
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicPopup>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { createModel } from '@/api/onlineDev/visualDev';
|
||||
import { reactive, toRefs, nextTick, ref, unref, computed } from 'vue';
|
||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
|
||||
import { BasicPopup, usePopupInner } from '@/components/Popup';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import dayjs from 'dayjs';
|
||||
import { getDateTimeUnit } from '@/utils/yunzhupaas';
|
||||
|
||||
interface State {
|
||||
formConf: any;
|
||||
config: any;
|
||||
loading: boolean;
|
||||
btnLoading: boolean;
|
||||
key: number;
|
||||
}
|
||||
|
||||
defineEmits(['register']);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const [registerPopup, { changeLoading }] = usePopupInner(init);
|
||||
const parserRef = ref<any>(null);
|
||||
const state = reactive<State>({
|
||||
formConf: {},
|
||||
config: {},
|
||||
loading: false,
|
||||
btnLoading: false,
|
||||
key: +new Date(),
|
||||
});
|
||||
const { formConf, key, loading, config, btnLoading } = toRefs(state);
|
||||
const Parser = createAsyncComponent(() => import('@/components/FormGenerator/src/components/Parser.vue'));
|
||||
|
||||
const getOkText = computed(() => {
|
||||
const text = state.formConf.confirmButtonTextI18nCode
|
||||
? t(state.formConf.confirmButtonTextI18nCode, state.formConf.confirmButtonText)
|
||||
: state.formConf.confirmButtonText;
|
||||
return text || t('common.okText');
|
||||
});
|
||||
|
||||
function fillFormData(form, data) {
|
||||
const userInfo = userStore.getUserInfo;
|
||||
const currDate = new Date();
|
||||
const loop = list => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item.__vModel__) {
|
||||
if (item.__config__.defaultCurrent) {
|
||||
if (item.__config__.yunzhupaasKey === 'datePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).startOf(getDateTimeUnit(item.format)).valueOf();
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'timePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).format(item.format || 'HH:mm:ss');
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'organizeSelect' && userInfo.organizeIdList?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.organizeIdList] : userInfo.organizeIdList;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'depSelect' && userInfo.departmentId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.departmentId] : userInfo.departmentId;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'userSelect' && userInfo.userId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.userId] : userInfo.userId;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'usersSelect' && userInfo.userId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.userId + '--user'] : userInfo.userId + '--user';
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'posSelect' && userInfo.positionIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.positionIds.map(o => o.id) : userInfo.positionIds[0].id;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'roleSelect' && userInfo.roleIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.roleIds : userInfo.roleIds[0];
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'groupSelect' && userInfo.groupIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.groupIds : userInfo.groupIds[0];
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'sign' && userInfo.signImg) {
|
||||
item.__config__.defaultValue = userInfo.signImg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.__config__ && item.__config__.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children);
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(form.fields);
|
||||
form.formData = data;
|
||||
}
|
||||
function init(data) {
|
||||
changeLoading(true);
|
||||
state.loading = true;
|
||||
state.config = data;
|
||||
state.formConf = data.formData ? JSON.parse(data.formData) : {};
|
||||
fillFormData(state.formConf, {});
|
||||
nextTick(() => {
|
||||
changeLoading(false);
|
||||
state.loading = false;
|
||||
state.key = +new Date();
|
||||
});
|
||||
}
|
||||
function submitForm(data, callback) {
|
||||
if (!data) return;
|
||||
state.btnLoading = true;
|
||||
const dataForm = { data: JSON.stringify(data) };
|
||||
createModel(state.config.modelId, dataForm)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
state.btnLoading = false;
|
||||
handleReset();
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
function handleReset() {
|
||||
fillFormData(state.formConf, {});
|
||||
nextTick(() => {
|
||||
getParser().handleReset();
|
||||
});
|
||||
}
|
||||
function handleSubmit() {
|
||||
if (state.config.isPreview) return createMessage.warning('功能预览不支持数据保存');
|
||||
getParser().handleSubmit();
|
||||
}
|
||||
function getParser() {
|
||||
const parser = unref(parserRef);
|
||||
if (!parser) throw new Error('parser is null!');
|
||||
return parser;
|
||||
}
|
||||
</script>
|
||||
41
src/views/common/dynamicModel/form/index.vue
Normal file
41
src/views/common/dynamicModel/form/index.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper bg-white">
|
||||
<FormPopup @register="registerFormPopup" />
|
||||
<FlowParser @register="registerFlowParser" @reload="init()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue';
|
||||
import { usePopup } from '@/components/Popup';
|
||||
import FormPopup from './FormPopup.vue';
|
||||
import FlowParser from '@/views/workFlow/components/FlowParser.vue';
|
||||
|
||||
const props = defineProps(['config', 'modelId', 'isPreview']);
|
||||
const [registerFormPopup, { openPopup: openFormPopup }] = usePopup();
|
||||
const [registerFlowParser, { openPopup: openFlowParser }] = usePopup();
|
||||
|
||||
function openFlowPopup() {
|
||||
const data = {
|
||||
id: '',
|
||||
flowId: props.config.flowId,
|
||||
opType: '-1',
|
||||
hideCancelBtn: true,
|
||||
hideSaveBtn: true,
|
||||
};
|
||||
openFlowParser(true, data);
|
||||
}
|
||||
function init() {
|
||||
if (props.config.enableFlow) return openFlowPopup();
|
||||
const data = {
|
||||
modelId: props.modelId,
|
||||
isPreview: props.isPreview,
|
||||
...props.config,
|
||||
};
|
||||
openFormPopup(true, data);
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
97
src/views/common/dynamicModel/index.vue
Normal file
97
src/views/common/dynamicModel/index.vue
Normal file
@@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<component :is="currentView" :config="config" :modelId="modelId" :isPreview="isPreview" :isDataManage="isDataManage" v-if="showPage" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted, toRefs, markRaw } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { getConfigData } from '@/api/onlineDev/visualDev';
|
||||
import { getFlowStartFormId } from '@/api/workFlow/template';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useTabs } from '@/hooks/web/useTabs';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import Form from './form/index.vue';
|
||||
import List from './list/index.vue';
|
||||
|
||||
interface State {
|
||||
currentView: any;
|
||||
showPage: boolean;
|
||||
isPreview: boolean;
|
||||
isDataManage: boolean;
|
||||
previewType: string;
|
||||
modelId: string;
|
||||
flowId: string;
|
||||
enableFlow: number;
|
||||
config: any;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'dynamicModel' });
|
||||
const { createMessage } = useMessage();
|
||||
const baseStore = useBaseStore();
|
||||
const { close } = useTabs();
|
||||
const state = reactive<State>({
|
||||
currentView: '',
|
||||
showPage: false,
|
||||
isPreview: false,
|
||||
isDataManage: false,
|
||||
previewType: '',
|
||||
modelId: '',
|
||||
flowId: '',
|
||||
enableFlow: 0,
|
||||
config: {},
|
||||
});
|
||||
const { currentView, showPage, isPreview, isDataManage, modelId, config } = toRefs(state);
|
||||
const router = useRouter();
|
||||
|
||||
async function init() {
|
||||
const route = useRoute();
|
||||
await baseStore.getDictionaryAll();
|
||||
state.isPreview = (route.query.isPreview as unknown as boolean) || false;
|
||||
state.isDataManage = (route.query.isDataManage as unknown as boolean) || false;
|
||||
if (state.isPreview || state.isDataManage) {
|
||||
if (state.isPreview) {
|
||||
state.previewType = (route.query.previewType as string) || '';
|
||||
}
|
||||
getConfig(route.query.id);
|
||||
return;
|
||||
}
|
||||
state.enableFlow = route.meta.type === 9 ? 1 : 0;
|
||||
if (!state.enableFlow) return getConfig(route.meta.relationId);
|
||||
getModelId(route.meta.relationId);
|
||||
}
|
||||
function getModelId(flowId) {
|
||||
state.flowId = flowId;
|
||||
getFlowStartFormId(flowId)
|
||||
.then(res => {
|
||||
if (!res?.data || !res?.data.formId) return;
|
||||
getConfig(res.data.formId);
|
||||
})
|
||||
.catch(() => {
|
||||
close();
|
||||
router.replace('/404');
|
||||
});
|
||||
}
|
||||
function getConfig(modelId) {
|
||||
if (!modelId) return;
|
||||
state.modelId = modelId;
|
||||
getConfigData(state.modelId, { type: state.previewType }).then(res => {
|
||||
if (res.code !== 200 || !res.data) {
|
||||
close();
|
||||
router.replace('/404');
|
||||
createMessage.error(res.msg || '请求出错,请重试');
|
||||
return;
|
||||
}
|
||||
state.config = res.data;
|
||||
state.config.id = state.config.id || state.modelId;
|
||||
if (state.enableFlow) {
|
||||
state.config.enableFlow = state.enableFlow;
|
||||
state.config.flowId = state.flowId;
|
||||
}
|
||||
state.currentView = res.data.webType == '1' ? markRaw(Form) : markRaw(List);
|
||||
state.showPage = true;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
142
src/views/common/dynamicModel/list/ChildTableColumn.vue
Normal file
142
src/views/common/dynamicModel/list/ChildTableColumn.vue
Normal file
@@ -0,0 +1,142 @@
|
||||
<template>
|
||||
<div class="child-table-column">
|
||||
<template v-if="!expand">
|
||||
<tr v-for="(item, index) in fewData" class="child-table__row" :key="index">
|
||||
<td
|
||||
v-for="(headItem, i) in head"
|
||||
:key="i"
|
||||
:style="{ width: `${headItem.width}px`, 'text-align': headItem.align }"
|
||||
:class="{ 'td-flex-1': !headItem.width }">
|
||||
<div class="cell" v-if="headItem.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text" :title="item[headItem.dataIndex]" @click="toDetail(headItem.modelId, item[`${headItem.dataIndex}_id`], headItem.propsValue)">
|
||||
{{ item[headItem.dataIndex] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number v-model:value="item[headItem.dataIndex]" :precision="headItem.precision" :thousands="headItem.thousands" disabled detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
v-model:value="item[headItem.dataIndex]"
|
||||
:isStorage="headItem.isStorage"
|
||||
:precision="headItem.precision"
|
||||
:thousands="headItem.thousands"
|
||||
:roundType="headItem.roundType"
|
||||
detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="item[headItem.dataIndex]" detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'signature'">
|
||||
<yunzhupaas-signature v-model:value="item[headItem.dataIndex]" detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="item[headItem.dataIndex]" :count="headItem.count" :allowHalf="headItem.allowHalf" disabled />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="item[headItem.dataIndex]" :min="headItem.min" :max="headItem.max" :step="headItem.step" disabled />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="item[headItem.dataIndex]" disabled detailed simple v-if="item[headItem.dataIndex]?.length" />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="item[headItem.dataIndex]" disabled detailed simple v-if="item[headItem.dataIndex]?.length" />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="item[headItem.dataIndex]"
|
||||
:useMask="headItem.useMask"
|
||||
:maskConfig="headItem.maskConfig"
|
||||
:showOverflow="showOverflow"
|
||||
detailed />
|
||||
</div>
|
||||
<div class="cell" :class="{ ellipsis: showOverflow }" :title="item[headItem.dataIndex]" v-else>{{ item[headItem.dataIndex] }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<template v-if="expand">
|
||||
<tr v-for="(item, index) in data" class="child-table__row" :key="index">
|
||||
<td
|
||||
v-for="(headItem, i) in head"
|
||||
:key="i"
|
||||
:style="{ width: `${headItem.width}px`, 'text-align': headItem.align }"
|
||||
:class="{ 'td-flex-1': !headItem.width }">
|
||||
<div class="cell" v-if="headItem.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text" :title="item[headItem.dataIndex]" @click="toDetail(headItem.modelId, item[`${headItem.dataIndex}_id`], headItem.propsValue)">
|
||||
{{ item[headItem.dataIndex] }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number v-model:value="item[headItem.dataIndex]" :precision="headItem.precision" :thousands="headItem.thousands" disabled detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
v-model:value="item[headItem.dataIndex]"
|
||||
:isStorage="headItem.isStorage"
|
||||
:precision="headItem.precision"
|
||||
:thousands="headItem.thousands"
|
||||
:roundType="headItem.roundType"
|
||||
detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="item[headItem.dataIndex]" detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'signature '">
|
||||
<yunzhupaas-signature v-model:value="item[headItem.dataIndex]" detailed />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="item[headItem.dataIndex]" :count="headItem.count" :allowHalf="headItem.allowHalf" disabled />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="item[headItem.dataIndex]" :min="headItem.min" :max="headItem.max" :step="headItem.step" disabled />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="item[headItem.dataIndex]" disabled detailed simple v-if="item[headItem.dataIndex]?.length" />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="item[headItem.dataIndex]" disabled detailed simple v-if="item[headItem.dataIndex]?.length" />
|
||||
</div>
|
||||
<div class="cell" v-else-if="headItem.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="item[headItem.dataIndex]"
|
||||
:useMask="headItem.useMask"
|
||||
:maskConfig="headItem.maskConfig"
|
||||
:showOverflow="showOverflow"
|
||||
detailed />
|
||||
</div>
|
||||
<div class="cell" :class="{ ellipsis: showOverflow }" :title="item[headItem.dataIndex]" v-else>{{ item[headItem.dataIndex] }}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<div class="expand-more-btn" v-if="data && data.length > defaultNumber">
|
||||
<a-button v-if="expand" type="link" @click="toggleExpand">{{ t('views.dynamicModel.hideSome') }}</a-button>
|
||||
<a-button v-if="!expand" type="link" @click="toggleExpand">{{ t('views.dynamicModel.showMore') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import type { PropType } from 'vue';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
defineOptions({ name: 'childTableColumn' });
|
||||
const props = defineProps({
|
||||
data: { type: Array as PropType<any[]>, default: () => [] },
|
||||
head: { type: Array as PropType<any[]>, default: () => [] },
|
||||
defaultNumber: { type: Number, default: 3 },
|
||||
expand: { type: Boolean, default: false },
|
||||
showOverflow: { type: Boolean, default: true },
|
||||
});
|
||||
const emit = defineEmits(['toggleExpand', 'toDetail']);
|
||||
const { t } = useI18n();
|
||||
|
||||
const fewData = computed(() => (props.data ? props.data.slice(0, props.defaultNumber) : []));
|
||||
|
||||
function toggleExpand() {
|
||||
emit('toggleExpand');
|
||||
}
|
||||
function toDetail(modelId, id, propsValue) {
|
||||
emit('toDetail', modelId, id, propsValue);
|
||||
}
|
||||
</script>
|
||||
244
src/views/common/dynamicModel/list/CustomForm.vue
Normal file
244
src/views/common/dynamicModel/list/CustomForm.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<BasicPopup v-bind="$attrs" @register="registerPopup" :title="config.popupTitle" showOkBtn :okText="getOkText" destroyOnClose @ok="handleSubmit()">
|
||||
<div class="p-10px" :style="{ margin: '0 auto', width: config.popupWidth || '100%' }">
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicPopup>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:title="config.popupTitle"
|
||||
:width="config.popupWidth"
|
||||
:minHeight="100"
|
||||
:okText="getOkText"
|
||||
@ok="handleSubmit()">
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</BasicModal>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
:title="config.popupTitle"
|
||||
:width="config.popupWidth"
|
||||
showFooter
|
||||
:okText="getOkText"
|
||||
@ok="handleSubmit()">
|
||||
<div class="p-10px">
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { createModel, getModelInfo, getConfigData } from '@/api/onlineDev/visualDev';
|
||||
import { getDataInterfaceRes } from '@/api/systemData/dataInterface';
|
||||
import { reactive, toRefs, nextTick, ref, unref, computed } from 'vue';
|
||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
|
||||
import { BasicPopup, usePopup } from '@/components/Popup';
|
||||
import { BasicModal, useModal } from '@/components/Modal';
|
||||
import { BasicDrawer, useDrawer } from '@/components/Drawer';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import dayjs from 'dayjs';
|
||||
import { getDateTimeUnit, getParamList } from '@/utils/yunzhupaas';
|
||||
|
||||
interface State {
|
||||
formConf: any;
|
||||
formData: any;
|
||||
config: any;
|
||||
loading: boolean;
|
||||
key: number;
|
||||
dataForm: any;
|
||||
formOperates: any[];
|
||||
}
|
||||
|
||||
const emit = defineEmits(['reload']);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const [registerPopup, { openPopup, setPopupProps }] = usePopup();
|
||||
const [registerModal, { openModal, setModalProps }] = useModal();
|
||||
const [registerDrawer, { openDrawer, setDrawerProps }] = useDrawer();
|
||||
const parserRef = ref<any>(null);
|
||||
const state = reactive<State>({
|
||||
formConf: {},
|
||||
formData: {},
|
||||
config: {},
|
||||
loading: false,
|
||||
key: +new Date(),
|
||||
dataForm: {
|
||||
id: '',
|
||||
data: '',
|
||||
},
|
||||
formOperates: [],
|
||||
});
|
||||
const { config, formConf, key, loading } = toRefs(state);
|
||||
const Parser = createAsyncComponent(() => import('@/components/FormGenerator/src/components/Parser.vue'));
|
||||
|
||||
const getOkText = computed(() => {
|
||||
const text = state.formConf.confirmButtonTextI18nCode
|
||||
? t(state.formConf.confirmButtonTextI18nCode, state.formConf.confirmButtonText)
|
||||
: state.formConf.confirmButtonText;
|
||||
return text || t('common.okText');
|
||||
});
|
||||
|
||||
defineExpose({ init });
|
||||
|
||||
function fillFormData(form, data) {
|
||||
const userInfo = userStore.getUserInfo;
|
||||
const currDate = new Date();
|
||||
const loop = (list, parent?) => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item.__vModel__) {
|
||||
if (item.__config__.defaultCurrent) {
|
||||
if (item.__config__.yunzhupaasKey === 'datePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).startOf(getDateTimeUnit(item.format)).valueOf();
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'timePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).format(item.format || 'HH:mm:ss');
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'organizeSelect' && userInfo.organizeIdList?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.organizeIdList] : userInfo.organizeIdList;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'depSelect' && userInfo.departmentId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.departmentId] : userInfo.departmentId;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'userSelect' && userInfo.userId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.userId] : userInfo.userId;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'usersSelect' && userInfo.userId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.userId + '--user'] : userInfo.userId + '--user';
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'posSelect' && userInfo.positionIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.positionIds.map(o => o.id) : userInfo.positionIds[0].id;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'roleSelect' && userInfo.roleIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.roleIds : userInfo.roleIds[0];
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'groupSelect' && userInfo.groupIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.groupIds : userInfo.groupIds[0];
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'sign' && userInfo.signImg) {
|
||||
item.__config__.defaultValue = userInfo.signImg;
|
||||
}
|
||||
}
|
||||
let val = data.hasOwnProperty(item.__vModel__) ? data[item.__vModel__] : item.__config__.defaultValue;
|
||||
item.__config__.defaultValue = val;
|
||||
if (!state.config.isPreview && state.config.useFormPermission) {
|
||||
let id = item.__config__.isSubTable ? parent.__vModel__ + '-' + item.__vModel__ : item.__vModel__;
|
||||
let noShow = true;
|
||||
if (state.formOperates && state.formOperates.length) {
|
||||
noShow = !state.formOperates.some(o => o.enCode === id);
|
||||
}
|
||||
noShow = item.__config__.noShow ? item.__config__.noShow : noShow;
|
||||
item.__config__.noShow = noShow;
|
||||
}
|
||||
}
|
||||
if (item.__config__ && item.__config__.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(form.fields);
|
||||
form.formData = data;
|
||||
}
|
||||
function init(data) {
|
||||
state.config = data;
|
||||
state.formData = {};
|
||||
openForm();
|
||||
nextTick(() => {
|
||||
setTimeout(() => {
|
||||
if (state.config.modelId) initData();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
function initData() {
|
||||
changeLoading(true);
|
||||
state.loading = true;
|
||||
getConfigData(state.config.modelId).then(res => {
|
||||
if (res.code !== 200 || !res.data) return createMessage.error(res.msg || '请求出错,请重试');
|
||||
if (!res.data.formData) return;
|
||||
state.formConf = JSON.parse(res.data.formData);
|
||||
if (state.config.webType == '4' || !state.config.record.id) return setFormValue(state.config.record);
|
||||
getInfo();
|
||||
});
|
||||
}
|
||||
function getInfo() {
|
||||
getModelInfo(state.config.recordModelId, state.config.record.id).then(res => {
|
||||
if (!res.data || !res.data.data) return;
|
||||
const formData = JSON.parse(res.data.data);
|
||||
setFormValue({ ...formData, id: state.config.record.id });
|
||||
});
|
||||
}
|
||||
function setFormValue(formData) {
|
||||
if (state.config.formOptions.length) {
|
||||
for (let [key, value] of Object.entries(formData)) {
|
||||
for (let i = 0; i < state.config.formOptions.length; i++) {
|
||||
const e = state.config.formOptions[i];
|
||||
if (e.currentField == '@formId') state.formData[e.field] = formData[state.config.rowKey];
|
||||
if (e.currentField == key) state.formData[e.field] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
fillFormData(state.formConf, state.formData);
|
||||
nextTick(() => {
|
||||
state.loading = false;
|
||||
state.key = +new Date();
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
function submitForm(data, callback) {
|
||||
if (!data) return;
|
||||
setFormProps({ confirmLoading: true });
|
||||
if (state.config.customBtn) {
|
||||
const query = { paramList: getParamList(state.config.templateJson, { ...data, id: state.config.record.id }) || [] };
|
||||
getDataInterfaceRes(state.config.interfaceId, query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
setFormProps({ confirmLoading: false });
|
||||
setFormProps({ open: false });
|
||||
if (state.config?.isRefresh) emit('reload');
|
||||
})
|
||||
.catch(() => {
|
||||
setFormProps({ confirmLoading: false });
|
||||
});
|
||||
} else {
|
||||
const formData = { ...state.formData, ...data };
|
||||
state.dataForm.data = JSON.stringify(formData);
|
||||
createModel(state.config.modelId, state.dataForm)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
setFormProps({ confirmLoading: false });
|
||||
setFormProps({ open: false });
|
||||
if (state.config?.isRefresh) emit('reload');
|
||||
})
|
||||
.catch(() => {
|
||||
setFormProps({ confirmLoading: false });
|
||||
});
|
||||
}
|
||||
}
|
||||
function handleSubmit() {
|
||||
if (state.config.isPreview) return createMessage.warning('功能预览不支持数据保存');
|
||||
getParser().handleSubmit();
|
||||
}
|
||||
function getParser() {
|
||||
const parser = unref(parserRef);
|
||||
if (!parser) throw new Error('parser is null!');
|
||||
return parser;
|
||||
}
|
||||
function openForm() {
|
||||
if (state.config.popupType === 'fullScreen') return openPopup();
|
||||
if (state.config.popupType === 'drawer') return openDrawer();
|
||||
openModal();
|
||||
}
|
||||
function setFormProps(data) {
|
||||
if (state.config.popupType === 'fullScreen') return setPopupProps(data);
|
||||
if (state.config.popupType === 'drawer') return setDrawerProps(data);
|
||||
setModalProps(data);
|
||||
}
|
||||
function changeLoading(loading) {
|
||||
setFormProps({ loading });
|
||||
}
|
||||
</script>
|
||||
395
src/views/common/dynamicModel/list/Form.vue
Normal file
395
src/views/common/dynamicModel/list/Form.vue
Normal file
@@ -0,0 +1,395 @@
|
||||
<template>
|
||||
<BasicPopup
|
||||
v-bind="$attrs"
|
||||
@register="registerPopup"
|
||||
showOkBtn
|
||||
:okText="getOkText"
|
||||
:cancelText="getCancelText"
|
||||
destroyOnClose
|
||||
@ok="handleSubmit"
|
||||
:closeFunc="onClose"
|
||||
class="full-popup">
|
||||
<template #title>
|
||||
<a-space :size="10">
|
||||
<div class="text-16px font-medium">{{ title }}</div>
|
||||
<a-space-compact size="small" block v-if="getShowMoreBtn">
|
||||
<a-tooltip :title="t('common.prevRecord')">
|
||||
<a-button size="small" :loading="state.prevBtnLoading" :disabled="getPrevDisabled" @click="handlePrev">
|
||||
<i class="icon-ym icon-ym-caret-left text-10px"></i>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="t('common.nextRecord')">
|
||||
<a-button size="small" :loading="state.nextBtnLoading" :disabled="getNextDisabled" @click="handleNext">
|
||||
<i class="icon-ym icon-ym-caret-right text-10px"></i>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space-compact>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #insertToolbar>
|
||||
<YunzhupaasCheckboxSingle v-model:value="submitType" :label="continueText" v-if="showContinueBtn" />
|
||||
</template>
|
||||
<div class="yunzhupaas-common-form-wrapper">
|
||||
<div class="yunzhupaas-common-form-wrapper__main p-10px" :style="{ margin: '0 auto', width: formConf.fullScreenWidth || '100%' }">
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
<FormExtraPanel v-bind="getFormExtraBind" v-if="state.dataForm.id && formConf.dataLog && !loading" :key="key" />
|
||||
</div>
|
||||
</BasicPopup>
|
||||
<BasicModal
|
||||
v-bind="$attrs"
|
||||
@register="registerModal"
|
||||
:width="formConf.generalWidth"
|
||||
:minHeight="100"
|
||||
:okText="getOkText"
|
||||
:cancelText="getCancelText"
|
||||
@ok="handleSubmit"
|
||||
:closeFunc="onClose">
|
||||
<template #title>
|
||||
<a-space :size="10">
|
||||
<div class="text-16px font-medium">{{ title }}</div>
|
||||
<a-space-compact size="small" block v-if="getShowMoreBtn">
|
||||
<a-tooltip :title="t('common.prevRecord')">
|
||||
<a-button size="small" :loading="state.prevBtnLoading" :disabled="getPrevDisabled" @click="handlePrev">
|
||||
<i class="icon-ym icon-ym-caret-left text-10px"></i>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="t('common.nextRecord')">
|
||||
<a-button size="small" :loading="state.nextBtnLoading" :disabled="getNextDisabled" @click="handleNext">
|
||||
<i class="icon-ym icon-ym-caret-right text-10px"></i>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space-compact>
|
||||
</a-space>
|
||||
</template>
|
||||
<template #insertFooter>
|
||||
<div class="float-left mt-5px" v-if="showContinueBtn">
|
||||
<YunzhupaasCheckboxSingle v-model:value="submitType" :label="continueText" />
|
||||
</div>
|
||||
</template>
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</BasicModal>
|
||||
<BasicDrawer
|
||||
v-bind="$attrs"
|
||||
@register="registerDrawer"
|
||||
:width="formConf.drawerWidth"
|
||||
showFooter
|
||||
:okText="getOkText"
|
||||
:cancelText="getCancelText"
|
||||
@ok="handleSubmit"
|
||||
:closeFunc="onClose">
|
||||
<template #title>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="text-16px font-medium">{{ title }}</div>
|
||||
<a-space-compact size="small" v-if="getShowMoreBtn">
|
||||
<a-tooltip :title="t('common.prevRecord')">
|
||||
<a-button size="small" :loading="state.prevBtnLoading" :disabled="getPrevDisabled" @click="handlePrev">
|
||||
<i class="icon-ym icon-ym-caret-left text-10px"></i>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip :title="t('common.nextRecord')">
|
||||
<a-button size="small" :loading="state.nextBtnLoading" :disabled="getNextDisabled" @click="handleNext">
|
||||
<i class="icon-ym icon-ym-caret-right text-10px"></i>
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</a-space-compact>
|
||||
</div>
|
||||
</template>
|
||||
<template #insertFooter>
|
||||
<div class="float-left mt-5px" v-if="showContinueBtn">
|
||||
<YunzhupaasCheckboxSingle v-model:value="submitType" :label="continueText" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="p-10px">
|
||||
<Parser ref="parserRef" :formConf="formConf" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicDrawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { createModel, updateModel, getModelInfo } from '@/api/onlineDev/visualDev';
|
||||
import { reactive, toRefs, nextTick, ref, unref, computed, inject } from 'vue';
|
||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
|
||||
import { BasicPopup, usePopup } from '@/components/Popup';
|
||||
import { BasicModal, useModal } from '@/components/Modal';
|
||||
import { BasicDrawer, useDrawer } from '@/components/Drawer';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useGeneratorStore } from '@/store/modules/generator';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import dayjs from 'dayjs';
|
||||
import { getDateTimeUnit } from '@/utils/yunzhupaas';
|
||||
import FormExtraPanel from '@/components/FormExtraPanel/index.vue';
|
||||
|
||||
interface State {
|
||||
formConf: any;
|
||||
defaultFormConf: any;
|
||||
formData: any;
|
||||
config: any;
|
||||
loading: boolean;
|
||||
prevBtnLoading: boolean;
|
||||
nextBtnLoading: boolean;
|
||||
key: number;
|
||||
dataForm: any;
|
||||
formOperates: any[];
|
||||
title: string;
|
||||
continueText: string;
|
||||
allList: any[];
|
||||
currIndex: number;
|
||||
isContinue: boolean;
|
||||
submitType: number;
|
||||
showContinueBtn: boolean;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['reload']);
|
||||
const getLeftTreeActiveInfo: (() => any) | null = inject('getLeftTreeActiveInfo', null);
|
||||
const userStore = useUserStore();
|
||||
const generatorStore = useGeneratorStore();
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerPopup, { openPopup, setPopupProps }] = usePopup();
|
||||
const [registerModal, { openModal, setModalProps }] = useModal();
|
||||
const [registerDrawer, { openDrawer, setDrawerProps }] = useDrawer();
|
||||
const parserRef = ref<any>(null);
|
||||
const state = reactive<State>({
|
||||
formConf: {},
|
||||
defaultFormConf: {},
|
||||
formData: {},
|
||||
config: {},
|
||||
loading: true,
|
||||
prevBtnLoading: false,
|
||||
nextBtnLoading: false,
|
||||
key: +new Date(),
|
||||
dataForm: {
|
||||
id: '',
|
||||
data: '',
|
||||
},
|
||||
formOperates: [],
|
||||
title: '',
|
||||
continueText: '',
|
||||
allList: [],
|
||||
currIndex: 0,
|
||||
isContinue: false,
|
||||
submitType: 0,
|
||||
showContinueBtn: true,
|
||||
});
|
||||
const { title, formConf, key, loading, continueText, showContinueBtn, submitType } = toRefs(state);
|
||||
const Parser = createAsyncComponent(() => import('@/components/FormGenerator/src/components/Parser.vue'));
|
||||
|
||||
const getPrevDisabled = computed(() => state.currIndex === 0);
|
||||
const getNextDisabled = computed(() => state.currIndex === state.allList.length - 1);
|
||||
const getShowMoreBtn = computed(() => state.config.id && state.config.showMoreBtn && state.formConf.hasConfirmAndAddBtn);
|
||||
const getOkText = computed(() => {
|
||||
const text = state.formConf.confirmButtonTextI18nCode
|
||||
? t(state.formConf.confirmButtonTextI18nCode, state.formConf.confirmButtonText)
|
||||
: state.formConf.confirmButtonText;
|
||||
return text || t('common.okText');
|
||||
});
|
||||
const getCancelText = computed(() => {
|
||||
const text = state.formConf.cancelButtonTextI18nCode
|
||||
? t(state.formConf.cancelButtonTextI18nCode, state.formConf.cancelButtonText)
|
||||
: state.formConf.cancelButtonText;
|
||||
return text || t('common.cancelText');
|
||||
});
|
||||
const getFormExtraBind = computed(() => ({ showLog: state.formConf.dataLog, modelId: state.config.modelId, formDataId: state.config.id }));
|
||||
|
||||
defineExpose({ init });
|
||||
|
||||
function fillFormData(form, data, isAdd) {
|
||||
const userInfo = userStore.getUserInfo;
|
||||
const currDate = new Date();
|
||||
const loop = (list, parent?) => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item.__vModel__) {
|
||||
let val = data.hasOwnProperty(item.__vModel__) ? data[item.__vModel__] : item.__config__.defaultValue;
|
||||
if (!item.__config__.isSubTable) item.__config__.defaultValue = val;
|
||||
if (isAdd || item.__config__.isSubTable) {
|
||||
if (item.__config__.defaultCurrent) {
|
||||
if (item.__config__.yunzhupaasKey === 'datePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).startOf(getDateTimeUnit(item.format)).valueOf();
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'timePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).format(item.format || 'HH:mm:ss');
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'organizeSelect' && userInfo.organizeIdList?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.organizeIdList] : userInfo.organizeIdList;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'depSelect' && userInfo.departmentId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.departmentId] : userInfo.departmentId;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'userSelect' && userInfo.userId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.userId] : userInfo.userId;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'usersSelect' && userInfo.userId) {
|
||||
item.__config__.defaultValue = item.multiple ? [userInfo.userId + '--user'] : userInfo.userId + '--user';
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'posSelect' && userInfo.positionIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.positionIds.map(o => o.id) : userInfo.positionIds[0].id;
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'roleSelect' && userInfo.roleIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.roleIds : userInfo.roleIds[0];
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'groupSelect' && userInfo.groupIds?.length) {
|
||||
item.__config__.defaultValue = item.multiple ? userInfo.groupIds : userInfo.groupIds[0];
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'sign' && userInfo.signImg) {
|
||||
item.__config__.defaultValue = userInfo.signImg;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isAdd && !item.__config__.isSubTable && data.hasOwnProperty(item.__vModel__)) item.__config__.defaultValue = data[item.__vModel__];
|
||||
if (!state.config.isPreview && !state.config.isDataManage && state.config.useFormPermission) {
|
||||
let id = item.__config__.isSubTable ? parent.__vModel__ + '-' + item.__vModel__ : item.__vModel__;
|
||||
let noShow = true;
|
||||
if (state.formOperates && state.formOperates.length) {
|
||||
noShow = !state.formOperates.some(o => o.enCode === id);
|
||||
}
|
||||
noShow = item.__config__.noShow ? item.__config__.noShow : noShow;
|
||||
item.__config__.noShow = noShow;
|
||||
}
|
||||
}
|
||||
if (item.__config__ && item.__config__.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(form.fields);
|
||||
form.formData = data;
|
||||
}
|
||||
function init(data) {
|
||||
state.loading = true;
|
||||
state.submitType = 0;
|
||||
state.isContinue = false;
|
||||
state.prevBtnLoading = false;
|
||||
state.nextBtnLoading = false;
|
||||
state.title = !data.id || data.id === 'yunzhupaasAdd' ? t('common.add2Text') : t('common.editText');
|
||||
state.continueText = !data.id ? t('common.continueAndAddText') : t('common.continueText');
|
||||
state.config = data;
|
||||
state.defaultFormConf = cloneDeep(data.formConf);
|
||||
state.formConf = cloneDeep(state.defaultFormConf);
|
||||
state.showContinueBtn = !data.formData && state.formConf.hasConfirmAndAddBtn;
|
||||
state.dataForm.id = !data.id || data.id === 'yunzhupaasAdd' ? '' : data.id;
|
||||
getFormOperates();
|
||||
openForm();
|
||||
state.allList = data.allList;
|
||||
state.currIndex = state.allList.length && data.id ? state.allList.findIndex(item => item.id === data.id) : 0;
|
||||
nextTick(() => {
|
||||
if (!data.formData) return setTimeout(initData, 0);
|
||||
// 行内编辑
|
||||
setTimeout(() => {
|
||||
state.formData = { ...data.formData, id: state.dataForm.id };
|
||||
setFormValue();
|
||||
}, 0);
|
||||
});
|
||||
}
|
||||
function initData() {
|
||||
changeLoading(true);
|
||||
state.loading = true;
|
||||
if (state.config.id) {
|
||||
const extra = { modelId: state.config.modelId, id: state.config.id, type: 2 };
|
||||
generatorStore.setDynamicModelExtra(extra);
|
||||
getInfo(state.config.id);
|
||||
} else {
|
||||
generatorStore.setDynamicModelExtra({});
|
||||
state.formData = {};
|
||||
setFormValue(true);
|
||||
}
|
||||
}
|
||||
function getInfo(id) {
|
||||
getModelInfo(state.config.modelId, id, state.config.menuId).then(res => {
|
||||
state.dataForm = res.data || {};
|
||||
if (!state.dataForm.data) return;
|
||||
state.formData = { ...JSON.parse(state.dataForm.data), id: state.dataForm.id };
|
||||
setFormValue();
|
||||
});
|
||||
}
|
||||
function setFormValue(isAdd = false) {
|
||||
if (isAdd && getLeftTreeActiveInfo) state.formData = { ...(getLeftTreeActiveInfo() || {}) };
|
||||
state.formConf = cloneDeep(state.defaultFormConf);
|
||||
fillFormData(state.formConf, state.formData, isAdd);
|
||||
nextTick(() => {
|
||||
state.key = +new Date();
|
||||
state.loading = false;
|
||||
state.prevBtnLoading = false;
|
||||
state.nextBtnLoading = false;
|
||||
changeLoading(false);
|
||||
});
|
||||
}
|
||||
function getFormOperates() {
|
||||
if (state.config.isPreview || state.config.isDataManage || !state.config.useFormPermission) return;
|
||||
const permissionList = userStore.getPermissionList;
|
||||
const modelId = state.config.menuId;
|
||||
const list = permissionList.filter(o => o.modelId === modelId);
|
||||
state.formOperates = list[0] && list[0].form ? list[0].form : [];
|
||||
}
|
||||
function submitForm(data, callback) {
|
||||
if (!data) return;
|
||||
setFormProps({ confirmLoading: true });
|
||||
const formData = { ...state.formData, ...data };
|
||||
state.dataForm.data = JSON.stringify(formData);
|
||||
const formMethod = state.dataForm.id ? updateModel : createModel;
|
||||
formMethod(state.config.modelId, { ...state.dataForm, menuId: state.config.menuId })
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
setFormProps({ confirmLoading: false });
|
||||
if (state.submitType == 1) {
|
||||
initData();
|
||||
state.isContinue = true;
|
||||
} else {
|
||||
setFormProps({ open: false });
|
||||
emit('reload');
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
setFormProps({ confirmLoading: false });
|
||||
});
|
||||
}
|
||||
function handleReset() {
|
||||
getParser().handleReset();
|
||||
}
|
||||
function handleSubmit() {
|
||||
if (state.config.isPreview) return createMessage.warning('功能预览不支持数据保存');
|
||||
getParser().handleSubmit();
|
||||
}
|
||||
function handlePrev() {
|
||||
state.currIndex--;
|
||||
// state.prevBtnLoading = true;
|
||||
handleGetNewInfo();
|
||||
}
|
||||
function handleNext() {
|
||||
state.currIndex++;
|
||||
// state.nextBtnLoading = true;
|
||||
handleGetNewInfo();
|
||||
}
|
||||
function handleGetNewInfo() {
|
||||
changeLoading(true);
|
||||
state.loading = true;
|
||||
handleReset();
|
||||
state.config.id = state.allList[state.currIndex].id;
|
||||
getInfo(state.config.id);
|
||||
}
|
||||
function getParser() {
|
||||
const parser = unref(parserRef);
|
||||
if (!parser) throw new Error('parser is null!');
|
||||
return parser;
|
||||
}
|
||||
function openForm() {
|
||||
if (state.formConf.popupType === 'fullScreen') return openPopup();
|
||||
if (state.formConf.popupType === 'drawer') return openDrawer();
|
||||
openModal();
|
||||
}
|
||||
function setFormProps(data) {
|
||||
if (state.formConf.popupType === 'fullScreen') return setPopupProps(data);
|
||||
if (state.formConf.popupType === 'drawer') return setDrawerProps(data);
|
||||
setModalProps(data);
|
||||
}
|
||||
function changeLoading(loading) {
|
||||
setFormProps({ loading });
|
||||
}
|
||||
async function onClose() {
|
||||
if (state.isContinue) emit('reload');
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
128
src/views/common/dynamicModel/list/components/ViewList.vue
Normal file
128
src/views/common/dynamicModel/list/components/ViewList.vue
Normal file
@@ -0,0 +1,128 @@
|
||||
<template>
|
||||
<Tooltip placement="top">
|
||||
<template #title>
|
||||
<span>{{ t('component.table.viewList') }}</span>
|
||||
</template>
|
||||
<Popover v-model:open="visible" placement="bottomRight" trigger="click" overlayClassName="yunzhupaas-view-list-popover">
|
||||
<template #content>
|
||||
<div class="content">
|
||||
<div v-for="item in viewList" :key="item.id" class="item" @click="handleClick(item)">
|
||||
<span class="item-name">{{ item.fullName }}</span>
|
||||
<div class="item-delete">
|
||||
<i class="icon-ym icon-ym-app-delete" @click.stop="handleDel(item.id)" v-if="item.type !== 0" />
|
||||
</div>
|
||||
<div class="item-default">
|
||||
<PushpinOutlined :class="{ 'item-default-icon': item.status == 0 }" @click.stop="handleDefault(item)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<menu-outlined />
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import type { PropType } from 'vue';
|
||||
import { reactive, toRefs } from 'vue';
|
||||
import { Tooltip, Popover } from 'ant-design-vue';
|
||||
import { MenuOutlined, PushpinOutlined } from '@ant-design/icons-vue';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { delView, setDefaultView } from '@/api/onlineDev/visualDev';
|
||||
|
||||
interface State {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const state = reactive<State>({
|
||||
visible: false,
|
||||
});
|
||||
const { visible } = toRefs(state);
|
||||
const emit = defineEmits(['itemClick', 'reload']);
|
||||
const { t } = useI18n();
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const props = defineProps({
|
||||
menuId: { type: String },
|
||||
viewList: { type: Array as PropType<any[]>, default: () => [] },
|
||||
});
|
||||
|
||||
function handleClick(item) {
|
||||
emit('itemClick', item);
|
||||
}
|
||||
function handleDel(id) {
|
||||
state.visible = false;
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '确定要删除此视图,是否继续?',
|
||||
onOk: () => {
|
||||
delView(id, props.menuId).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
emit('reload');
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function handleDefault(item) {
|
||||
if (item.status === 1) return;
|
||||
state.visible = false;
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: '设置此视图为默认视图,是否继续?',
|
||||
onOk: () => {
|
||||
setDefaultView(item.id, props.menuId).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
emit('reload');
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.yunzhupaas-view-list-popover {
|
||||
.ant-popover-inner-content {
|
||||
padding: 0;
|
||||
.content {
|
||||
width: 230px;
|
||||
max-height: 250px;
|
||||
overflow: auto;
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 4px 6px;
|
||||
height: 36px;
|
||||
padding: 0 8px 0 18px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: @selected-hover-bg;
|
||||
.item-delete i {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
.item-name {
|
||||
flex: 1;
|
||||
}
|
||||
.item-delete {
|
||||
width: 14px;
|
||||
i {
|
||||
display: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.item-default {
|
||||
margin-left: 12px;
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.item-default-icon {
|
||||
color: @primary-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
213
src/views/common/dynamicModel/list/components/ViewSetting.vue
Normal file
213
src/views/common/dynamicModel/list/components/ViewSetting.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<Tooltip placement="topRight">
|
||||
<template #title>
|
||||
<span>{{ t('component.table.viewSetting') }}</span>
|
||||
</template>
|
||||
<setting-outlined @click="openDrawer" />
|
||||
</Tooltip>
|
||||
<a-drawer width="340px" v-model:open="visible" class="common-container-drawer">
|
||||
<template #title>
|
||||
<a-input v-model:value="dataForm.fullName" :maxlength="6" class="w-130px" />
|
||||
</template>
|
||||
<div class="common-container-drawer-body column-setting-body">
|
||||
<a-tabs v-model:activeKey="activeKey" :tabBarGutter="11" class="average-tabs" @change="onTabChange">
|
||||
<a-tab-pane :key="0" tab="查询字段" v-if="dataForm.searchList?.length"> </a-tab-pane>
|
||||
<a-tab-pane :key="1" tab="列表字段"> </a-tab-pane>
|
||||
</a-tabs>
|
||||
<div class="flex-1 overflow-auto">
|
||||
<CheckboxGroup v-model:value="searchCheckedList" ref="searchListRef" class="check-contain" v-if="activeKey === 0">
|
||||
<div v-for="item in dataForm.searchList" :key="item.id" class="check-item">
|
||||
<DragOutlined class="table-search-drag-icon" />
|
||||
<Checkbox :value="item.id">
|
||||
<div :title="item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label">
|
||||
{{ item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label }}
|
||||
</div>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</CheckboxGroup>
|
||||
<CheckboxGroup v-model:value="columnCheckedList" ref="columnListRef" class="check-contain" v-else>
|
||||
<div v-for="item in dataForm.columnList" :key="item.id" class="check-item">
|
||||
<DragOutlined class="table-column-drag-icon" />
|
||||
<Checkbox :value="item.id">
|
||||
<div :title="item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label">
|
||||
{{ item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label }}
|
||||
</div>
|
||||
</Checkbox>
|
||||
</div>
|
||||
</CheckboxGroup>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-60px leading-60px yunzhupaas-basic-drawer-footer">
|
||||
<a-button class="mr-10px" :loading="showAddLoading" @click="addOrUpdateHandle('')">{{ t('common.addView') }}</a-button>
|
||||
<a-button class="mr-10px" :loading="showUpdateLoading" @click="addOrUpdateHandle(dataForm.id)" type="primary" v-if="currentView.type !== 0">
|
||||
{{ t('common.updateView') }}
|
||||
</a-button>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, toRefs, nextTick, unref, ref } from 'vue';
|
||||
import { Tooltip, Checkbox, CheckboxGroup, Drawer as ADrawer } from 'ant-design-vue';
|
||||
import { SettingOutlined, DragOutlined } from '@ant-design/icons-vue';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { isNullAndUnDef } from '@/utils/is';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import Sortablejs from 'sortablejs';
|
||||
import { createView, updateView } from '@/api/onlineDev/visualDev';
|
||||
|
||||
interface State {
|
||||
dataForm: any;
|
||||
visible: boolean;
|
||||
activeKey: number;
|
||||
searchCheckedList: string[];
|
||||
columnCheckedList: string[];
|
||||
showAddLoading: boolean;
|
||||
showUpdateLoading: boolean;
|
||||
}
|
||||
|
||||
const state = reactive<State>({
|
||||
dataForm: {
|
||||
viewName: '',
|
||||
searchList: [],
|
||||
columnList: [],
|
||||
},
|
||||
visible: false,
|
||||
activeKey: 0,
|
||||
searchCheckedList: [],
|
||||
columnCheckedList: [],
|
||||
showAddLoading: false,
|
||||
showUpdateLoading: false,
|
||||
});
|
||||
const { dataForm, visible, activeKey, searchCheckedList, columnCheckedList, showAddLoading, showUpdateLoading } = toRefs(state);
|
||||
const emit = defineEmits(['reload']);
|
||||
const { t } = useI18n();
|
||||
const { createMessage } = useMessage();
|
||||
const searchListRef = ref(null);
|
||||
const columnListRef = ref(null);
|
||||
const props = defineProps({
|
||||
menuId: { type: String, default: '' },
|
||||
currentView: { type: Object, default: {} },
|
||||
viewList: { type: Array as PropType<any[]>, default: [] },
|
||||
});
|
||||
|
||||
function openDrawer() {
|
||||
state.visible = true;
|
||||
state.showUpdateLoading = false;
|
||||
state.showAddLoading = false;
|
||||
state.dataForm = cloneDeep(props.currentView);
|
||||
initData();
|
||||
state.activeKey = state.dataForm.searchList?.length ? 0 : 1;
|
||||
initSortable();
|
||||
initCheckedList();
|
||||
}
|
||||
function initData() {
|
||||
const defaultData = props.viewList.filter(o => o.type == 0)[0] || {};
|
||||
state.dataForm.searchList = mergeList(state.dataForm.searchList, defaultData.searchList);
|
||||
state.dataForm.columnList = mergeList(state.dataForm.columnList, defaultData.columnList);
|
||||
}
|
||||
function mergeList(array1: any = [], array2: any = []) {
|
||||
array2 = array2.map(o => ({ ...o, show: false }));
|
||||
return [...array1.filter(o => array2.some(i => i.id === o.id)), ...array2.filter(o => !array1.some(i => i.id === o.id))];
|
||||
}
|
||||
function initCheckedList() {
|
||||
state.searchCheckedList = state.dataForm.searchList.filter(o => o.show).map(o => o.id);
|
||||
state.columnCheckedList = state.dataForm.columnList.filter(o => o.show).map(o => o.id);
|
||||
}
|
||||
function onTabChange() {
|
||||
initSortable();
|
||||
}
|
||||
function initSortable() {
|
||||
nextTick(() => {
|
||||
const elRef = state.activeKey == 0 ? unref(searchListRef) : unref(columnListRef);
|
||||
if (!elRef) return;
|
||||
const el = (elRef as any).$el;
|
||||
if (!el) return;
|
||||
Sortablejs.create(unref(el), {
|
||||
animation: 500,
|
||||
delay: 400,
|
||||
delayOnTouchOnly: true,
|
||||
handle: state.activeKey == 0 ? '.table-search-drag-icon' : '.table-column-drag-icon',
|
||||
onEnd: evt => {
|
||||
const { oldIndex, newIndex } = evt;
|
||||
if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) return;
|
||||
const list = state.activeKey == 0 ? state.dataForm.searchList : state.dataForm.columnList;
|
||||
if (oldIndex > newIndex) {
|
||||
list.splice(newIndex, 0, list[oldIndex]);
|
||||
list.splice(oldIndex + 1, 1);
|
||||
} else {
|
||||
list.splice(newIndex + 1, 0, list[oldIndex]);
|
||||
list.splice(oldIndex, 1);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
function getRealList(list, checkedList) {
|
||||
return list.map(o => {
|
||||
const show = checkedList.findIndex(c => c == o.id) !== -1;
|
||||
return { ...o, show: show };
|
||||
});
|
||||
}
|
||||
function addOrUpdateHandle(id) {
|
||||
state.dataForm.searchList = getRealList(state.dataForm.searchList, state.searchCheckedList);
|
||||
state.dataForm.columnList = getRealList(state.dataForm.columnList, state.columnCheckedList);
|
||||
const methods = id ? updateView : createView;
|
||||
const loading = id ? 'showUpdateLoading' : 'showAddLoading';
|
||||
const query = {
|
||||
...state.dataForm,
|
||||
searchList: JSON.stringify(state.dataForm.searchList),
|
||||
columnList: JSON.stringify(state.dataForm.columnList),
|
||||
id,
|
||||
menuId: props.menuId,
|
||||
};
|
||||
state[loading] = true;
|
||||
methods(query)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
state[loading] = false;
|
||||
state.visible = false;
|
||||
emit('reload', state.dataForm.id);
|
||||
})
|
||||
.catch(() => {
|
||||
state[loading] = false;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<style lang="less">
|
||||
.column-setting-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
.check-contain {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
.check-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 100%;
|
||||
padding: 4px 0 8px 0;
|
||||
.table-search-drag-icon,
|
||||
.table-column-drag-icon {
|
||||
margin: 0 5px;
|
||||
cursor: move;
|
||||
}
|
||||
.ant-checkbox-wrapper {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
span:last-child {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
div {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
669
src/views/common/dynamicModel/list/detail/ExtraList.vue
Normal file
669
src/views/common/dynamicModel/list/detail/ExtraList.vue
Normal file
@@ -0,0 +1,669 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper">
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-search-box" v-if="getSearchList?.length">
|
||||
<BasicForm
|
||||
@register="registerSearchForm"
|
||||
:schemas="getSearchList"
|
||||
@advanced-change="redoHeight"
|
||||
@submit="handleSearchSubmit"
|
||||
@reset="handleSearchReset"
|
||||
class="search-form">
|
||||
</BasicForm>
|
||||
</div>
|
||||
<div class="yunzhupaas-content-wrapper-content bg-white">
|
||||
<BasicTable @register="registerTable" v-bind="getTableBindValue" ref="tableRef" @columns-change="handleColumnChange">
|
||||
<template #tableTitle>
|
||||
<a-button
|
||||
v-for="item in state.headerBtnsList"
|
||||
:key="item.value"
|
||||
:type="item.value === 'add' ? 'primary' : 'link'"
|
||||
:preIcon="item.icon"
|
||||
@click="headBtnsHandle(item.value)">
|
||||
{{ item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label }}
|
||||
</a-button>
|
||||
</template>
|
||||
<template #expandedRowRender="{ record }" v-if="[1, 2].includes(columnData.type) && getChildTableStyle === 2 && childColumnList.length">
|
||||
<a-tabs size="small">
|
||||
<a-tab-pane :key="cIndex" :tab="child.label" :label="child.label" v-for="(child, cIndex) in childColumnList">
|
||||
<BasicTable @register="registerChildTable" :ellipsis="!!columnData.showOverflow" :data-source="record[child.prop]" :columns="child.children">
|
||||
<template #bodyCell="{ column = {}, record: childRecord }">
|
||||
<template v-if="column.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text" @click="toDetail(column.modelId, childRecord[`${column.dataIndex}_id`], childRecord[column.propsValue])">
|
||||
{{ childRecord[column.dataIndex] }}
|
||||
</p>
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number
|
||||
v-model:value="childRecord[column.dataIndex]"
|
||||
:precision="column.precision"
|
||||
:thousands="column.thousands"
|
||||
disabled
|
||||
detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
v-model:value="childRecord[column.dataIndex]"
|
||||
:isStorage="column.isStorage"
|
||||
:precision="column.precision"
|
||||
:thousands="column.thousands"
|
||||
:roundType="column.roundType"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="childRecord[column.dataIndex]" detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'signature'">
|
||||
<yunzhupaas-signature v-model:value="childRecord[column.dataIndex]" detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="childRecord[column.dataIndex]" :count="column.count" :allowHalf="column.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="childRecord[column.dataIndex]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="childRecord[column.dataIndex]" disabled detailed simple v-if="childRecord[column.dataIndex]?.length" />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="childRecord[column.dataIndex]" disabled detailed simple v-if="childRecord[column.dataIndex]?.length" />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="childRecord[column.dataIndex]"
|
||||
:useMask="column.useMask"
|
||||
:maskConfig="column.maskConfig"
|
||||
:showOverflow="columnData.showOverflow"
|
||||
detailed />
|
||||
</template>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
<template #bodyCell="{ column = {}, record, index }">
|
||||
<template v-if="column.flag === 'INDEX'">
|
||||
<span>{{ index + 1 }}</span>
|
||||
</template>
|
||||
<template v-for="(item, index) in childColumnList" v-if="getChildTableStyle !== 2 && childColumnList.length">
|
||||
<template v-if="column.id?.includes('-') && item.children && item.children[0] && column.id === item.children[0]?.id">
|
||||
<ChildTableColumn
|
||||
:data="record[item.prop]"
|
||||
:head="item.children"
|
||||
@toggleExpand="toggleExpand(record, `${item.prop}Expand`)"
|
||||
@toDetail="toDetail"
|
||||
:expand="record[`${item.prop}Expand`]"
|
||||
:showOverflow="columnData.showOverflow"
|
||||
:key="index" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="!(record.top || column.id?.includes('-'))">
|
||||
<template v-if="column.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text" @click="toDetail(column.modelId, record[`${column.prop}_id`], column.propsValue)">{{ record[column.prop] }}</p>
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number v-model:value="record[column.prop]" :precision="column.precision" :thousands="column.thousands" disabled detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
v-model:value="record[column.prop]"
|
||||
:isStorage="column.isStorage"
|
||||
:precision="column.precision"
|
||||
:thousands="column.thousands"
|
||||
:roundType="column.roundType"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="record[column.prop]" detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'signature'">
|
||||
<yunzhupaas-signature v-model:value="record[column.prop]" detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="record[column.prop]" :count="column.count" :allowHalf="column.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="record[column.prop]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="record[column.prop]" disabled detailed simple v-if="record[column.prop]?.length" />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="record[column.prop]" disabled detailed simple v-if="record[column.prop]?.length" />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="record[column.prop]"
|
||||
:useMask="column.useMask"
|
||||
:maskConfig="column.maskConfig"
|
||||
:showOverflow="columnData.showOverflow"
|
||||
detailed />
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.key === 'action' && (!record.top || columnData.type == 5)">
|
||||
<TableAction :actions="getTableActions(record, index)" />
|
||||
</template>
|
||||
</template>
|
||||
<template #summary v-if="columnData.showSummary && [1, 2, 4].includes(columnData.type)">
|
||||
<a-table-summary fixed>
|
||||
<a-table-summary-row>
|
||||
<a-table-summary-cell :index="0">{{ t('component.table.summary') }}</a-table-summary-cell>
|
||||
<a-table-summary-cell v-for="(item, index) in getColumnSum" :key="index" :index="index + 1" :align="getSummaryCellAlign(index)">
|
||||
{{ item }}
|
||||
</a-table-summary-cell>
|
||||
<a-table-summary-cell :index="getColumnSum.length + 1"></a-table-summary-cell>
|
||||
</a-table-summary-row>
|
||||
</a-table-summary>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
<Form ref="formRef" @reload="reload" />
|
||||
<Detail ref="detailRef" />
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getModelList, batchDelete, getConfigData } from '@/api/onlineDev/visualDev';
|
||||
import { getDataInterfaceRes } from '@/api/systemData/dataInterface';
|
||||
import { ref, reactive, onMounted, toRefs, computed, unref, nextTick } from 'vue';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useBaseStore } from '@/store/modules/base';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { BasicTable, useTable, TableAction, ActionItem, TableActionType, SorterResult } from '@/components/Table';
|
||||
import Form from '../Form.vue';
|
||||
import Detail from '../detail/index.vue';
|
||||
import ChildTableColumn from '../ChildTableColumn.vue';
|
||||
import { getScriptFunc, onlineUtils, thousandsFormat } from '@/utils/yunzhupaas';
|
||||
import { getSearchFormSchemas } from '@/components/FormGenerator/src/helper/transform';
|
||||
import { dyOptionsList } from '@/components/FormGenerator/src/helper/config';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { usePermission } from '@/hooks/web/usePermission';
|
||||
|
||||
interface State {
|
||||
config: any;
|
||||
columnData: any;
|
||||
formConf: any;
|
||||
headerBtnsList: any[];
|
||||
columnBtnsList: any[];
|
||||
columnOptions: any[];
|
||||
columns: any[];
|
||||
childColumnList: any[];
|
||||
cacheList: any[];
|
||||
columnSettingList: any[];
|
||||
searchSchemas: any[];
|
||||
customRow: any;
|
||||
tabActiveKey: any;
|
||||
tabList: any[];
|
||||
}
|
||||
|
||||
const props = defineProps(['config', 'detailFormData', 'isPreview', 'isDataManage']);
|
||||
const emit = defineEmits(['openDetail', 'openForm']);
|
||||
const { hasBtnP } = usePermission();
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const userStore = useUserStore();
|
||||
const baseStore = useBaseStore();
|
||||
const formRef = ref<any>(null);
|
||||
const tableRef = ref<Nullable<TableActionType>>(null);
|
||||
const detailRef = ref<any>(null);
|
||||
const searchInfo = reactive({
|
||||
modelId: '',
|
||||
menuId: '',
|
||||
queryJson: '',
|
||||
superQueryJson: '',
|
||||
extraQueryJson: '',
|
||||
});
|
||||
const state = reactive<State>({
|
||||
config: {},
|
||||
columnData: {},
|
||||
formConf: {},
|
||||
headerBtnsList: [],
|
||||
columnBtnsList: [],
|
||||
columnOptions: [],
|
||||
columns: [],
|
||||
childColumnList: [],
|
||||
cacheList: [],
|
||||
columnSettingList: [],
|
||||
searchSchemas: [],
|
||||
customRow: null,
|
||||
tabActiveKey: '',
|
||||
tabList: [],
|
||||
});
|
||||
const { columnData, childColumnList } = toRefs(state);
|
||||
const [registerSearchForm, { updateSchema, submit: searchFormSubmit }] = useForm({
|
||||
baseColProps: { span: 6 },
|
||||
showActionButtonGroup: true,
|
||||
showAdvancedButton: true,
|
||||
compact: true,
|
||||
});
|
||||
const [registerTable, { reload, setLoading, clearSelectedRowKeys, redoHeight }] = useTable({
|
||||
api: getModelList,
|
||||
immediate: false,
|
||||
clickToRowSelect: false,
|
||||
tableSetting: { setting: false, redo: !props.isPreview },
|
||||
afterFetch: data => {
|
||||
state.cacheList = cloneDeep(data);
|
||||
return data;
|
||||
},
|
||||
});
|
||||
const [registerChildTable] = useTable({
|
||||
pagination: false,
|
||||
canResize: false,
|
||||
showTableSetting: false,
|
||||
});
|
||||
defineExpose({ reload });
|
||||
|
||||
const getPagination = computed(() => {
|
||||
if ([3, 5].includes(state.columnData.type) || !state.columnData.hasPage) return false;
|
||||
return { pageSize: state.columnData.pageSize };
|
||||
});
|
||||
const getChildTableStyle = computed(() => (state.columnData.type == 3 || state.columnData.type == 5 ? 1 : state.columnData.childTableStyle));
|
||||
const getColumns = computed(() => state.columns);
|
||||
const getSearchList = computed(() => {
|
||||
return cloneDeep(state.searchSchemas).map(o => ({ ...o, show: true }));
|
||||
});
|
||||
const getRowKey = computed(() => (state.config.webType == 4 && state.columnData.viewKey ? state.columnData.viewKey : 'id'));
|
||||
const getTableBindValue = computed(() => {
|
||||
let columns = unref(getColumns);
|
||||
const defaultSortConfig = (state.columnData.defaultSortConfig || []).map(o => (o.sort === 'desc' ? '-' : '') + o.field);
|
||||
const data: any = {
|
||||
pagination: unref(getPagination),
|
||||
searchInfo: unref(searchInfo),
|
||||
defSort: { sidx: defaultSortConfig.join(',') },
|
||||
sortFn: (sortInfo: SorterResult | SorterResult[]) => {
|
||||
if (Array.isArray(sortInfo)) {
|
||||
const sortList = sortInfo.map(o => (o.order === 'descend' ? '-' : '') + o.field);
|
||||
return { sidx: sortList.join(',') };
|
||||
} else {
|
||||
const { field, order } = sortInfo;
|
||||
if (field && order) {
|
||||
// 排序字段
|
||||
return { sidx: (order === 'descend' ? '-' : '') + field };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
columns,
|
||||
clearSelectOnPageChange: true,
|
||||
bordered: (unref(getChildTableStyle) != 2 && !!state.childColumnList?.length) || !!state.columnData.complexHeaderList?.length,
|
||||
rowKey: unref(getRowKey),
|
||||
};
|
||||
if (state.columnBtnsList.length) {
|
||||
let columnBtnsLen = state.columnBtnsList.length;
|
||||
data.actionColumn = {
|
||||
width: columnBtnsLen * 50,
|
||||
title: t('component.table.action'),
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
};
|
||||
}
|
||||
if (state.customRow) data.customRow = state.customRow;
|
||||
return data;
|
||||
});
|
||||
const getSummaryColumn = computed(() => {
|
||||
let defaultColumns = unref(getColumns);
|
||||
// 处理列固定
|
||||
if (state.columnSettingList?.length) {
|
||||
for (let i = 0; i < defaultColumns.length; i++) {
|
||||
inner: for (let j = 0; j < state.columnSettingList.length; j++) {
|
||||
if (defaultColumns[i].dataIndex === state.columnSettingList[j].dataIndex) {
|
||||
defaultColumns[i].fixed = state.columnSettingList[j].fixed;
|
||||
defaultColumns[i].visible = state.columnSettingList[j].visible;
|
||||
break inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultColumns = defaultColumns.filter(o => o.visible);
|
||||
}
|
||||
let columns: any[] = [];
|
||||
for (let i = 0; i < defaultColumns.length; i++) {
|
||||
const e = defaultColumns[i];
|
||||
if (e.yunzhupaasKey === 'table' || e.yunzhupaasKey === 'complexHeader') {
|
||||
if (e.children?.length) columns.push(...e.children);
|
||||
} else {
|
||||
columns.push(e);
|
||||
}
|
||||
if (e.fixed && e.children?.length) {
|
||||
for (let j = 0; j < e.children.length; j++) {
|
||||
e.children[j].fixed = e.fixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
const leftFixedList = columns.filter(o => o.fixed === 'left');
|
||||
const rightFixedList = columns.filter(o => o.fixed === 'right');
|
||||
const noFixedList = columns.filter(o => o.fixed !== 'left' && o.fixed !== 'right');
|
||||
return [...leftFixedList, ...noFixedList, ...rightFixedList];
|
||||
});
|
||||
// 列表合计
|
||||
const getColumnSum = computed(() => {
|
||||
const sums: any[] = [];
|
||||
const isSummary = key => state.columnData.summaryField.includes(key);
|
||||
const useThousands = key => unref(getSummaryColumn).some(o => o.__vModel__ === key && o.thousands);
|
||||
unref(getSummaryColumn).forEach((column, index) => {
|
||||
let sumVal = state.cacheList.reduce((sum, d) => sum + getCmpValOfRow(d, column.prop), 0);
|
||||
if (!isSummary(column.prop)) sumVal = '';
|
||||
sumVal = Number.isNaN(sumVal) ? '' : sumVal;
|
||||
const realVal = sumVal && !Number.isInteger(sumVal) ? Number(sumVal).toFixed(2) : sumVal;
|
||||
sums[index] = useThousands(column.prop) ? thousandsFormat(realVal) : realVal;
|
||||
});
|
||||
if ([1, 2].includes(state.columnData.type) && unref(getChildTableStyle) === 2 && state.childColumnList.length) sums.unshift('');
|
||||
return sums;
|
||||
});
|
||||
function getCmpValOfRow(row, key) {
|
||||
const isSummary = key => state.columnData.summaryField.includes(key);
|
||||
if (!state.columnData.summaryField.length || !isSummary(key)) return 0;
|
||||
const target = row[key];
|
||||
if (!target) return 0;
|
||||
const data = isNaN(target) ? 0 : Number(target);
|
||||
return data;
|
||||
}
|
||||
function getSummaryCellAlign(index) {
|
||||
if (!unref(getSummaryColumn).length) return;
|
||||
return unref(getSummaryColumn)[index]?.align || 'left';
|
||||
}
|
||||
function getTableActions(record, index): ActionItem[] {
|
||||
const list = state.columnBtnsList.map(o => {
|
||||
const item: ActionItem = {
|
||||
label: o.labelI18nCode ? t(o.labelI18nCode, o.label) : o.label,
|
||||
onClick: columnBtnsHandle.bind(null, o.value, record),
|
||||
};
|
||||
if (o.value === 'remove') item.color = 'error';
|
||||
if (state.config.enableFlow) {
|
||||
if (o.value === 'edit') item.disabled = ![0, 8, 9].includes(record.flowState);
|
||||
if (o.value === 'remove') item.disabled = ![0, 9].includes(record.flowState);
|
||||
if (o.value === 'detail') item.disabled = !record.flowState;
|
||||
} else {
|
||||
if (o?.event?.enableFunc) {
|
||||
const parameter = { row: record, rowIndex: index, onlineUtils };
|
||||
const func: any = getScriptFunc(o.event.enableFunc);
|
||||
item.disabled = (func && !func(parameter)) || false;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
});
|
||||
return list;
|
||||
}
|
||||
function addHandle() {
|
||||
const data = {
|
||||
id: '',
|
||||
formConf: transferExtraList(state.formConf),
|
||||
modelId: searchInfo.modelId,
|
||||
isPreview: props.isPreview,
|
||||
useFormPermission: state.columnData.useFormPermission,
|
||||
showMoreBtn: ![3, 5].includes(state.columnData.type),
|
||||
menuId: searchInfo.menuId,
|
||||
allList: state.cacheList,
|
||||
};
|
||||
emit('openForm', data);
|
||||
}
|
||||
// 顶部按钮点击事件
|
||||
function headBtnsHandle(key) {
|
||||
if (key === 'add') return addHandle();
|
||||
}
|
||||
// 行按钮点击事件
|
||||
function columnBtnsHandle(key, record) {
|
||||
if (key === 'edit') return updateHandle(record);
|
||||
if (key === 'detail') return goDetail(record);
|
||||
if (key == 'remove') handleDelete(record.id);
|
||||
}
|
||||
// 编辑
|
||||
function updateHandle(record) {
|
||||
const data = {
|
||||
id: record.id,
|
||||
formConf: state.formConf,
|
||||
modelId: searchInfo.modelId,
|
||||
isPreview: props.isPreview,
|
||||
useFormPermission: state.columnData.useFormPermission,
|
||||
showMoreBtn: ![3, 5].includes(state.columnData.type),
|
||||
menuId: searchInfo.menuId,
|
||||
allList: state.cacheList,
|
||||
};
|
||||
emit('openForm', data);
|
||||
}
|
||||
// 查看详情
|
||||
function goDetail(record) {
|
||||
const data = {
|
||||
id: record.id,
|
||||
formConf: state.formConf,
|
||||
modelId: searchInfo.modelId,
|
||||
menuId: searchInfo.menuId,
|
||||
useFormPermission: state.columnData.useFormPermission,
|
||||
title: state.config.fullName,
|
||||
hideExtra: true,
|
||||
};
|
||||
emit('openDetail', data);
|
||||
}
|
||||
function handleDelete(id) {
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: t('common.delTip'),
|
||||
onOk: () => {
|
||||
const query = { ids: [id], flowId: state.config.flowId || '' };
|
||||
batchDelete(searchInfo.modelId, query).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
reload();
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
function init() {
|
||||
state.config = {
|
||||
modelId: searchInfo.modelId,
|
||||
isPreview: props.isPreview,
|
||||
...props.config.extraConfig,
|
||||
};
|
||||
searchInfo.modelId = state.config.id;
|
||||
searchInfo.menuId = props.config.targetFormId;
|
||||
const obj = {};
|
||||
obj[props.config.targetField] = props.detailFormData[props.config.currentField + '_yunzhupaasId'];
|
||||
searchInfo.extraQueryJson = JSON.stringify(obj) === '{}' ? '' : JSON.stringify(obj);
|
||||
if (!state.config.columnData || (state.config.webType != '4' && !state.config.formData)) return;
|
||||
state.columnData = JSON.parse(state.config.columnData);
|
||||
state.formConf = state.config.formData ? JSON.parse(state.config.formData) : {};
|
||||
const columnBtnsList = state.columnData.columnBtnsList || [];
|
||||
getHeaderBtnsList(state.columnData.btnsList.filter(o => o.value === 'add') || []);
|
||||
getColumnBtnsList(columnBtnsList);
|
||||
state.columnOptions = state.columnData.columnOptions || [];
|
||||
if (!unref(getPagination)) (searchInfo as any).pageSize = 1000000;
|
||||
setLoading(true);
|
||||
getSearchSchemas();
|
||||
getColumnList();
|
||||
if (props.isPreview) return setLoading(false);
|
||||
nextTick(() => {
|
||||
unref(getSearchList)?.length ? searchFormSubmit() : reload({ page: 1 });
|
||||
});
|
||||
}
|
||||
function getHeaderBtnsList(btnsList) {
|
||||
btnsList = btnsList.filter(o => o.show || !Reflect.has(o, 'show'));
|
||||
if (props.isPreview || props.isDataManage || !state.columnData.useBtnPermission) return (state.headerBtnsList = btnsList);
|
||||
// 过滤权限
|
||||
let btns: any[] = [];
|
||||
for (let i = 0; i < btnsList.length; i++) {
|
||||
if (hasBtnP('btn_' + btnsList[i].value, true, searchInfo.menuId)) btns.push(btnsList[i]);
|
||||
}
|
||||
state.headerBtnsList = btns;
|
||||
}
|
||||
function getColumnBtnsList(columnBtnsList) {
|
||||
columnBtnsList = columnBtnsList.filter(o => o.show || !Reflect.has(o, 'show'));
|
||||
let btns: any[] = [];
|
||||
if (props.isPreview || props.isDataManage || !state.columnData.useBtnPermission) {
|
||||
btns = columnBtnsList;
|
||||
} else {
|
||||
// 过滤权限
|
||||
const permissionList = userStore.getPermissionList;
|
||||
const list = permissionList.filter(o => o.modelId === searchInfo.menuId);
|
||||
const perBtnList = list[0] && list[0].button ? list[0].button : [];
|
||||
for (let i = 0; i < columnBtnsList.length; i++) {
|
||||
inner: for (let j = 0; j < perBtnList.length; j++) {
|
||||
if ('btn_' + columnBtnsList[i].value === perBtnList[j].enCode) {
|
||||
btns.push(columnBtnsList[i]);
|
||||
break inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state.columnBtnsList = btns;
|
||||
}
|
||||
function getSearchSchemas() {
|
||||
const schemas = getSearchFormSchemas(state.columnData.searchList);
|
||||
schemas.forEach(cur => {
|
||||
const config = cur.__config__;
|
||||
if (dyOptionsList.includes(config.yunzhupaasKey)) {
|
||||
if (config.dataType === 'dictionary' && config.dictionaryType) {
|
||||
baseStore.getDicDataSelector(config.dictionaryType).then(res => {
|
||||
updateSchema([{ field: cur.field, componentProps: { options: res } }]);
|
||||
});
|
||||
}
|
||||
if (config.dataType === 'dynamic' && config.propsUrl) {
|
||||
const query = { paramList: config.templateJson || [] };
|
||||
getDataInterfaceRes(config.propsUrl, query).then(res => {
|
||||
const data = Array.isArray(res.data) ? res.data : [];
|
||||
updateSchema([{ field: cur.field, componentProps: { options: data } }]);
|
||||
});
|
||||
}
|
||||
}
|
||||
if ((Array.isArray(cur.value) && cur.value.length) || cur.value || cur.value === 0 || cur.value === false) cur.defaultValue = cur.value;
|
||||
});
|
||||
state.searchSchemas = schemas;
|
||||
}
|
||||
// 获取列
|
||||
function getColumnList() {
|
||||
let columnList: any[] = [];
|
||||
if (props.isPreview || props.isDataManage || !state.columnData.useColumnPermission) {
|
||||
columnList = state.columnData.columnList;
|
||||
} else {
|
||||
// 过滤权限
|
||||
const permissionList = userStore.getPermissionList;
|
||||
const list = permissionList.filter(o => o.modelId === searchInfo.menuId);
|
||||
const perColumnList = list[0] && list[0].column ? list[0].column : [];
|
||||
for (let i = 0; i < state.columnData.columnList.length; i++) {
|
||||
inner: for (let j = 0; j < perColumnList.length; j++) {
|
||||
if (state.columnData.columnList[i].prop === perColumnList[j].enCode) {
|
||||
columnList.push(state.columnData.columnList[i]);
|
||||
break inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let columns = columnList.map(o => ({
|
||||
...o,
|
||||
placeholder: state.columnData.type == 4 && o.placeholderI18nCode ? t(o.placeholderI18nCode, o.placeholder) : o.placeholder,
|
||||
title: o.labelI18nCode ? t(o.labelI18nCode, o.label) : o.label,
|
||||
dataIndex: o.prop,
|
||||
align: o.align,
|
||||
fixed: o.fixed == 'none' ? false : o.fixed,
|
||||
sorter: o.sortable ? { multiple: 1 } : o.sortable,
|
||||
width: o.width || 100,
|
||||
}));
|
||||
if (state.columnData.type !== 3 && state.columnData.type !== 5) columns = getComplexColumns(columns);
|
||||
state.columns = columns.filter(o => o.prop.indexOf('-') < 0);
|
||||
}
|
||||
function getComplexColumns(columns) {
|
||||
let complexHeaderList: any[] = state.columnData.complexHeaderList || [];
|
||||
if (!complexHeaderList.length) return columns;
|
||||
let childColumns: any[] = [];
|
||||
let firstChildColumns: string[] = [];
|
||||
for (let i = 0; i < complexHeaderList.length; i++) {
|
||||
const e = complexHeaderList[i];
|
||||
e.label = e.fullName;
|
||||
e.labelI18nCode = e.fullNameI18nCode;
|
||||
e.title = e.fullNameI18nCode ? t(e.fullNameI18nCode, e.fullName) : e.fullName;
|
||||
e.align = e.align;
|
||||
e.dataIndex = e.id;
|
||||
e.prop = e.id;
|
||||
e.children = [];
|
||||
e.yunzhupaasKey = 'complexHeader';
|
||||
if (e.childColumns?.length) {
|
||||
childColumns.push(...e.childColumns);
|
||||
for (let k = 0; k < e.childColumns.length; k++) {
|
||||
const item = e.childColumns[k];
|
||||
for (let j = 0; j < columns.length; j++) {
|
||||
const o = columns[j];
|
||||
if (o.prop == item && o.fixed !== 'left' && o.fixed !== 'right') e.children.push({ ...o });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.children.length) firstChildColumns.push(e.children[0].prop);
|
||||
}
|
||||
complexHeaderList = complexHeaderList.filter(o => o.children.length);
|
||||
let list: any[] = [];
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const e = columns[i];
|
||||
if (!childColumns.includes(e.prop) || e.fixed === 'left' || e.fixed === 'right') {
|
||||
list.push(e);
|
||||
} else {
|
||||
if (firstChildColumns.includes(e.prop)) {
|
||||
const item = complexHeaderList.find(o => o.childColumns.includes(e.prop));
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
function toggleExpand(row, field) {
|
||||
row[field] = !row[field];
|
||||
}
|
||||
// 关联表单查看详情
|
||||
function toDetail(modelId, id, propsValue) {
|
||||
if (!id) return;
|
||||
getConfigData(modelId).then(res => {
|
||||
if (!res.data || !res.data.formData) return;
|
||||
const formConf = JSON.parse(res.data.formData);
|
||||
formConf.popupType = 'general';
|
||||
formConf.customBtns = [];
|
||||
formConf.hasPrintBtn = false;
|
||||
const data = { id, formConf, modelId, propsValue };
|
||||
detailRef.value?.init(data);
|
||||
});
|
||||
}
|
||||
function handleColumnChange(data) {
|
||||
state.columnSettingList = data;
|
||||
}
|
||||
function handleSearchSubmit(data) {
|
||||
if (props.isPreview) return;
|
||||
clearSelectedRowKeys();
|
||||
let obj = {};
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
if (value || value === 0 || value === false) {
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length) obj[key] = value;
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
searchInfo.queryJson = JSON.stringify(obj) === '{}' ? '' : JSON.stringify(obj);
|
||||
reload({ page: 1 });
|
||||
}
|
||||
function handleSearchReset() {
|
||||
searchFormSubmit();
|
||||
}
|
||||
function transferExtraList(formConf) {
|
||||
if (!props.config?.formOptions?.length) return formConf;
|
||||
const loop = list => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item?.__config__?.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children);
|
||||
}
|
||||
if (item.__vModel__) {
|
||||
for (let j = 0; j < props.config?.formOptions.length; j++) {
|
||||
const element = props.config?.formOptions[j];
|
||||
if (element.targetField == item.__vModel__) {
|
||||
item.__config__.defaultValue = props.detailFormData[element.currentField + '_yunzhupaasId'];
|
||||
item.__config__.defaultCurrent = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(formConf.fields);
|
||||
return formConf;
|
||||
}
|
||||
|
||||
onMounted(() => init());
|
||||
</script>
|
||||
716
src/views/common/dynamicModel/list/detail/Item.vue
Normal file
716
src/views/common/dynamicModel/list/detail/Item.vue
Normal file
@@ -0,0 +1,716 @@
|
||||
<template>
|
||||
<a-col
|
||||
:class="[...(getConfig.className || []), getConfig.layout === 'colFormItem' ? 'ant-col-item' : '']"
|
||||
:span="getConfig.span"
|
||||
v-if="!getConfig.noShow && (!getConfig.visibility || (Array.isArray(getConfig.visibility) && getConfig.visibility.includes('pc')))">
|
||||
<template v-if="getConfig.layout === 'colFormItem'">
|
||||
<template v-if="getConfig.yunzhupaasKey === 'divider'">
|
||||
<yunzhupaas-divider :contentPosition="getItem.contentPosition" :content="getItem.content" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-form-item :name="getItem.__vModel__" :labelCol="getLabelCol">
|
||||
<template #label v-if="getConfig.showLabel">
|
||||
{{ getLabel ? getLabel + (formConf.labelSuffix || '') : '' }}
|
||||
<BasicHelp :text="getTipLabel" v-if="getLabel && getTipLabel" />
|
||||
</template>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'text'">
|
||||
<yunzhupaas-text :content="getItem.content" :textStyle="getItem.textStyle" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'link'">
|
||||
<yunzhupaas-link :content="getItem.content" :href="getItem.href" :target="getItem.target" :textStyle="getItem.textStyle" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'alert'">
|
||||
<yunzhupaas-alert
|
||||
:title="getItem.title"
|
||||
:type="getItem.type"
|
||||
:closable="getItem.closable"
|
||||
:showIcon="getItem.showIcon"
|
||||
:description="getItem.description"
|
||||
:closeText="getItem.closeText" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'groupTitle'">
|
||||
<yunzhupaas-group-title :content="getItem.content" :contentPosition="getItem.contentPosition" :helpMessage="getItem.helpMessage" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'button'">
|
||||
<yunzhupaas-button :align="getItem.align" :buttonText="getItem.buttonText" :type="getItem.type" :disabled="getItem.disabled" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="getConfig.defaultValue" detailed disabled />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="getConfig.defaultValue" detailed disabled />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'colorPicker'">
|
||||
<yunzhupaas-color-picker v-model:value="getConfig.defaultValue" :showAlpha="getItem.showAlpha" :colorFormat="getItem.colorFormat" disabled />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="getConfig.defaultValue" :count="getItem.count" :allowHalf="getItem.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="getConfig.defaultValue" :min="getItem.min" :max="getItem.max" :step="getItem.step" disabled />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'editor'">
|
||||
<div v-html="getConfig.defaultValue"></div>
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text leading-32px" @click="toDetail(item)">{{ getItem.name || getConfig.defaultValue }}</p>
|
||||
<ExtraRelationInfo
|
||||
:extraOptions="getItem.extraOptions"
|
||||
:data="extraData"
|
||||
v-if="getItem.extraOptions?.length && extraData && JSON.stringify(extraData) !== '{}'" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'popupSelect'">
|
||||
<p class="leading-32px">{{ getItem.name || getConfig.defaultValue }}</p>
|
||||
<ExtraRelationInfo
|
||||
:extraOptions="getItem.extraOptions"
|
||||
:data="extraData"
|
||||
v-if="getItem.extraOptions?.length && extraData && JSON.stringify(extraData) !== '{}'" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'barcode'">
|
||||
<yunzhupaas-barcode
|
||||
:format="getItem.format"
|
||||
:lineColor="getItem.lineColor"
|
||||
:background="getItem.background"
|
||||
:width="getItem.width"
|
||||
:height="getItem.height"
|
||||
:staticText="getItem.staticText"
|
||||
:dataType="getItem.dataType"
|
||||
:relationField="getItem.relationField + '_id'"
|
||||
:formData="formData" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'qrcode'">
|
||||
<yunzhupaas-qrcode
|
||||
:format="getItem.format"
|
||||
:colorLight="getItem.colorLight"
|
||||
:colorDark="getItem.colorDark"
|
||||
:width="getItem.width"
|
||||
:staticText="getItem.staticText"
|
||||
:dataType="getItem.dataType"
|
||||
:relationField="getItem.relationField + '_id'"
|
||||
:formData="formData" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number
|
||||
v-model:value="getConfig.defaultValue"
|
||||
:precision="getItem.precision"
|
||||
:addonBefore="getItem.addonBefore"
|
||||
:addonAfter="getItem.addonAfter"
|
||||
:thousands="getItem.thousands"
|
||||
:isAmountChinese="getItem.isAmountChinese"
|
||||
disabled
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
:expression="getItem.expression"
|
||||
:isStorage="getItem.isStorage"
|
||||
:formData="formData"
|
||||
:precision="getItem.precision"
|
||||
:thousands="getItem.thousands"
|
||||
:isAmountChinese="getItem.isAmountChinese"
|
||||
:roundType="getItem.roundType"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'location'">
|
||||
<yunzhupaas-location v-model:value="getConfig.defaultValue" :enableLocationScope="getItem.enableLocationScope" detailed />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="getConfig.defaultValue" detailed />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'signature'">
|
||||
<yunzhupaas-signature v-model:value="getConfig.defaultValue" detailed />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'iframe'">
|
||||
<yunzhupaas-iframe
|
||||
:href="getItem.href"
|
||||
:height="getItem.height"
|
||||
:borderType="getItem.borderType"
|
||||
:borderColor="getItem.borderColor"
|
||||
:borderWidth="getItem.borderWidth" />
|
||||
</template>
|
||||
<template v-else-if="getConfig.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="getConfig.defaultValue"
|
||||
:addonBefore="getItem.addonBefore"
|
||||
:addonAfter="getItem.addonAfter"
|
||||
:useMask="getItem.useMask"
|
||||
:maskConfig="getItem.maskConfig"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>{{ getValue(getItem.__config__?.defaultValue || '') }}</p>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'card'">
|
||||
<a-card :hoverable="getItem.shadow === 'hover'" :size="formConf.size">
|
||||
<template #title v-if="getItem.header">{{ getItem.header }}<BasicHelp :text="getTipLabel" v-if="getTipLabel" /></template>
|
||||
<a-row>
|
||||
<Item v-for="(childItem, childIndex) in getConfig.children" v-bind="getBindValue" :key="childIndex" :item="childItem" @toDetail="toDetail" />
|
||||
</a-row>
|
||||
</a-card>
|
||||
</template>
|
||||
<a-row v-if="getConfig.yunzhupaasKey === 'row'">
|
||||
<Item v-for="(childItem, childIndex) in getConfig.children" v-bind="getBindValue" :key="childIndex" :item="childItem" @toDetail="toDetail" />
|
||||
</a-row>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'tab'">
|
||||
<a-tabs :type="getItem.type" :tabPosition="getItem.tabPosition" :size="formConf.size" v-model:activeKey="getConfig.active">
|
||||
<a-tab-pane v-for="pane in getConfig.children" :key="pane.name" :tab="pane.titleI18nCode ? t(pane.titleI18nCode, pane.title) : pane.title">
|
||||
<a-row>
|
||||
<Item
|
||||
v-for="(childItem, childIndex) in pane.__config__.children"
|
||||
v-bind="getBindValue"
|
||||
:key="childIndex"
|
||||
:item="childItem"
|
||||
@toDetail="toDetail" />
|
||||
</a-row>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'steps'">
|
||||
<a-steps :size="formConf.size" v-model:current="getConfig.active" :type="item.simple ? 'navigation' : 'default'" :status="item.processStatus">
|
||||
<a-step v-for="step in getConfig.children" :key="step.name" :title="step.titleI18nCode ? t(step.titleI18nCode, step.title) : step.title">
|
||||
<template #icon v-if="step.icon">
|
||||
<span :class="step.icon + ' custom-icon'"></span>
|
||||
</template>
|
||||
</a-step>
|
||||
</a-steps>
|
||||
<a-row v-for="(step, stepIndex) in getConfig.children" :key="step.name" class="!pt-12px w-full" v-show="getConfig.active === stepIndex">
|
||||
<Item v-for="(childItem, childIndex) in step.__config__.children" v-bind="getBindValue" :key="childIndex" :item="childItem" @toDetail="toDetail" />
|
||||
</a-row>
|
||||
</template>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'collapse'">
|
||||
<a-collapse :ghost="getItem.ghost" :expandIconPosition="getItem.expandIconPosition" :accordion="getItem.accordion" v-model:activeKey="getConfig.active">
|
||||
<a-collapse-panel v-for="pane in getConfig.children" :key="pane.name" :header="pane.titleI18nCode ? t(pane.titleI18nCode, pane.title) : pane.title">
|
||||
<a-row>
|
||||
<Item
|
||||
v-for="(childItem, childIndex) in pane.__config__.children"
|
||||
v-bind="getBindValue"
|
||||
:key="childIndex"
|
||||
:item="childItem"
|
||||
@toDetail="toDetail" />
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</template>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'table' && !getConfig.noShow">
|
||||
<div class="yunzhupaas-child-list" v-if="item.layoutType === 'list'">
|
||||
<a-collapse expandIconPosition="end" :bordered="false" class="outer-collapse" v-model:activeKey="outerActiveKey">
|
||||
<a-collapse-panel forceRender>
|
||||
<template #header>
|
||||
<span class="min-h-22px inline-block">{{ getConfig.showTitle ? getLabel : '' }}</span>
|
||||
<BasicHelp :text="getTipLabel" v-if="getConfig.showTitle && getLabel && getTipLabel" />
|
||||
</template>
|
||||
<a-collapse :bordered="false" v-model:activeKey="innerActiveKey">
|
||||
<template #expandIcon="{ isActive }">
|
||||
<CaretRightOutlined :rotate="isActive ? 90 : 0" />
|
||||
</template>
|
||||
<a-collapse-panel v-for="(record, index) in getDefaultValue" :key="record.yunzhupaasId" forceRender>
|
||||
<template #header>
|
||||
<span class="min-h-22px inline-block">{{ getConfig.showTitle ? getLabel : '' }}({{ index + 1 }})</span>
|
||||
</template>
|
||||
<a-row :gutter="formConf.formStyle ? 0 : formConf.gutter">
|
||||
<a-col :span="column.__config__.span" v-for="column in getColumns" :key="column.__config__.formId">
|
||||
<a-form-item :labelCol="column.labelCol">
|
||||
<template #label v-if="column.__config__.showLabel">
|
||||
{{ column.title ? column.title + (column.__config__.labelSuffix || '') : '' }}
|
||||
<BasicHelp :text="column.tipLabel" v-if="column.title && column.tipLabel" />
|
||||
</template>
|
||||
<template v-if="column.__config__?.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="record[column.dataIndex]" detailed disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="record[column.dataIndex]" detailed disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'colorPicker'">
|
||||
<yunzhupaas-color-picker
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:showAlpha="column.showAlpha"
|
||||
:colorFormat="column.colorFormat"
|
||||
disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="record[column.dataIndex]" :count="column.count" :allowHalf="column.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="record[column.dataIndex]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text" @click="toTableDetail(column, record[column.dataIndex + '_id'])">{{ record[column.dataIndex] }}</p>
|
||||
</template>
|
||||
<template v-else-if="['relationFormAttr', 'popupAttr'].includes(column.__config__?.yunzhupaasKey)">
|
||||
<p v-if="!record[column.dataIndex]">{{ record[column.relationField.split('_yunzhupaasTable_')[0] + '_' + column.showField] }}</p>
|
||||
<p v-else>{{ record[column.dataIndex] }}</p>
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:precision="column.precision"
|
||||
:addonBefore="column.addonBefore"
|
||||
:addonAfter="column.addonAfter"
|
||||
:thousands="column.thousands"
|
||||
:isAmountChinese="column.isAmountChinese"
|
||||
disabled
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
:rowIndex="index"
|
||||
:expression="column.expression"
|
||||
:isStorage="column.isStorage"
|
||||
:formData="formData"
|
||||
:precision="column.precision"
|
||||
:thousands="column.thousands"
|
||||
:isAmountChinese="column.isAmountChinese"
|
||||
:roundType="column.roundType"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'location'">
|
||||
<yunzhupaas-location v-model:value="record[column.dataIndex]" :enableLocationScope="column.enableLocationScope" detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="record[column.dataIndex]" detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'signature'">
|
||||
<yunzhupaas-signature v-model:value="record[column.dataIndex]" detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:addonBefore="column.addonBefore"
|
||||
:addonAfter="column.addonAfter"
|
||||
:useMask="column.useMask"
|
||||
:maskConfig="column.maskConfig"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>{{ getValue(record[column.dataIndex]) }}</p>
|
||||
</template>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
<a-collapse-panel key="summary" v-if="getDefaultValue.length && item.showSummary">
|
||||
<template #header>
|
||||
<span class="min-h-22px inline-block">合计</span>
|
||||
</template>
|
||||
<a-row :gutter="formConf.formStyle ? 0 : formConf.gutter">
|
||||
<template v-for="(column, cIndex) in getColumns" :key="column.__config__.formId">
|
||||
<a-col :span="column.__config__.span" v-if="column.dataIndex && item.summaryField.includes(column.dataIndex)">
|
||||
<a-form-item :labelCol="column.labelCol">
|
||||
<template #label v-if="column.__config__.showLabel">
|
||||
{{ column.title ? column.title + (column.__config__.labelSuffix || '') : '' }}
|
||||
<BasicHelp :text="column.tipLabel" v-if="column.title && column.tipLabel" />
|
||||
</template>
|
||||
<YunzhupaasInput :value="getColumnSum[cIndex]" disabled detailed :style="column.style" />
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</template>
|
||||
</a-row>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</a-collapse-panel>
|
||||
</a-collapse>
|
||||
</div>
|
||||
<a-form-item v-else>
|
||||
<YunzhupaasGroupTitle :content="getLabel" v-if="getConfig.showTitle && getLabel" :helpMessage="getTipLabel" :bordered="false" />
|
||||
<a-table
|
||||
:data-source="getItem.__config__.defaultValue"
|
||||
:columns="getColumns"
|
||||
size="small"
|
||||
:pagination="false"
|
||||
:scroll="{ x: 'max-content' }"
|
||||
:bordered="formConf.formStyle === 'word-form' || !!getConfig?.complexHeaderList?.length">
|
||||
<template #headerCell="{ column }">
|
||||
{{ column.title }}
|
||||
<BasicHelp v-if="column.title && column.tipLabel" :text="column.tipLabel" />
|
||||
</template>
|
||||
<template #bodyCell="{ column, record, index }">
|
||||
<template v-if="column.key === 'index'">{{ index + 1 }}</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'uploadFile'">
|
||||
<yunzhupaas-upload-file v-model:value="record[column.dataIndex]" detailed disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'uploadImg'">
|
||||
<yunzhupaas-upload-img v-model:value="record[column.dataIndex]" detailed disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'colorPicker'">
|
||||
<yunzhupaas-color-picker v-model:value="record[column.dataIndex]" :showAlpha="column.showAlpha" :colorFormat="column.colorFormat" disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="record[column.dataIndex]" :count="column.count" :allowHalf="column.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="record[column.dataIndex]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'relationForm'">
|
||||
<p class="link-text" @click="toTableDetail(column, record[column.dataIndex + '_id'])">{{ record[column.dataIndex] }}</p>
|
||||
</template>
|
||||
<template v-else-if="['relationFormAttr', 'popupAttr'].includes(column.__config__?.yunzhupaasKey)">
|
||||
<p v-if="!record[column.dataIndex]">{{ record[column.relationField.split('_yunzhupaasTable_')[0] + '_' + column.showField] }}</p>
|
||||
<p v-else>{{ record[column.dataIndex] }}</p>
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:precision="column.precision"
|
||||
:addonBefore="column.addonBefore"
|
||||
:addonAfter="column.addonAfter"
|
||||
:thousands="column.thousands"
|
||||
:isAmountChinese="column.isAmountChinese"
|
||||
disabled
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
:rowIndex="index"
|
||||
:expression="column.expression"
|
||||
:isStorage="column.isStorage"
|
||||
:formData="formData"
|
||||
:precision="column.precision"
|
||||
:thousands="column.thousands"
|
||||
:isAmountChinese="column.isAmountChinese"
|
||||
:roundType="column.roundType"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'location'">
|
||||
<yunzhupaas-location v-model:value="record[column.dataIndex]" :enableLocationScope="column.enableLocationScope" detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'sign'">
|
||||
<yunzhupaas-sign v-model:value="record[column.dataIndex]" detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'signature'">
|
||||
<yunzhupaas-signature v-model:value="record[column.dataIndex]" detailed />
|
||||
</template>
|
||||
<template v-else-if="column.__config__?.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:addonBefore="column.addonBefore"
|
||||
:addonAfter="column.addonAfter"
|
||||
:useMask="column.useMask"
|
||||
:maskConfig="column.maskConfig"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-else>
|
||||
<p>{{ getValue(record[column.dataIndex]) }}</p>
|
||||
</template>
|
||||
</template>
|
||||
<template #summary v-if="getItem.__config__.defaultValue.length && getItem.showSummary">
|
||||
<a-table-summary fixed>
|
||||
<a-table-summary-row>
|
||||
<a-table-summary-cell :index="0">{{ t('component.table.summary') }}</a-table-summary-cell>
|
||||
<a-table-summary-cell v-for="(item, index) in getColumnSum" :key="index" :index="index + 1" :align="getSummaryCellAlign(index)">
|
||||
{{ item }}
|
||||
</a-table-summary-cell>
|
||||
</a-table-summary-row>
|
||||
</a-table-summary>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-form-item>
|
||||
</template>
|
||||
<template v-if="getConfig.yunzhupaasKey === 'tableGrid'">
|
||||
<table
|
||||
class="table-grid-box"
|
||||
:style="{
|
||||
'--borderType': getItem.__config__.borderType,
|
||||
'--borderColor': getItem.__config__.borderColor,
|
||||
'--borderWidth': getItem.__config__.borderWidth + 'px',
|
||||
}">
|
||||
<tbody>
|
||||
<tr v-for="(tr, index) in getConfig.children" :key="index">
|
||||
<td
|
||||
v-for="(td, i) in tr.__config__.children"
|
||||
:key="i"
|
||||
:colspan="td.__config__.colspan"
|
||||
:rowspan="td.__config__.rowspan"
|
||||
v-show="!td.__config__.merged"
|
||||
:style="{
|
||||
'--backgroundColor': td.__config__.backgroundColor,
|
||||
}">
|
||||
<a-row>
|
||||
<Item
|
||||
v-for="(childItem, childIndex) in td.__config__.children"
|
||||
v-bind="getBindValue"
|
||||
:key="childIndex"
|
||||
:item="childItem"
|
||||
@toDetail="toDetail" />
|
||||
</a-row>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</template>
|
||||
</template>
|
||||
</a-col>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, unref, reactive, toRefs, onMounted } from 'vue';
|
||||
import { omit } from 'lodash-es';
|
||||
import { thousandsFormat } from '@/utils/yunzhupaas';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { CaretRightOutlined } from '@ant-design/icons-vue';
|
||||
import { buildUUID } from '@/utils/uuid';
|
||||
import ExtraRelationInfo from '@/components/Yunzhupaas/RelationForm/src/ExtraRelationInfo.vue';
|
||||
import { getDataChange } from '@/api/onlineDev/visualDev';
|
||||
import { getDataInterfaceDataInfoByIds } from '@/api/systemData/dataInterface';
|
||||
|
||||
interface State {
|
||||
outerActiveKey: number[];
|
||||
innerActiveKey: string[];
|
||||
extraData: any;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'Item' });
|
||||
const props = defineProps({
|
||||
item: { type: Object, required: true },
|
||||
formConf: { type: Object, required: true },
|
||||
formData: { type: Object },
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
const emit = defineEmits(['toDetail']);
|
||||
const { t } = useI18n();
|
||||
const state = reactive<State>({
|
||||
outerActiveKey: [0],
|
||||
innerActiveKey: [],
|
||||
extraData: {},
|
||||
});
|
||||
const { outerActiveKey, innerActiveKey, extraData } = toRefs(state);
|
||||
|
||||
const getBindValue = computed(() => ({ ...omit(props, ['item']) }));
|
||||
const getItem = computed(() => {
|
||||
const item = props.item;
|
||||
const config = item.__config__;
|
||||
if (['groupTitle', 'divider', 'link', 'text'].includes(config.yunzhupaasKey)) {
|
||||
if (item.contentI18nCode) item.content = t(item.contentI18nCode, item.content);
|
||||
if (item.helpMessageI18nCode) item.helpMessage = t(item.helpMessageI18nCode, item.helpMessage);
|
||||
}
|
||||
if (config.yunzhupaasKey === 'button') {
|
||||
if (item.buttonTextI18nCode) item.buttonText = t(item.buttonTextI18nCode, item.buttonText);
|
||||
}
|
||||
if (config.yunzhupaasKey === 'alert') {
|
||||
if (item.titleI18nCode) item.title = t(item.titleI18nCode, item.title);
|
||||
if (item.descriptionI18nCode) item.description = t(item.descriptionI18nCode, item.description);
|
||||
if (item.closeTextI18nCode) item.closeText = t(item.closeTextI18nCode, item.closeText);
|
||||
}
|
||||
if (config.yunzhupaasKey === 'card') {
|
||||
if (item.headerI18nCode) item.header = t(item.headerI18nCode, item.header);
|
||||
}
|
||||
return item;
|
||||
});
|
||||
const getConfig = computed(() => props.item.__config__);
|
||||
const getDefaultValue = computed(() => {
|
||||
let defaultValue = props.item.__config__.defaultValue;
|
||||
if (unref(getConfig).yunzhupaasKey === 'table') {
|
||||
defaultValue = defaultValue.map(o => ({ ...o, yunzhupaasId: buildUUID() }));
|
||||
} else {
|
||||
Array.isArray(defaultValue) && (defaultValue = defaultValue.join());
|
||||
}
|
||||
return defaultValue;
|
||||
});
|
||||
const getLabelCol = computed(() => {
|
||||
const globalLabelWidth = props.formConf.labelWidth;
|
||||
let labelCol = {};
|
||||
if (props.formConf.labelPosition !== 'top' && unref(getConfig).showLabel) {
|
||||
let labelWidth = (unref(getConfig).labelWidth || globalLabelWidth) + 'px';
|
||||
if (!unref(getConfig).showLabel) labelWidth = '0px';
|
||||
labelCol = { style: { width: labelWidth } };
|
||||
}
|
||||
return labelCol;
|
||||
});
|
||||
const getLabel = computed(() => (unref(getConfig).labelI18nCode ? t(unref(getConfig).labelI18nCode, unref(getConfig).label) : unref(getConfig).label));
|
||||
const getTipLabel = computed(() =>
|
||||
unref(getConfig).tipLabelI18nCode ? t(unref(getConfig).tipLabelI18nCode, unref(getConfig).tipLabel) : unref(getConfig).tipLabel,
|
||||
);
|
||||
const getColumns = computed(() => {
|
||||
if (unref(getConfig).yunzhupaasKey !== 'table') return [];
|
||||
const noColumn = {
|
||||
width: 50,
|
||||
title: t('component.table.index'),
|
||||
dataIndex: 'index',
|
||||
key: 'index',
|
||||
align: 'center',
|
||||
customRender: ({ index }) => index + 1,
|
||||
fixed: 'left',
|
||||
};
|
||||
const list = unref(getConfig)
|
||||
.children.filter(
|
||||
o => !o.__config__.noShow && (!o.__config__.visibility || (Array.isArray(o.__config__.visibility) && o.__config__.visibility.includes('pc'))),
|
||||
)
|
||||
.map(o => ({
|
||||
...o,
|
||||
title: o.__config__?.labelI18nCode ? t(o.__config__?.labelI18nCode, o.__config__?.label) : o.__config__?.label,
|
||||
tipLabel: o.__config__?.tipLabelI18nCode ? t(o.__config__?.tipLabelI18nCode, o.__config__?.tipLabel) : o.__config__?.tipLabel,
|
||||
dataIndex: o.__vModel__,
|
||||
width: o.__config__?.columnWidth || undefined,
|
||||
align: o.__config__.tableAlign || 'left',
|
||||
fixed: o.__config__.tableFixed == 'left' || o.__config__.tableFixed == 'right' ? o.__config__.tableFixed : false,
|
||||
...(props.item.layoutType === 'list' ? { labelCol: getChildTableLabelCol(o.__config__) } : {}),
|
||||
}));
|
||||
let columnList = list;
|
||||
if (props.item.layoutType === 'list') return columnList;
|
||||
let complexHeaderList: any[] = props.item.__config__.complexHeaderList || [];
|
||||
if (complexHeaderList.length) {
|
||||
let childColumns: any[] = [];
|
||||
let firstChildColumns: string[] = [];
|
||||
for (let i = 0; i < complexHeaderList.length; i++) {
|
||||
const e = complexHeaderList[i];
|
||||
e.title = e.fullNameI18nCode ? t(e.fullNameI18nCode, e.fullName) : e.fullName;
|
||||
e.align = e.align;
|
||||
e.children = [];
|
||||
e.yunzhupaasKey = 'complexHeader';
|
||||
if (e.childColumns?.length) {
|
||||
childColumns.push(...e.childColumns);
|
||||
for (let k = 0; k < e.childColumns.length; k++) {
|
||||
const item = e.childColumns[k];
|
||||
for (let j = 0; j < list.length; j++) {
|
||||
const o = list[j];
|
||||
if (o.__vModel__ == item && o.__config__.tableFixed !== 'left' && o.__config__.tableFixed !== 'right') e.children.push({ ...o });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.children.length) firstChildColumns.push(e.children[0].__vModel__);
|
||||
}
|
||||
complexHeaderList = complexHeaderList.filter(o => o.children.length);
|
||||
let newList: any[] = [];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const e = list[i];
|
||||
if (!childColumns.includes(e.__vModel__) || e.__config__?.tableFixed === 'left' || e.__config__?.tableFixed === 'right') {
|
||||
newList.push(e);
|
||||
} else {
|
||||
if (firstChildColumns.includes(e.__vModel__)) {
|
||||
const item = complexHeaderList.find(o => o.childColumns.includes(e.__vModel__));
|
||||
newList.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
columnList = newList;
|
||||
}
|
||||
let columns = [noColumn, ...columnList];
|
||||
const leftFixedList = columns.filter(o => o.fixed === 'left');
|
||||
const rightFixedList = columns.filter(o => o.fixed === 'right');
|
||||
const noFixedList = columns.filter(o => o.fixed !== 'left' && o.fixed !== 'right');
|
||||
return [...leftFixedList, ...noFixedList, ...rightFixedList];
|
||||
});
|
||||
const getSummaryColumn = computed(() => {
|
||||
let defaultColumns = unref(getColumns);
|
||||
let columns: any[] = [];
|
||||
for (let i = 0; i < defaultColumns.length; i++) {
|
||||
const e = defaultColumns[i];
|
||||
if (e.yunzhupaasKey === 'table' || e.yunzhupaasKey === 'complexHeader') {
|
||||
if (e.children?.length) columns.push(...e.children);
|
||||
} else {
|
||||
columns.push(e);
|
||||
}
|
||||
if (e.fixed && e.children?.length) {
|
||||
for (let j = 0; j < e.children.length; j++) {
|
||||
e.children[j].fixed = e.fixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
return columns.filter(o => o?.key != 'index' && o?.key != 'action');
|
||||
});
|
||||
const getColumnSum = computed(() => {
|
||||
if (unref(getConfig).yunzhupaasKey !== 'table') return [];
|
||||
const list = unref(getSummaryColumn);
|
||||
const sums: any[] = [];
|
||||
const isSummary = key => props.item.summaryField.includes(key);
|
||||
const useThousands = key => list.some(o => o.__vModel__ === key && o.thousands);
|
||||
const tableData = list.filter(o => !o.__config__.noShow && (!o.__config__.visibility || o.__config__.visibility.includes('pc')));
|
||||
tableData.forEach((column, index) => {
|
||||
let sumVal = unref(getConfig).defaultValue.reduce((sum, d) => sum + getCmpValOfRow(d, column.__vModel__), 0);
|
||||
if (!isSummary(column.__vModel__)) sumVal = '';
|
||||
sumVal = Number.isNaN(sumVal) ? '' : sumVal;
|
||||
const realVal = sumVal && !Number.isInteger(sumVal) ? Number(sumVal).toFixed(2) : sumVal;
|
||||
sums[index] = useThousands(column.__vModel__) ? thousandsFormat(realVal) : realVal.toString();
|
||||
});
|
||||
return sums;
|
||||
});
|
||||
|
||||
function toDetail(item) {
|
||||
emit('toDetail', item);
|
||||
}
|
||||
function toTableDetail(item, value) {
|
||||
item.__config__.defaultValue = value;
|
||||
emit('toDetail', item);
|
||||
}
|
||||
function getValue(value) {
|
||||
return Array.isArray(value) ? value.join() : value;
|
||||
}
|
||||
function getCmpValOfRow(row, key) {
|
||||
const isSummary = key => props.item.summaryField.includes(key);
|
||||
if (!props.item.summaryField.length || !isSummary(key)) return 0;
|
||||
const target = row[key];
|
||||
if (!target) return 0;
|
||||
const data = isNaN(target) ? 0 : Number(target);
|
||||
return data;
|
||||
}
|
||||
function getSummaryCellAlign(index) {
|
||||
if (!unref(getSummaryColumn).length) return;
|
||||
return unref(getSummaryColumn)[index]?.align || 'left';
|
||||
}
|
||||
function getChildTableLabelCol(config) {
|
||||
const globalLabelWidth = props.formConf.labelWidth;
|
||||
let labelCol = {};
|
||||
if (props.formConf.labelPosition !== 'top' && config.showLabel) {
|
||||
let labelWidth = (config.labelWidth || globalLabelWidth) + 'px';
|
||||
if (!config.showLabel) labelWidth = '0px';
|
||||
labelCol = { style: { width: labelWidth } };
|
||||
}
|
||||
return labelCol;
|
||||
}
|
||||
// 平铺布局时设置默认展开
|
||||
function setActiveKey() {
|
||||
if (unref(getConfig).yunzhupaasKey !== 'table' || props.item.layoutType !== 'list') return;
|
||||
state.outerActiveKey = [0];
|
||||
state.innerActiveKey = [];
|
||||
if (!props.item.defaultExpandAll) return;
|
||||
state.innerActiveKey = ['summary'];
|
||||
if (!unref(getDefaultValue).length) return;
|
||||
for (let i = 0; i < unref(getDefaultValue).length; i++) {
|
||||
state.innerActiveKey.push(unref(getDefaultValue)[i].yunzhupaasId);
|
||||
}
|
||||
}
|
||||
function getParamList() {
|
||||
let templateJson: any[] = unref(getItem).templateJson;
|
||||
if (!templateJson || !templateJson.length || !props.formData) return templateJson;
|
||||
for (let i = 0; i < templateJson.length; i++) {
|
||||
if (templateJson[i].relationField && templateJson[i].sourceType == 1) {
|
||||
templateJson[i].defaultValue = props.formData[templateJson[i].relationField + '_yunzhupaasId'] || '';
|
||||
}
|
||||
}
|
||||
return templateJson;
|
||||
}
|
||||
// 弹窗选择/关联表单获取额外关联信息
|
||||
function getExtraRelationInfo() {
|
||||
if (!['relationForm', 'popupSelect'].includes(unref(getConfig).yunzhupaasKey) || !unref(getDefaultValue)) return;
|
||||
if (unref(getConfig).yunzhupaasKey === 'relationForm') {
|
||||
getDataChange(unref(getItem).modelId, { id: unref(getDefaultValue), propsValue: unref(getItem).propsValue }).then(res => {
|
||||
if (!res.data || !res.data.data) return;
|
||||
const data = JSON.parse(res.data.data);
|
||||
state.extraData = data;
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (unref(getConfig).yunzhupaasKey === 'popupSelect') {
|
||||
const paramList = getParamList();
|
||||
const query = {
|
||||
ids: [unref(getDefaultValue)],
|
||||
interfaceId: unref(getItem).interfaceId,
|
||||
propsValue: unref(getItem).propsValue,
|
||||
relationField: unref(getItem).relationField,
|
||||
paramList,
|
||||
};
|
||||
getDataInterfaceDataInfoByIds(unref(getItem).interfaceId, query).then(res => {
|
||||
const data = res.data && res.data.length ? res.data[0] : {};
|
||||
state.extraData = data;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
setActiveKey();
|
||||
getExtraRelationInfo();
|
||||
});
|
||||
</script>
|
||||
40
src/views/common/dynamicModel/list/detail/Parser.vue
Normal file
40
src/views/common/dynamicModel/list/detail/Parser.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<a-row :class="formConf.formStyle ? formConf.formStyle + ' word-form-detail' : ''">
|
||||
<a-form
|
||||
class="w-full"
|
||||
ref="formElRef"
|
||||
:name="getFormName"
|
||||
:colon="formConf.colon"
|
||||
:size="formConf.size"
|
||||
:disabled="formConf.disabled"
|
||||
:labelCol="getLabelCol"
|
||||
:layout="formConf.labelPosition === 'top' ? 'vertical' : 'horizontal'"
|
||||
:labelAlign="formConf.labelPosition === 'right' ? 'right' : 'left'">
|
||||
<a-row :gutter="formConf.formStyle ? 0 : formConf.gutter">
|
||||
<Item v-for="(item, index) in formConf.fields" :key="index" :item="item" v-bind="getBindValue" @toDetail="toDetail" />
|
||||
</a-row>
|
||||
</a-form>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed } from 'vue';
|
||||
import { buildUUID } from '@/utils/uuid';
|
||||
import Item from './Item.vue';
|
||||
|
||||
const props = defineProps({
|
||||
formConf: { type: Object, required: true },
|
||||
relationData: { type: Object, default: () => {} },
|
||||
formData: { type: Object },
|
||||
loading: { type: Boolean, default: false },
|
||||
});
|
||||
const emit = defineEmits(['toDetail']);
|
||||
|
||||
const getFormName = computed(() => `form-${buildUUID()}`);
|
||||
const getLabelCol = computed(() => ({ style: { width: props.formConf.labelWidth + 'px' } }));
|
||||
const getBindValue = computed(() => ({ ...props }));
|
||||
|
||||
function toDetail(data) {
|
||||
emit('toDetail', data);
|
||||
}
|
||||
</script>
|
||||
445
src/views/common/dynamicModel/list/detail/index.vue
Normal file
445
src/views/common/dynamicModel/list/detail/index.vue
Normal file
@@ -0,0 +1,445 @@
|
||||
<template>
|
||||
<BasicPopup v-bind="$attrs" @register="registerPopup" :title="title" destroyOnClose :closeFunc="onClose" class="full-popup">
|
||||
<template #insertToolbar>
|
||||
<a-button
|
||||
class="ml-10px"
|
||||
v-for="item in state.customBtns"
|
||||
:key="item.value"
|
||||
type="primary"
|
||||
:preIcon="item.actionConfig?.btnIcon"
|
||||
v-if="!loading"
|
||||
@click="customBtnsHandle(item)">
|
||||
{{ item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label }}
|
||||
</a-button>
|
||||
<a-button class="ml-10px" type="primary" @click="handlePrint" v-if="formConf.hasPrintBtn && formConf.printId">{{ getPrintText }}</a-button>
|
||||
</template>
|
||||
<div class="yunzhupaas-common-form-wrapper">
|
||||
<div class="yunzhupaas-common-form-wrapper__main" :style="{ margin: '0 auto', width: formConf.fullScreenWidth || '100%' }">
|
||||
<template v-if="!loading && extraList.length && !hideExtra">
|
||||
<a-tabs v-model:activeKey="extraActiveKey" class="yunzhupaas-content-wrapper-tabs" destroyInactiveTabPane @change="onTabChange">
|
||||
<a-tab-pane v-for="(item, index) in extraList" :key="index" :tab="item.fullName"></a-tab-pane>
|
||||
</a-tabs>
|
||||
<div class="yunzhupaas-content-detail-extra">
|
||||
<Parser
|
||||
class="p-10px !pt-0px"
|
||||
ref="parserRef"
|
||||
:formConf="formConf"
|
||||
:formData="formData"
|
||||
@toDetail="toDetail"
|
||||
:key="key"
|
||||
v-if="extraActiveKey == 0" />
|
||||
<div class="h-full" v-loading="extraLoading" v-else>
|
||||
<ExtraList
|
||||
ref="extraListRef"
|
||||
:config="extraList[extraActiveKey]"
|
||||
:detailFormData="formData"
|
||||
:key="extraKey"
|
||||
@openDetail="handleOpenDetail"
|
||||
@openForm="handleOpenForm"
|
||||
v-if="extraList[extraActiveKey]?.extraConfig && !extraLoading" />
|
||||
<yunzhupaas-empty class="extra-empty" v-if="!extraList[extraActiveKey]?.extraConfig && !extraLoading" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<Parser
|
||||
class="p-10px"
|
||||
ref="parserRef"
|
||||
:formConf="formConf"
|
||||
:formData="formData"
|
||||
@toDetail="toDetail"
|
||||
:key="key"
|
||||
v-if="!loading && (!extraList.length || hideExtra)" />
|
||||
</div>
|
||||
<FormExtraPanel v-bind="getFormExtraBind" v-if="state.dataForm.id && formConf.dataLog" />
|
||||
</div>
|
||||
</BasicPopup>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="formConf.generalWidth" :minHeight="100" :showOkBtn="false" :closeFunc="onClose">
|
||||
<template #insertFooter>
|
||||
<a-button
|
||||
class="ml-10px"
|
||||
v-for="item in state.customBtns"
|
||||
:key="item.value"
|
||||
type="primary"
|
||||
:preIcon="item.actionConfig?.btnIcon"
|
||||
v-if="!loading"
|
||||
@click="customBtnsHandle(item)">
|
||||
{{ item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label }}
|
||||
</a-button>
|
||||
<a-button class="ml-10px" type="primary" @click="handlePrint" v-if="formConf.hasPrintBtn && formConf.printId">{{ getPrintText }}</a-button>
|
||||
</template>
|
||||
<Parser ref="parserRef" :formConf="formConf" :formData="formData" @toDetail="toDetail" :key="key" v-if="!loading" />
|
||||
</BasicModal>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" :title="title" :width="formConf.drawerWidth" showFooter :showOkBtn="false" :closeFunc="onClose">
|
||||
<template #insertFooter>
|
||||
<a-button
|
||||
class="ml-10px"
|
||||
v-for="item in state.customBtns"
|
||||
:key="item.value"
|
||||
type="primary"
|
||||
:preIcon="item.actionConfig?.btnIcon"
|
||||
v-if="!loading"
|
||||
@click="customBtnsHandle(item)">
|
||||
{{ item.labelI18nCode ? t(item.labelI18nCode, item.label) : item.label }}
|
||||
</a-button>
|
||||
<a-button class="ml-10px" type="primary" @click="handlePrint" v-if="formConf.hasPrintBtn && formConf.printId">{{ getPrintText }}</a-button>
|
||||
</template>
|
||||
<div class="p-10px">
|
||||
<Parser ref="parserRef" :formConf="formConf" :formData="formData" @toDetail="toDetail" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicDrawer>
|
||||
<Detail v-if="detailVisible" ref="detailRef" @close="state.detailVisible = false" />
|
||||
<Form v-if="formVisible" ref="formRef" @reload="reloadTable" />
|
||||
<PrintSelect @register="registerPrintSelect" @change="handleShowBrowse" />
|
||||
<PrintBrowse @register="registerPrintBrowse" />
|
||||
<CustomForm ref="customFormRef" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getDataChange, getConfigData, getConfigDataByMenuId, getModelInfo, launchFlow } from '@/api/onlineDev/visualDev';
|
||||
import { getDataInterfaceRes } from '@/api/systemData/dataInterface';
|
||||
import { reactive, toRefs, nextTick, ref, computed } from 'vue';
|
||||
import { BasicPopup, usePopup } from '@/components/Popup';
|
||||
import { BasicModal, useModal } from '@/components/Modal';
|
||||
import { BasicDrawer, useDrawer } from '@/components/Drawer';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useGeneratorStore } from '@/store/modules/generator';
|
||||
import Parser from './Parser.vue';
|
||||
import Form from '../Form.vue';
|
||||
import PrintSelect from '@/components/PrintDesign/printSelect/index.vue';
|
||||
import PrintBrowse from '@/components/PrintDesign/printBrowse/index.vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { getScriptFunc, onlineUtils, getParamList, getLaunchFlowParamList } from '@/utils/yunzhupaas';
|
||||
import CustomForm from '../CustomForm.vue';
|
||||
import FormExtraPanel from '@/components/FormExtraPanel/index.vue';
|
||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
|
||||
|
||||
interface State {
|
||||
formConf: any;
|
||||
formData: any;
|
||||
config: any;
|
||||
loading: boolean;
|
||||
key: number;
|
||||
dataForm: any;
|
||||
formOperates: any[];
|
||||
title: string;
|
||||
detailVisible: boolean;
|
||||
formVisible: boolean;
|
||||
customBtns: any[];
|
||||
extraList: any[];
|
||||
extraActiveKey: number;
|
||||
extraConfig: any;
|
||||
extraLoading: boolean;
|
||||
extraKey: number;
|
||||
hideExtra: boolean;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'Detail' });
|
||||
const ExtraList = createAsyncComponent(() => import('./ExtraList.vue'));
|
||||
const emit = defineEmits(['close']);
|
||||
const userStore = useUserStore();
|
||||
const generatorStore = useGeneratorStore();
|
||||
const { createMessage, createConfirm } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const [registerPopup, { openPopup, closePopup, setPopupProps }] = usePopup();
|
||||
const [registerModal, { openModal, closeModal, setModalProps }] = useModal();
|
||||
const [registerDrawer, { openDrawer, closeDrawer, setDrawerProps }] = useDrawer();
|
||||
const [registerPrintSelect, { openModal: openPrintSelect }] = useModal();
|
||||
const [registerPrintBrowse, { openModal: openPrintBrowse }] = useModal();
|
||||
const parserRef = ref<any>(null);
|
||||
const detailRef = ref<any>(null);
|
||||
const customFormRef = ref<any>(null);
|
||||
const formRef = ref<any>(null);
|
||||
const extraListRef = ref<any>(null);
|
||||
const state = reactive<State>({
|
||||
formConf: {},
|
||||
formData: {},
|
||||
config: {},
|
||||
loading: false,
|
||||
key: +new Date(),
|
||||
dataForm: {
|
||||
id: '',
|
||||
data: '',
|
||||
},
|
||||
formOperates: [],
|
||||
title: t('common.detailText'),
|
||||
detailVisible: false,
|
||||
formVisible: false,
|
||||
customBtns: [],
|
||||
extraList: [],
|
||||
extraActiveKey: 0,
|
||||
extraConfig: {},
|
||||
extraLoading: false,
|
||||
extraKey: 0,
|
||||
hideExtra: false,
|
||||
});
|
||||
const { title, formConf, formData, key, loading, detailVisible, formVisible, extraList, extraActiveKey, extraLoading, extraKey, hideExtra } = toRefs(state);
|
||||
|
||||
const getPrintText = computed(() => {
|
||||
const text = state.formConf.printButtonTextI18nCode
|
||||
? t(state.formConf.printButtonTextI18nCode, state.formConf.printButtonText)
|
||||
: state.formConf.printButtonText;
|
||||
return text || t('common.printText');
|
||||
});
|
||||
const getFormExtraBind = computed(() => ({ showLog: state.formConf.dataLog, modelId: state.config.modelId, formDataId: state.config.id }));
|
||||
|
||||
defineExpose({ init });
|
||||
|
||||
function fillFormData(form, data) {
|
||||
const loop = (list, parent?) => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item.__vModel__) {
|
||||
if (item.__config__.yunzhupaasKey === 'relationForm' || item.__config__.yunzhupaasKey === 'popupSelect') {
|
||||
item.__config__.defaultValue = data[item.__vModel__ + '_id'];
|
||||
item.name = data[item.__vModel__] || '';
|
||||
} else {
|
||||
const val = data.hasOwnProperty(item.__vModel__) ? data[item.__vModel__] : item.__config__.defaultValue;
|
||||
item.__config__.defaultValue = val;
|
||||
}
|
||||
if (!state.config.isDataManage && state.config.useFormPermission) {
|
||||
let id = item.__config__.isSubTable ? parent.__vModel__ + '-' + item.__vModel__ : item.__vModel__;
|
||||
let noShow = true;
|
||||
if (state.formOperates && state.formOperates.length) {
|
||||
noShow = !state.formOperates.some(o => o.enCode === id);
|
||||
}
|
||||
noShow = item.__config__.noShow ? item.__config__.noShow : noShow;
|
||||
item.__config__.noShow = noShow;
|
||||
}
|
||||
} else {
|
||||
if (['relationFormAttr', 'popupAttr'].includes(item.__config__.yunzhupaasKey)) {
|
||||
item.__config__.defaultValue = data[item.relationField.split('_yunzhupaasTable_')[0] + '_' + item.showField];
|
||||
}
|
||||
}
|
||||
if (item.__config__ && item.__config__.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(form.fields);
|
||||
}
|
||||
function init(data) {
|
||||
state.loading = true;
|
||||
state.config = data;
|
||||
state.formConf = cloneDeep(data.formConf);
|
||||
state.customBtns = (state.formConf.customBtns || []).reverse();
|
||||
state.dataForm.id = data.id;
|
||||
state.extraActiveKey = 0;
|
||||
state.extraConfig = {};
|
||||
state.hideExtra = data.hideExtra || false;
|
||||
getFormOperates();
|
||||
getExtraList();
|
||||
openForm();
|
||||
nextTick(() => {
|
||||
setTimeout(initData, 0);
|
||||
});
|
||||
}
|
||||
function initData() {
|
||||
changeLoading(true);
|
||||
state.loading = true;
|
||||
if (state.config.id) {
|
||||
const extra = { modelId: state.config.modelId, id: state.config.id, type: 2 };
|
||||
generatorStore.setDynamicModelExtra(extra);
|
||||
getInfo(state.config.id, state.config.propsValue);
|
||||
} else {
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
function getInfo(id, propsValue) {
|
||||
let query: any = {
|
||||
id: id,
|
||||
menuId: state.config.menuId,
|
||||
};
|
||||
if (propsValue) query = { ...query, propsValue };
|
||||
getDataChange(state.config.modelId, query).then(res => {
|
||||
state.dataForm = res.data || {};
|
||||
if (!state.dataForm.data) return;
|
||||
state.formData = JSON.parse(state.dataForm.data);
|
||||
fillFormData(state.formConf, state.formData);
|
||||
initRelationForm(state.formConf.fields);
|
||||
nextTick(() => {
|
||||
state.loading = false;
|
||||
state.key = +new Date();
|
||||
changeLoading(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
function initRelationForm(componentList) {
|
||||
componentList.forEach(cur => {
|
||||
const config = cur.__config__;
|
||||
if (config.yunzhupaasKey == 'relationFormAttr' || config.yunzhupaasKey == 'popupAttr') {
|
||||
const relationKey = cur.relationField.split('_yunzhupaasTable_')[0];
|
||||
componentList.forEach(item => {
|
||||
const noVisibility = Array.isArray(item.__config__.visibility) && !item.__config__.visibility.includes('pc');
|
||||
if (relationKey == item.__vModel__ && (noVisibility || !!item.__config__.noShow) && !cur.__vModel__) {
|
||||
cur.__config__.noShow = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (cur.__config__.children && cur.__config__.children.length) initRelationForm(cur.__config__.children);
|
||||
});
|
||||
}
|
||||
function getFormOperates() {
|
||||
if (state.config.isPreview || state.config.isDataManage || !state.config.useFormPermission) return;
|
||||
const permissionList = userStore.getPermissionList;
|
||||
const modelId = state.config.menuId;
|
||||
const list = permissionList.filter(o => o.modelId === modelId);
|
||||
state.formOperates = list[0] && list[0].form ? list[0].form : [];
|
||||
}
|
||||
function toDetail(item) {
|
||||
if (!item.__config__.defaultValue) return;
|
||||
getConfigData(item.modelId).then(res => {
|
||||
if (!res.data) return;
|
||||
if (!res.data.formData) return;
|
||||
const formConf = JSON.parse(res.data.formData);
|
||||
formConf.popupType = state.formData.popupType;
|
||||
formConf.customBtns = [];
|
||||
formConf.hasPrintBtn = false;
|
||||
const data = {
|
||||
id: item.__config__.defaultValue,
|
||||
formConf,
|
||||
modelId: item.modelId,
|
||||
propsValue: item.propsValue,
|
||||
};
|
||||
state.detailVisible = true;
|
||||
nextTick(() => {
|
||||
detailRef.value?.init(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
function handlePrint() {
|
||||
if (state.config.isPreview) return createMessage.warning('功能预览不支持打印');
|
||||
if (!state.formConf.printId?.length) return createMessage.error('未配置打印模板');
|
||||
if (state.formConf.printId?.length === 1) return handleShowBrowse(state.formConf.printId[0]);
|
||||
openPrintSelect(true, state.formConf.printId);
|
||||
}
|
||||
function handleShowBrowse(id) {
|
||||
openPrintBrowse(true, { id, formInfo: [{ formId: state.dataForm.id }] });
|
||||
}
|
||||
function openForm() {
|
||||
if (state.formConf.popupType === 'fullScreen') return openPopup();
|
||||
if (state.formConf.popupType === 'drawer') return openDrawer();
|
||||
openModal();
|
||||
}
|
||||
function closeForm() {
|
||||
if (state.formConf.popupType === 'fullScreen') return closePopup();
|
||||
if (state.formConf.popupType === 'drawer') return closeDrawer();
|
||||
closeModal();
|
||||
}
|
||||
function setFormProps(data) {
|
||||
if (state.formConf.popupType === 'fullScreen') return setPopupProps(data);
|
||||
if (state.formConf.popupType === 'drawer') return setDrawerProps(data);
|
||||
setModalProps(data);
|
||||
}
|
||||
function changeLoading(loading) {
|
||||
setFormProps({ loading });
|
||||
}
|
||||
async function onClose() {
|
||||
emit('close');
|
||||
return true;
|
||||
}
|
||||
// 自定义按钮点击事件
|
||||
function customBtnsHandle(item) {
|
||||
if (item.actionConfig.btnType == 1) handlePopup(item.actionConfig);
|
||||
if (item.actionConfig.btnType == 2) handleScriptFunc(item.actionConfig);
|
||||
if (item.actionConfig.btnType == 3) handleInterface(item.actionConfig);
|
||||
if (item.actionConfig.btnType == 4) handleLaunchFlow(item);
|
||||
}
|
||||
function handlePopup(item) {
|
||||
const data = {
|
||||
...item,
|
||||
recordModelId: state.config.modelId,
|
||||
record: state.formData,
|
||||
};
|
||||
customFormRef.value?.init(data);
|
||||
}
|
||||
function handleScriptFunc(item) {
|
||||
const parameter = { data: state.formData, onlineUtils };
|
||||
const func: any = getScriptFunc(item.func);
|
||||
if (!func) return;
|
||||
func(parameter);
|
||||
}
|
||||
function handleInterface(item) {
|
||||
const handlerData = () => {
|
||||
getModelInfo(state.config.modelId, state.config.id).then(res => {
|
||||
const dataForm = res.data || {};
|
||||
if (!dataForm.data) return;
|
||||
const data = { ...JSON.parse(dataForm.data), id: state.config.id };
|
||||
handlerInterface(data);
|
||||
});
|
||||
};
|
||||
const handlerInterface = data => {
|
||||
const query = { paramList: getParamList(item.templateJson, { ...data, id: state.config.id }) || [] };
|
||||
getDataInterfaceRes(item.interfaceId, query).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
});
|
||||
};
|
||||
if (!item.useConfirm) return handlerData();
|
||||
createConfirm({
|
||||
iconType: 'warning',
|
||||
title: t('common.tipTitle'),
|
||||
content: item.confirmTitle || '确认执行此操作?',
|
||||
onOk: () => {
|
||||
handlerData();
|
||||
},
|
||||
});
|
||||
}
|
||||
function handleLaunchFlow(item) {
|
||||
const launchFlowCfg = cloneDeep(item.actionConfig.launchFlow);
|
||||
const query = {
|
||||
template: launchFlowCfg.flowId,
|
||||
btnCode: item.value,
|
||||
currentUser: launchFlowCfg.currentUser,
|
||||
customUser: launchFlowCfg.customUser,
|
||||
initiator: launchFlowCfg.initiator,
|
||||
dataList: [getLaunchFlowParamList(launchFlowCfg.transferList, state.formData)],
|
||||
};
|
||||
launchFlow(state.config.modelId, query).then(res => {
|
||||
createMessage.success(res.msg);
|
||||
});
|
||||
}
|
||||
function getExtraList() {
|
||||
state.extraList = state.formConf.detailExtraList?.length ? [{ fullName: state.config.title, id: 0 }, ...state.formConf.detailExtraList] : [];
|
||||
}
|
||||
function onTabChange(index) {
|
||||
if (state.extraList[index]?.extraConfig) return (state.extraKey = +new Date());
|
||||
state.extraLoading = true;
|
||||
setTimeout(() => {
|
||||
getConfig(state.extraList[index]?.targetFormId, index);
|
||||
}, 200);
|
||||
}
|
||||
function getConfig(menuId, index) {
|
||||
if (!menuId) return (state.extraLoading = false);
|
||||
getConfigDataByMenuId({ menuId, systemId: userStore.getUserInfo?.systemId })
|
||||
.then(res => {
|
||||
if (res.code !== 200 || !res.data) {
|
||||
state.extraList[index].extraConfig = '';
|
||||
state.extraLoading = false;
|
||||
state.extraKey = +new Date();
|
||||
return;
|
||||
}
|
||||
state.extraList[index].extraConfig = res.data;
|
||||
state.extraLoading = false;
|
||||
state.extraKey = +new Date();
|
||||
})
|
||||
.catch(() => {
|
||||
state.extraLoading = false;
|
||||
state.extraKey = +new Date();
|
||||
});
|
||||
}
|
||||
function handleOpenDetail(data) {
|
||||
state.detailVisible = true;
|
||||
nextTick(() => {
|
||||
detailRef.value?.init(data);
|
||||
});
|
||||
}
|
||||
function handleOpenForm(data) {
|
||||
state.formVisible = true;
|
||||
nextTick(() => {
|
||||
formRef.value?.init(data);
|
||||
});
|
||||
}
|
||||
function reloadTable() {
|
||||
extraListRef.value?.reload();
|
||||
}
|
||||
</script>
|
||||
2006
src/views/common/dynamicModel/list/index.vue
Normal file
2006
src/views/common/dynamicModel/list/index.vue
Normal file
File diff suppressed because it is too large
Load Diff
42
src/views/common/dynamicPortal/index.vue
Normal file
42
src/views/common/dynamicPortal/index.vue
Normal file
@@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper" v-loading="loading">
|
||||
<template v-if="!ajaxing">
|
||||
<template v-if="portalId">
|
||||
<PortalLayout :layout="layout" :enabledLock="enabledLock" v-if="type === 0" @layoutUpdatedEvent="layoutUpdatedEvent" />
|
||||
<div class="custom-page" v-if="type === 1">
|
||||
<component :is="currentView" v-if="linkType === 0" />
|
||||
<embed :src="url" width="100%" height="100%" type="text/html" v-if="linkType === 1" />
|
||||
</div>
|
||||
</template>
|
||||
<div class="portal-layout-nodata" v-else>
|
||||
<yunzhupaas-empty :image="emptyImage" />
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, toRefs, onUnmounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import PortalLayout from '@/components/VisualPortal/Portal/Layout/index.vue';
|
||||
import { usePortal } from '@/views/basic/home/hooks/usePortal';
|
||||
import emptyImage from '@/assets/images/dashboard-nodata.png';
|
||||
|
||||
defineOptions({ name: 'dynamicPortal' });
|
||||
defineEmits(['register']);
|
||||
|
||||
const { state, initData, clearAutoRefresh, layoutUpdatedEvent } = usePortal();
|
||||
const { loading, layout, type, linkType, currentView, url, ajaxing, portalId, enabledLock } = toRefs(state);
|
||||
|
||||
function init() {
|
||||
const route = useRoute();
|
||||
state.portalId = (route.meta.relationId as string) || '';
|
||||
if (!state.portalId) return;
|
||||
initData();
|
||||
}
|
||||
|
||||
onMounted(() => init());
|
||||
onUnmounted(() => clearAutoRefresh());
|
||||
</script>
|
||||
<style lang="less">
|
||||
@import '@/components/VisualPortal/style/index.less';
|
||||
</style>
|
||||
196
src/views/common/dynamicReport/index.vue
Normal file
196
src/views/common/dynamicReport/index.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper yunzhupaas-dynamicReport-wrapper" v-loading="pageLoading">
|
||||
<div class="tool-wrap" v-if="getHasBtn">
|
||||
<a-tooltip title="打印" v-if="allowPrint">
|
||||
<a-button type="text" :disabled="pageLoading" class="action-bar-btn" @click="handlePrint">
|
||||
<i class="icon-ym icon-ym-printExample" />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip title="导出" v-if="allowExport">
|
||||
<a-button type="text" :disabled="pageLoading" class="action-bar-btn" @click="handleDownload">
|
||||
<i class="icon-ym icon-ym-btn-export1" />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<div class="query-wrap" v-if="searchSchemas?.length">
|
||||
<BasicForm @register="registerSearchForm" :schemas="searchSchemas" @submit="handleSearchSubmit" @reset="searchFormSubmit" class="search-form">
|
||||
</BasicForm>
|
||||
</div>
|
||||
<div class="content-main">
|
||||
<YunzhupaasUniver ref="yunzhupaasUniverRef" :key="yunzhupaasUniverKey" />
|
||||
</div>
|
||||
</div>
|
||||
<ReportPrint @register="registerReportPrint" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted, onUnmounted, toRefs, ref, unref, nextTick, computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { getPreviewTemplate, exportFileExcel } from '@/api/onlineDev/report';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import YunzhupaasUniver from 'yunzhupaas-univer';
|
||||
import { useReport } from '@/components/Report/src/hooks/useReport';
|
||||
import { ReportPrint } from '@/components/Report';
|
||||
import { downloadByUrlReport } from '@/utils/file/download';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import { getFloatUrl } from '@/components/Report/src/helper';
|
||||
import { useGlobSetting } from '@/hooks/setting';
|
||||
|
||||
defineOptions({ name: 'dynamicReport' });
|
||||
|
||||
interface State {
|
||||
id: string;
|
||||
reportData: any;
|
||||
pageLoading: boolean;
|
||||
yunzhupaasUniverAPI: any;
|
||||
allowExport: number;
|
||||
allowPrint: number;
|
||||
sheetId: string;
|
||||
yunzhupaasUniverKey: number;
|
||||
queryList: any[];
|
||||
}
|
||||
|
||||
const yunzhupaasUniverRef = ref();
|
||||
const state = reactive<State>({
|
||||
id: '',
|
||||
reportData: {},
|
||||
pageLoading: false,
|
||||
yunzhupaasUniverAPI: null,
|
||||
allowExport: 0,
|
||||
allowPrint: 0,
|
||||
sheetId: '',
|
||||
yunzhupaasUniverKey: 0,
|
||||
queryList: [],
|
||||
});
|
||||
const { pageLoading, allowExport, allowPrint, yunzhupaasUniverKey } = toRefs(state);
|
||||
|
||||
const [registerReportPrint, { openModal: openPrintModal }] = useModal();
|
||||
const [registerSearchForm, { updateSchema, submit: searchFormSubmit, getFieldsValue }] = useForm({
|
||||
baseColProps: { span: 6 },
|
||||
showActionButtonGroup: true,
|
||||
showAdvancedButton: true,
|
||||
compact: true,
|
||||
});
|
||||
const formApi = { updateSchema, getFieldsValue };
|
||||
const { getRealEchart, getSearchSchema, searchSchemas } = useReport(formApi);
|
||||
const globSetting = useGlobSetting();
|
||||
const getHasBtn = computed(() => {
|
||||
if (state.allowExport || state.allowPrint) return true;
|
||||
return false;
|
||||
});
|
||||
|
||||
// 初始化
|
||||
function init() {
|
||||
const route = useRoute();
|
||||
state.id = route?.meta?.relationId as string;
|
||||
if (!state.id) return;
|
||||
state.pageLoading = true;
|
||||
initData();
|
||||
}
|
||||
function initData(data = {}) {
|
||||
state.pageLoading = true;
|
||||
getPreviewTemplate(state.id, data).then(res => {
|
||||
state.reportData = res.data;
|
||||
state.allowExport = res.data.allowExport || 0;
|
||||
state.allowPrint = res.data.allowPrint || 0;
|
||||
state.queryList = res.data.queryList ? JSON.parse(res.data.queryList) : [];
|
||||
const snapshot = res.data.snapshot ? JSON.parse(res.data.snapshot) : null;
|
||||
const cells = res.data.cells ? JSON.parse(res.data.cells) : {};
|
||||
const chartData = res.data.chartData ? JSON.parse(res.data.chartData) : [];
|
||||
const floatEcharts = getRealEchart(cells.floatEcharts ?? null, chartData);
|
||||
let floatImages = cells.floatImages || {};
|
||||
let item = getFloatUrl(snapshot.resources, globSetting.reportApiUrl, floatImages);
|
||||
snapshot.resources = item.list;
|
||||
floatImages = item?.floatImages || {};
|
||||
getSearchSchema(state.queryList, state.sheetId || '');
|
||||
state.yunzhupaasUniverKey = +new Date();
|
||||
nextTick(() => {
|
||||
handleCreate(snapshot, floatEcharts, floatImages);
|
||||
});
|
||||
state.pageLoading = false;
|
||||
});
|
||||
}
|
||||
// 创建报表实例
|
||||
function handleCreate(snapshot, floatEcharts, floatImages) {
|
||||
const res = unref(yunzhupaasUniverRef)?.handleCreateDesignUnit({
|
||||
mode: 'preview',
|
||||
snapshot,
|
||||
floatEcharts,
|
||||
floatImages,
|
||||
uiHeader: false,
|
||||
uiContextMenu: false,
|
||||
workbookReadonly: true,
|
||||
defaultActiveSheetId: state.sheetId || '',
|
||||
loading: true,
|
||||
});
|
||||
state.yunzhupaasUniverAPI = res ? res?.yunzhupaasUniverAPI : null;
|
||||
onReportCommandExecuted();
|
||||
}
|
||||
function onReportCommandExecuted() {
|
||||
state.yunzhupaasUniverAPI?.onCommandExecuted((command: any) => {
|
||||
const { id: commandId } = command ?? {};
|
||||
// 切换sheet
|
||||
if (commandId === 'sheet.operation.set-worksheet-active') {
|
||||
state.sheetId = command.params.subUnitId;
|
||||
getSearchSchema(state.queryList, state.sheetId);
|
||||
}
|
||||
});
|
||||
}
|
||||
// 销毁示例
|
||||
function handleDisposeUnit() {
|
||||
unref(yunzhupaasUniverRef)?.handleDisposeUnit();
|
||||
}
|
||||
function handleSearchSubmit(data) {
|
||||
initData({ sheetId: state.sheetId, ...data });
|
||||
}
|
||||
// 打印
|
||||
function handlePrint() {
|
||||
const { snapshot } = unref(yunzhupaasUniverRef)?.getPreviewWorkbookData();
|
||||
const sheetId = state.yunzhupaasUniverAPI.getActiveWorkbook()?.getActiveSheet()?.getSheetId();
|
||||
const activeWorksheetId = unref(yunzhupaasUniverRef)?.getActiveWorksheetId();
|
||||
const { xSplit = 0, ySplit = 0 } = snapshot?.sheets?.[activeWorksheetId]?.freeze ?? {};
|
||||
const hasXFreeze = xSplit > 0;
|
||||
const hasYFreeze = ySplit > 0;
|
||||
openPrintModal(true, { id: state.reportData.versionId, fullName: state.reportData.fullName, sheetId, snapshot, hasXFreeze, hasYFreeze });
|
||||
}
|
||||
// 导出
|
||||
function handleDownload() {
|
||||
const { snapshot } = unref(yunzhupaasUniverRef)?.getPreviewWorkbookData();
|
||||
const sheetId = state.yunzhupaasUniverAPI.getActiveWorkbook()?.getActiveSheet()?.getSheetId();
|
||||
const data = unref(searchSchemas).length ? getFieldsValue() : {};
|
||||
const query = { ...data, sheetId, fullName: state.reportData.fullName, snapshot: snapshot ? JSON.stringify(snapshot) : null };
|
||||
exportFileExcel(state.reportData.versionId, query).then(res => {
|
||||
downloadByUrlReport({ url: res.data.url });
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
handleDisposeUnit();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="less">
|
||||
.yunzhupaas-dynamicReport-wrapper {
|
||||
background-color: @component-background;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tool-wrap {
|
||||
height: 53px;
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid @border-color-base1;
|
||||
}
|
||||
.query-wrap {
|
||||
padding: 10px 10px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
.content-main {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
25
src/views/common/externalLink/index.vue
Normal file
25
src/views/common/externalLink/index.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper yunzhupaas-content-wrapper-form">
|
||||
<iframe width="100%" height="100%" frameborder="0" scrolling="yes" :src="url"></iframe>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useTabs } from '@/hooks/web/useTabs';
|
||||
import { decodeByBase64 } from '@/utils/cipher';
|
||||
|
||||
defineOptions({ name: 'externalLink' });
|
||||
|
||||
const route = useRoute();
|
||||
const { setTitle } = useTabs();
|
||||
const url = ref('');
|
||||
|
||||
onMounted(() => {
|
||||
const { query } = route;
|
||||
if (!query.href) return;
|
||||
const href = decodeByBase64(query.href as string);
|
||||
url.value = href;
|
||||
if (query.name) setTitle(query.name as string);
|
||||
});
|
||||
</script>
|
||||
217
src/views/common/formShortLink/form/index.vue
Normal file
217
src/views/common/formShortLink/form/index.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper short-link-wrapper">
|
||||
<div class="short-link-lock-wrapper" v-show="formPassUse">
|
||||
<div class="short-link-lock-form">
|
||||
<a-input-group compact class="enter-y">
|
||||
<a-input-password v-model:value="password" :placeholder="t('views.dynamicModel.passwordPlaceholder')" @keyup.enter="unLock()" />
|
||||
<a-button :loading="btnLoading" @click="unLock()">
|
||||
<template #icon><unlock-outlined /></template>
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="short-link-main" v-if="!formPassUse">
|
||||
<a-popover placement="bottomRight">
|
||||
<template #content>
|
||||
<p class="shortLink-tip">{{ t('views.dynamicModel.scanAndShare') }}</p>
|
||||
<QrCode :value="state.formLink" :width="154" :options="{ margin: 1 }" class="my-5px" />
|
||||
</template>
|
||||
<i class="ym-custom ym-custom-qrcode icon-qrcode"></i>
|
||||
</a-popover>
|
||||
<div class="short-link-header">
|
||||
{{ config.fullName }}
|
||||
</div>
|
||||
<div class="short-link-content short-link-form">
|
||||
<Parser ref="parserRef" :formConf="formConf" :isShortLink="true" @submit="submitForm" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
<div class="short-link-footer">
|
||||
<a-button type="primary" @click="handleSubmit" :loading="btnLoading">{{ getOkText }}</a-button>
|
||||
<a-button type="warning" class="ml-10px" @click="handleReset">{{ t('common.resetText') }}</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getConfig, createModel, checkPwd } from '@/api/onlineDev/shortLink';
|
||||
import { reactive, toRefs, nextTick, ref, unref, onMounted, computed } from 'vue';
|
||||
import { createAsyncComponent } from '@/utils/factory/createAsyncComponent';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { QrCode } from '@/components/Qrcode/index';
|
||||
import { UnlockOutlined } from '@ant-design/icons-vue';
|
||||
import { encryptByMd5 } from '@/utils/cipher';
|
||||
import dayjs from 'dayjs';
|
||||
import { getDateTimeUnit } from '@/utils/yunzhupaas';
|
||||
|
||||
interface State {
|
||||
formLink: string;
|
||||
formConf: any;
|
||||
config: any;
|
||||
loading: boolean;
|
||||
btnLoading: boolean;
|
||||
key: number;
|
||||
shortLinkId: string;
|
||||
formPassUse: number;
|
||||
password: string;
|
||||
}
|
||||
|
||||
const props = defineProps(['config', 'modelId', 'isPreview']);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const parserRef = ref<any>(null);
|
||||
const state = reactive<State>({
|
||||
formLink: '',
|
||||
formConf: {},
|
||||
config: {},
|
||||
loading: false,
|
||||
btnLoading: false,
|
||||
key: +new Date(),
|
||||
shortLinkId: '',
|
||||
formPassUse: -1,
|
||||
password: '',
|
||||
});
|
||||
const { formConf, key, loading, config, btnLoading, formPassUse, password } = toRefs(state);
|
||||
const Parser = createAsyncComponent(() => import('@/components/FormGenerator/src/components/Parser.vue'));
|
||||
const validFieldsList = [
|
||||
'input',
|
||||
'textarea',
|
||||
'inputNumber',
|
||||
'switch',
|
||||
'datePicker',
|
||||
'timePicker',
|
||||
'colorPicker',
|
||||
'rate',
|
||||
'slider',
|
||||
'editor',
|
||||
'link',
|
||||
'text',
|
||||
'alert',
|
||||
'table',
|
||||
'collapse',
|
||||
'collapseItem',
|
||||
'tabItem',
|
||||
'tab',
|
||||
'row',
|
||||
'card',
|
||||
'groupTitle',
|
||||
'divider',
|
||||
'tableGrid',
|
||||
'tableGridTr',
|
||||
'tableGridTd',
|
||||
'location',
|
||||
'iframe',
|
||||
'steps',
|
||||
'stepItem',
|
||||
];
|
||||
const selectFieldsList = ['radio', 'checkbox', 'select', 'treeSelect', 'cascader'];
|
||||
|
||||
const getOkText = computed(() => {
|
||||
const text = state.formConf.confirmButtonTextI18nCode
|
||||
? t(state.formConf.confirmButtonTextI18nCode, state.formConf.confirmButtonText)
|
||||
: state.formConf.confirmButtonText;
|
||||
return text || t('common.okText');
|
||||
});
|
||||
|
||||
function init() {
|
||||
state.config = props.config;
|
||||
state.formConf = state.config.formData ? JSON.parse(state.config.formData) : {};
|
||||
state.formConf.fields = getRealFields(state.formConf.fields);
|
||||
fillFormData(state.formConf, {});
|
||||
nextTick(() => {
|
||||
state.loading = false;
|
||||
state.key = +new Date();
|
||||
});
|
||||
}
|
||||
function validFields(o) {
|
||||
if (!o.__config__ || !o.__config__.yunzhupaasKey) return true;
|
||||
const yunzhupaasKey = o.__config__.yunzhupaasKey;
|
||||
if (validFieldsList.includes(yunzhupaasKey) || (selectFieldsList.includes(yunzhupaasKey) && o.__config__.dataType === 'static')) return true;
|
||||
return false;
|
||||
}
|
||||
function getRealFields(list) {
|
||||
let newList = list.filter(item => validFields(item));
|
||||
newList.forEach(o => o.__config__?.children && Array.isArray(o.__config__.children) && (o.__config__.children = getRealFields(o.__config__.children)));
|
||||
return newList;
|
||||
}
|
||||
function fillFormData(form, data) {
|
||||
const currDate = new Date();
|
||||
const loop = list => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item.__vModel__) {
|
||||
if (item.__config__.defaultCurrent) {
|
||||
if (item.__config__.yunzhupaasKey === 'datePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).startOf(getDateTimeUnit(item.format)).valueOf();
|
||||
}
|
||||
if (item.__config__.yunzhupaasKey === 'timePicker') {
|
||||
item.__config__.defaultValue = dayjs(currDate).format(item.format || 'HH:mm:ss');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item.__config__ && item.__config__.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children);
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(form.fields);
|
||||
form.formData = data;
|
||||
}
|
||||
function submitForm(data, callback) {
|
||||
if (!data) return;
|
||||
state.btnLoading = true;
|
||||
const dataForm = { data: JSON.stringify(data) };
|
||||
createModel(props.modelId, dataForm, state.config.encryption)
|
||||
.then(res => {
|
||||
createMessage.success(res.msg);
|
||||
if (callback && typeof callback === 'function') callback();
|
||||
state.btnLoading = false;
|
||||
handleReset();
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
function handleReset() {
|
||||
getParser().handleReset();
|
||||
}
|
||||
function handleSubmit() {
|
||||
if (props.isPreview) return createMessage.warning('功能预览不支持数据保存');
|
||||
getParser().handleSubmit();
|
||||
}
|
||||
function getParser() {
|
||||
const parser = unref(parserRef);
|
||||
if (!parser) throw new Error('parser is null!');
|
||||
return parser;
|
||||
}
|
||||
function unLock() {
|
||||
if (!state.password) return createMessage.error(t('views.dynamicModel.passwordPlaceholder'));
|
||||
state.btnLoading = true;
|
||||
const query = {
|
||||
id: state.shortLinkId,
|
||||
type: 0,
|
||||
encryption: props.config.encryption,
|
||||
password: encryptByMd5(state.password),
|
||||
};
|
||||
checkPwd(query)
|
||||
.then(() => {
|
||||
state.btnLoading = false;
|
||||
state.formPassUse = 0;
|
||||
init();
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
state.loading = true;
|
||||
getConfig(props.modelId, props.config.encryption).then(res => {
|
||||
state.formLink = res.data.formLink || '';
|
||||
state.shortLinkId = res.data.id || '';
|
||||
state.formPassUse = res.data.formPassUse || 0;
|
||||
if (state.formPassUse) return;
|
||||
init();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
66
src/views/common/formShortLink/index.vue
Normal file
66
src/views/common/formShortLink/index.vue
Normal file
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<component :is="currentView" :config="config" :modelId="modelId" :isPreview="isPreview" v-if="showPage" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { reactive, onMounted, toRefs, markRaw } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { getConfigData } from '@/api/onlineDev/shortLink';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useTabs } from '@/hooks/web/useTabs';
|
||||
import Form from './form/index.vue';
|
||||
import List from './list/index.vue';
|
||||
import { AesEncryption } from '@/utils/cipher';
|
||||
|
||||
interface State {
|
||||
currentView: any;
|
||||
showPage: boolean;
|
||||
isPreview: boolean;
|
||||
modelId: string;
|
||||
config: any;
|
||||
encryption: string;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'formShortLink' });
|
||||
const { createMessage } = useMessage();
|
||||
const { close } = useTabs();
|
||||
const state = reactive<State>({
|
||||
currentView: '',
|
||||
showPage: false,
|
||||
isPreview: false,
|
||||
modelId: '',
|
||||
config: {},
|
||||
encryption: '',
|
||||
});
|
||||
const { currentView, showPage, isPreview, modelId, config } = toRefs(state);
|
||||
const router = useRouter();
|
||||
|
||||
function init() {
|
||||
const route = useRoute();
|
||||
if (!route.query.encryption) return;
|
||||
state.encryption = route.query.encryption as string;
|
||||
const aesEncryption = new AesEncryption({ useHex: true });
|
||||
const configStr = aesEncryption.decryptByAES(state.encryption);
|
||||
if (!configStr) return;
|
||||
const config = JSON.parse(configStr);
|
||||
state.modelId = config.modelId;
|
||||
if (!state.modelId) return;
|
||||
getConfig(config.type);
|
||||
}
|
||||
function getConfig(type) {
|
||||
getConfigData(state.modelId, state.encryption).then(res => {
|
||||
if (res.code !== 200 || !res.data) {
|
||||
close();
|
||||
router.replace('/404');
|
||||
createMessage.error(res.msg || '请求出错,请重试');
|
||||
return;
|
||||
}
|
||||
state.config = { ...res.data, encryption: state.encryption };
|
||||
state.currentView = type == 'form' ? markRaw(Form) : markRaw(List);
|
||||
state.showPage = true;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
</script>
|
||||
167
src/views/common/formShortLink/list/detail/index.vue
Normal file
167
src/views/common/formShortLink/list/detail/index.vue
Normal file
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<BasicPopup v-bind="$attrs" @register="registerPopup" :title="title" destroyOnClose :closeFunc="onClose">
|
||||
<div class="p-10px" :style="{ margin: '0 auto', width: formConf.fullScreenWidth || '100%' }">
|
||||
<Parser ref="parserRef" :formConf="formConf" :formData="formData" :relationData="relationData" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicPopup>
|
||||
<BasicModal v-bind="$attrs" @register="registerModal" :title="title" :width="formConf.generalWidth" :minHeight="100" :showOkBtn="false" :closeFunc="onClose">
|
||||
<Parser ref="parserRef" :formConf="formConf" :formData="formData" :relationData="relationData" :key="key" v-if="!loading" />
|
||||
</BasicModal>
|
||||
<BasicDrawer v-bind="$attrs" @register="registerDrawer" :title="title" :width="formConf.drawerWidth" showFooter :showOkBtn="false" :closeFunc="onClose">
|
||||
<div class="p-10px">
|
||||
<Parser ref="parserRef" :formConf="formConf" :formData="formData" :relationData="relationData" :key="key" v-if="!loading" />
|
||||
</div>
|
||||
</BasicDrawer>
|
||||
<Detail v-if="detailVisible" ref="detailRef" @close="state.detailVisible = false" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { getDataChange } from '@/api/onlineDev/shortLink';
|
||||
import { reactive, toRefs, nextTick, ref } from 'vue';
|
||||
import { BasicPopup, usePopup } from '@/components/Popup';
|
||||
import { BasicModal, useModal } from '@/components/Modal';
|
||||
import { BasicDrawer, useDrawer } from '@/components/Drawer';
|
||||
import Parser from '../../../dynamicModel/list/detail/Parser.vue';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
|
||||
interface State {
|
||||
formConf: any;
|
||||
formData: any;
|
||||
config: any;
|
||||
loading: boolean;
|
||||
key: number;
|
||||
dataForm: any;
|
||||
formOperates: any[];
|
||||
title: string;
|
||||
relationData: any;
|
||||
detailVisible: boolean;
|
||||
}
|
||||
|
||||
defineOptions({ name: 'Detail' });
|
||||
const emit = defineEmits(['close']);
|
||||
const { t } = useI18n();
|
||||
const [registerPopup, { openPopup, closePopup, setPopupProps }] = usePopup();
|
||||
const [registerModal, { openModal, closeModal, setModalProps }] = useModal();
|
||||
const [registerDrawer, { openDrawer, closeDrawer, setDrawerProps }] = useDrawer();
|
||||
const parserRef = ref<any>(null);
|
||||
const detailRef = ref<any>(null);
|
||||
const state = reactive<State>({
|
||||
formConf: {},
|
||||
formData: {},
|
||||
config: {},
|
||||
loading: false,
|
||||
key: +new Date(),
|
||||
dataForm: {
|
||||
id: '',
|
||||
data: '',
|
||||
},
|
||||
formOperates: [],
|
||||
title: t('common.detailText'),
|
||||
relationData: {},
|
||||
detailVisible: false,
|
||||
});
|
||||
const { title, formConf, formData, relationData, key, loading, detailVisible } = toRefs(state);
|
||||
|
||||
defineExpose({ init });
|
||||
|
||||
function fillFormData(form, data) {
|
||||
let relationFormAttrList: any[] = [];
|
||||
const loop = (list, parent?) => {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
let item = list[i];
|
||||
if (item.__vModel__) {
|
||||
if (item.__config__.yunzhupaasKey === 'relationForm' || item.__config__.yunzhupaasKey === 'popupSelect') {
|
||||
item.__config__.defaultValue = data[item.__vModel__ + '_id'];
|
||||
item.name = data[item.__vModel__] || '';
|
||||
} else {
|
||||
const val = data.hasOwnProperty(item.__vModel__) ? data[item.__vModel__] : item.__config__.defaultValue;
|
||||
item.__config__.defaultValue = val;
|
||||
}
|
||||
if (state.config.useFormPermission) {
|
||||
let id = item.__config__.isSubTable ? parent.__vModel__ + '-' + item.__vModel__ : item.__vModel__;
|
||||
let noShow = true;
|
||||
if (state.formOperates && state.formOperates.length) {
|
||||
noShow = !state.formOperates.some(o => o.enCode === id);
|
||||
}
|
||||
noShow = item.__config__.noShow ? item.__config__.noShow : noShow;
|
||||
item.__config__.noShow = noShow;
|
||||
}
|
||||
}
|
||||
if (['relationFormAttr', 'popupAttr'].includes(item.__config__.yunzhupaasKey)) relationFormAttrList.push(item);
|
||||
if (item.__config__ && item.__config__.children && Array.isArray(item.__config__.children)) {
|
||||
loop(item.__config__.children, item);
|
||||
}
|
||||
}
|
||||
};
|
||||
loop(form.fields);
|
||||
}
|
||||
function init(data) {
|
||||
state.config = data;
|
||||
state.formConf = cloneDeep(data.formConf);
|
||||
state.dataForm.id = data.id;
|
||||
openForm();
|
||||
nextTick(() => {
|
||||
setTimeout(initData, 0);
|
||||
});
|
||||
}
|
||||
function initData() {
|
||||
changeLoading(true);
|
||||
state.loading = true;
|
||||
if (state.config.id) {
|
||||
getInfo(state.config.id);
|
||||
} else {
|
||||
closeForm();
|
||||
}
|
||||
}
|
||||
function getInfo(id) {
|
||||
getDataChange(state.config.modelId, id, state.config.encryption).then(res => {
|
||||
state.dataForm = res.data || {};
|
||||
if (!state.dataForm.data) return;
|
||||
state.formData = JSON.parse(state.dataForm.data);
|
||||
fillFormData(state.formConf, state.formData);
|
||||
initRelationForm(state.formConf.fields);
|
||||
nextTick(() => {
|
||||
state.loading = false;
|
||||
state.key = +new Date();
|
||||
changeLoading(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
function initRelationForm(componentList) {
|
||||
componentList.forEach(cur => {
|
||||
const config = cur.__config__;
|
||||
if (config.yunzhupaasKey == 'relationFormAttr' || config.yunzhupaasKey == 'popupAttr') {
|
||||
const relationKey = cur.relationField.split('_yunzhupaasTable_')[0];
|
||||
componentList.forEach(item => {
|
||||
const noVisibility = Array.isArray(item.__config__.visibility) && !item.__config__.visibility.includes('pc');
|
||||
if (relationKey == item.__vModel__ && (noVisibility || !!item.__config__.noShow) && !cur.__vModel__) {
|
||||
cur.__config__.noShow = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (cur.__config__.children && cur.__config__.children.length) initRelationForm(cur.__config__.children);
|
||||
});
|
||||
}
|
||||
function openForm() {
|
||||
if (state.formConf.popupType === 'fullScreen') return openPopup();
|
||||
if (state.formConf.popupType === 'drawer') return openDrawer();
|
||||
openModal();
|
||||
}
|
||||
function closeForm() {
|
||||
if (state.formConf.popupType === 'fullScreen') return closePopup();
|
||||
if (state.formConf.popupType === 'drawer') return closeDrawer();
|
||||
closeModal();
|
||||
}
|
||||
function setFormProps(data) {
|
||||
if (state.formConf.popupType === 'fullScreen') return setPopupProps(data);
|
||||
if (state.formConf.popupType === 'drawer') return setDrawerProps(data);
|
||||
setModalProps(data);
|
||||
}
|
||||
function changeLoading(loading) {
|
||||
setFormProps({ loading });
|
||||
}
|
||||
async function onClose() {
|
||||
emit('close');
|
||||
return true;
|
||||
}
|
||||
</script>
|
||||
675
src/views/common/formShortLink/list/index.vue
Normal file
675
src/views/common/formShortLink/list/index.vue
Normal file
@@ -0,0 +1,675 @@
|
||||
<template>
|
||||
<div class="yunzhupaas-content-wrapper short-link-wrapper short-link-wrapper-list">
|
||||
<div class="short-link-lock-wrapper" v-show="columnPassUse">
|
||||
<div class="short-link-lock-form">
|
||||
<a-input-group compact class="enter-y">
|
||||
<a-input-password v-model:value="password" :placeholder="t('views.dynamicModel.passwordPlaceholder')" @keyup.enter="unLock()" />
|
||||
<a-button :loading="btnLoading" @click="unLock()">
|
||||
<template #icon><unlock-outlined /></template>
|
||||
</a-button>
|
||||
</a-input-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="short-link-main" v-show="!columnPassUse">
|
||||
<a-popover placement="bottomRight">
|
||||
<template #content>
|
||||
<p class="shortLink-tip">{{ t('views.dynamicModel.scanAndShare') }}</p>
|
||||
<QrCode :value="state.columnLink" :width="154" :options="{ margin: 1 }" class="my-5px" />
|
||||
</template>
|
||||
<i class="ym-custom ym-custom-qrcode icon-qrcode"></i>
|
||||
</a-popover>
|
||||
<div class="short-link-header">
|
||||
{{ config.fullName }}
|
||||
</div>
|
||||
<div class="yunzhupaas-content-wrapper-center">
|
||||
<div class="yunzhupaas-content-wrapper-search-box" v-if="columnData.searchList?.length">
|
||||
<BasicForm
|
||||
@register="registerSearchForm"
|
||||
:schemas="searchSchemas"
|
||||
@advanced-change="redoHeight"
|
||||
@submit="handleSearchSubmit"
|
||||
@reset="handleSearchReset"
|
||||
class="search-form">
|
||||
</BasicForm>
|
||||
</div>
|
||||
<div class="yunzhupaas-content-wrapper-content">
|
||||
<BasicTable @register="registerTable" v-bind="getTableBindValue" ref="tableRef" @columns-change="handleColumnChange">
|
||||
<template #expandedRowRender="{ record }" v-if="getChildTableStyle === 2 && childColumnList.length">
|
||||
<a-tabs size="small">
|
||||
<a-tab-pane :key="cIndex" :tab="child.label" :label="child.label" v-for="(child, cIndex) in childColumnList">
|
||||
<a-table size="small" :data-source="record[child.prop]" :columns="child.children" :pagination="false" :scroll="{ x: 'max-content' }">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="record[column.dataIndex]" :count="column.count" :allowHalf="column.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="record[column.dataIndex]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:useMask="column.useMask"
|
||||
:maskConfig="column.maskConfig"
|
||||
:showOverflow="columnData.showOverflow"
|
||||
detailed />
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-tab-pane>
|
||||
</a-tabs>
|
||||
</template>
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-for="(item, index) in childColumnList" v-if="getChildTableStyle !== 2 && childColumnList.length">
|
||||
<template v-if="column.id?.includes('-') && item.children && item.children[0] && column.key === item.children[0]?.dataIndex">
|
||||
<ChildTableColumn
|
||||
:data="record[item.prop]"
|
||||
:head="item.children"
|
||||
@toggleExpand="toggleExpand(record, `${item.prop}Expand`)"
|
||||
:expand="record[`${item.prop}Expand`]"
|
||||
:showOverflow="columnData.showOverflow"
|
||||
:key="index" />
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="!(record.top || column.id?.includes('-'))">
|
||||
<template v-if="column.yunzhupaasKey === 'inputNumber'">
|
||||
<yunzhupaas-input-number v-model:value="record[column.dataIndex]" :precision="column.precision" :thousands="column.thousands" disabled detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'calculate'">
|
||||
<yunzhupaas-calculate
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:isStorage="column.isStorage"
|
||||
:precision="column.precision"
|
||||
:thousands="column.thousands"
|
||||
:roundType="column.roundType"
|
||||
detailed />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'rate'">
|
||||
<yunzhupaas-rate v-model:value="record[column.dataIndex]" :count="column.count" :allowHalf="column.allowHalf" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'slider'">
|
||||
<yunzhupaas-slider v-model:value="record[column.dataIndex]" :min="column.min" :max="column.max" :step="column.step" disabled />
|
||||
</template>
|
||||
<template v-if="column.yunzhupaasKey === 'input'">
|
||||
<yunzhupaas-input
|
||||
v-model:value="record[column.dataIndex]"
|
||||
:useMask="column.useMask"
|
||||
:maskConfig="column.maskConfig"
|
||||
:showOverflow="columnData.showOverflow"
|
||||
detailed />
|
||||
</template>
|
||||
</template>
|
||||
<template v-if="column.key === 'action' && (!record.top || columnData.type == 5)">
|
||||
<TableAction :actions="getTableActions(record)" />
|
||||
</template>
|
||||
</template>
|
||||
<template #summary v-if="columnData.showSummary && [1, 2, 4].includes(columnData.type)">
|
||||
<a-table-summary fixed>
|
||||
<a-table-summary-row>
|
||||
<a-table-summary-cell :index="0">{{ t('component.table.summary') }}</a-table-summary-cell>
|
||||
<a-table-summary-cell v-for="(item, index) in getColumnSum" :key="index" :index="index + 1">{{ item }}</a-table-summary-cell>
|
||||
<a-table-summary-cell :index="getColumnSum.length + 1"></a-table-summary-cell>
|
||||
</a-table-summary-row>
|
||||
</a-table-summary>
|
||||
</template>
|
||||
</BasicTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Detail ref="detailRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getModelList, getConfig, checkPwd } from '@/api/onlineDev/shortLink';
|
||||
import { ref, reactive, onMounted, toRefs, computed, unref, nextTick } from 'vue';
|
||||
import { useMessage } from '@/hooks/web/useMessage';
|
||||
import { useI18n } from '@/hooks/web/useI18n';
|
||||
import { BasicForm, useForm } from '@/components/Form';
|
||||
import { BasicTable, useTable, TableAction, ActionItem, TableActionType, SorterResult } from '@/components/Table';
|
||||
import Detail from './detail/index.vue';
|
||||
import ChildTableColumn from '../../dynamicModel/list/ChildTableColumn.vue';
|
||||
import { getScriptFunc, thousandsFormat } from '@/utils/yunzhupaas';
|
||||
import { getSearchFormSchemas } from '@/components/FormGenerator/src/helper/transform';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { QrCode } from '@/components/Qrcode/index';
|
||||
import { UnlockOutlined } from '@ant-design/icons-vue';
|
||||
import { encryptByMd5 } from '@/utils/cipher';
|
||||
|
||||
interface State {
|
||||
config: any;
|
||||
columnData: any;
|
||||
formConf: any;
|
||||
hasBatchBtn: boolean;
|
||||
columnBtnsList: any[];
|
||||
customBtnsList: any[];
|
||||
columnOptions: any[];
|
||||
treeFieldNames: any;
|
||||
leftTreeData: any[];
|
||||
leftTreeLoading: boolean;
|
||||
treeActiveId: string;
|
||||
treeActiveNodePath: any;
|
||||
columns: any[];
|
||||
complexColumns: any[];
|
||||
childColumnList: any[];
|
||||
exportList: any[];
|
||||
cacheList: any[];
|
||||
currFlow: any;
|
||||
isCustomCopy: boolean;
|
||||
candidateType: number;
|
||||
currRow: any;
|
||||
workFlowFormData: any;
|
||||
expandObj: any;
|
||||
columnSettingList: any[];
|
||||
searchSchemas: any[];
|
||||
treeRelationObj: any;
|
||||
customRow: any;
|
||||
customCell: any;
|
||||
columnLink: string;
|
||||
btnLoading: boolean;
|
||||
key: number;
|
||||
shortLinkId: string;
|
||||
columnPassUse: number;
|
||||
password: string;
|
||||
realSearchList: any[];
|
||||
realColumnList: any[];
|
||||
}
|
||||
|
||||
const props = defineProps(['config', 'modelId', 'isPreview']);
|
||||
const { createMessage } = useMessage();
|
||||
const { t } = useI18n();
|
||||
const tableRef = ref<Nullable<TableActionType>>(null);
|
||||
const detailRef = ref<any>(null);
|
||||
const searchInfo = reactive({
|
||||
modelId: '',
|
||||
queryJson: '',
|
||||
superQueryJson: '',
|
||||
encryption: props.config.encryption,
|
||||
});
|
||||
const state = reactive<State>({
|
||||
config: {},
|
||||
columnData: {},
|
||||
formConf: {},
|
||||
hasBatchBtn: false,
|
||||
columnBtnsList: [],
|
||||
customBtnsList: [],
|
||||
columnOptions: [],
|
||||
treeFieldNames: {
|
||||
children: 'children',
|
||||
title: 'fullName',
|
||||
key: 'id',
|
||||
isLeaf: 'isLeaf',
|
||||
},
|
||||
leftTreeData: [],
|
||||
leftTreeLoading: false,
|
||||
treeActiveId: '',
|
||||
treeActiveNodePath: [],
|
||||
columns: [],
|
||||
complexColumns: [], // 复杂表头
|
||||
childColumnList: [],
|
||||
exportList: [],
|
||||
cacheList: [],
|
||||
currFlow: {},
|
||||
isCustomCopy: false,
|
||||
candidateType: 1,
|
||||
currRow: {},
|
||||
workFlowFormData: {},
|
||||
expandObj: {},
|
||||
columnSettingList: [],
|
||||
searchSchemas: [],
|
||||
treeRelationObj: null,
|
||||
customRow: null,
|
||||
customCell: null,
|
||||
columnLink: '',
|
||||
btnLoading: false,
|
||||
key: +new Date(),
|
||||
shortLinkId: '',
|
||||
columnPassUse: -1,
|
||||
password: '',
|
||||
realSearchList: [],
|
||||
realColumnList: [],
|
||||
});
|
||||
const { columnData, childColumnList, searchSchemas, config, btnLoading, columnPassUse, password } = toRefs(state);
|
||||
const validFieldsList = [
|
||||
'input',
|
||||
'textarea',
|
||||
'inputNumber',
|
||||
'switch',
|
||||
'datePicker',
|
||||
'timePicker',
|
||||
'colorPicker',
|
||||
'rate',
|
||||
'slider',
|
||||
'editor',
|
||||
'link',
|
||||
'text',
|
||||
'alert',
|
||||
'table',
|
||||
'collapse',
|
||||
'collapseItem',
|
||||
'tabItem',
|
||||
'tab',
|
||||
'row',
|
||||
'card',
|
||||
'groupTitle',
|
||||
'divider',
|
||||
'tableGrid',
|
||||
'tableGridTr',
|
||||
'tableGridTd',
|
||||
'location',
|
||||
'iframe',
|
||||
'steps',
|
||||
'stepItem',
|
||||
];
|
||||
const selectFieldsList = ['radio', 'checkbox', 'select', 'treeSelect', 'cascader'];
|
||||
const [registerSearchForm, { submit: searchFormSubmit }] = useForm({
|
||||
baseColProps: { span: 6 },
|
||||
showActionButtonGroup: true,
|
||||
showAdvancedButton: true,
|
||||
compact: true,
|
||||
});
|
||||
const [registerTable, { reload, setLoading, redoHeight }] = useTable({
|
||||
api: getModelList,
|
||||
immediate: false,
|
||||
clickToRowSelect: false,
|
||||
resizeHeightOffset: -10,
|
||||
// scroll: { x: 'max-content' },
|
||||
afterFetch: data => {
|
||||
// 行内编辑
|
||||
if (state.columnData.type === 4) {
|
||||
const list = data.map(o => ({ ...o, rowEdit: false }));
|
||||
state.cacheList = cloneDeep(list);
|
||||
return list;
|
||||
}
|
||||
let list = data.map(o => ({
|
||||
...o,
|
||||
...state.expandObj,
|
||||
}));
|
||||
state.cacheList = cloneDeep(list);
|
||||
// 分组表格
|
||||
if (state.columnData.type === 3) {
|
||||
list.map(o => {
|
||||
if (o.children && o.children.length) {
|
||||
o.children = o.children.map(e => ({
|
||||
...e,
|
||||
...state.expandObj,
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
return list;
|
||||
},
|
||||
});
|
||||
|
||||
const getPagination = computed(() => {
|
||||
if ([3, 5].includes(state.columnData.type) || !state.columnData.hasPage) return false;
|
||||
return { pageSize: state.columnData.pageSize };
|
||||
});
|
||||
const getChildTableStyle = computed(() => (state.columnData.type == 3 || state.columnData.type == 5 ? 1 : state.columnData.childTableStyle));
|
||||
const getColumns = computed(() => (unref(getChildTableStyle) == 2 || state.columnData.type == 4 ? state.columns : state.complexColumns));
|
||||
const getTableBindValue = computed(() => {
|
||||
let columns = unref(getColumns);
|
||||
const defaultSortConfig = (state.columnData.defaultSortConfig || []).map(o => (o.sort === 'desc' ? '-' : '') + o.field);
|
||||
const data: any = {
|
||||
pagination: unref(getPagination),
|
||||
searchInfo: unref(searchInfo),
|
||||
defSort: { sidx: defaultSortConfig.join(',') },
|
||||
sortFn: (sortInfo: SorterResult | SorterResult[]) => {
|
||||
if (Array.isArray(sortInfo)) {
|
||||
const sortList = sortInfo.map(o => (o.order === 'descend' ? '-' : '') + o.field);
|
||||
return { sidx: sortList.join(',') };
|
||||
} else {
|
||||
const { field, order } = sortInfo;
|
||||
if (field && order) {
|
||||
// 排序字段
|
||||
return { sidx: (order === 'descend' ? '-' : '') + field };
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
},
|
||||
columns,
|
||||
clearSelectOnPageChange: true,
|
||||
ellipsis: !!state.columnData.showOverflow,
|
||||
isTreeTable: [3, 5].includes(state.columnData.type),
|
||||
bordered: (unref(getChildTableStyle) != 2 && !!state.childColumnList?.length) || !!state.columnData.complexHeaderList?.length,
|
||||
};
|
||||
data.actionColumn = {
|
||||
width: 50,
|
||||
title: t('component.table.action'),
|
||||
dataIndex: 'action',
|
||||
fixed: 'right',
|
||||
};
|
||||
if (state.customRow) data.customRow = state.customRow;
|
||||
return data;
|
||||
});
|
||||
const getSummaryColumn = computed(() => {
|
||||
let defaultColumns = unref(getColumns);
|
||||
// 处理列固定
|
||||
if (state.columnSettingList?.length) {
|
||||
for (let i = 0; i < defaultColumns.length; i++) {
|
||||
inner: for (let j = 0; j < state.columnSettingList.length; j++) {
|
||||
if (defaultColumns[i].dataIndex === state.columnSettingList[j].dataIndex) {
|
||||
defaultColumns[i].fixed = state.columnSettingList[j].fixed;
|
||||
defaultColumns[i].visible = state.columnSettingList[j].visible;
|
||||
break inner;
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultColumns = defaultColumns.filter(o => o.visible);
|
||||
}
|
||||
let columns: any[] = [];
|
||||
for (let i = 0; i < defaultColumns.length; i++) {
|
||||
const e = defaultColumns[i];
|
||||
if (e.yunzhupaasKey === 'table' || e.yunzhupaasKey === 'complexHeader') {
|
||||
if (e.children?.length) columns.push(...e.children);
|
||||
} else {
|
||||
columns.push(e);
|
||||
}
|
||||
if (e.fixed && e.children?.length) {
|
||||
for (let j = 0; j < e.children.length; j++) {
|
||||
e.children[j].fixed = e.fixed;
|
||||
}
|
||||
}
|
||||
}
|
||||
const leftFixedList = columns.filter(o => o.fixed === 'left');
|
||||
const rightFixedList = columns.filter(o => o.fixed === 'right');
|
||||
const noFixedList = columns.filter(o => o.fixed !== 'left' && o.fixed !== 'right');
|
||||
return [...leftFixedList, ...rightFixedList, ...noFixedList];
|
||||
});
|
||||
// 列表合计
|
||||
const getColumnSum = computed(() => {
|
||||
const sums: any[] = [];
|
||||
const isSummary = key => state.columnData.summaryField.includes(key);
|
||||
const useThousands = key => unref(getSummaryColumn).some(o => o.__vModel__ === key && o.thousands);
|
||||
unref(getSummaryColumn).forEach((column, index) => {
|
||||
let sumVal = state.cacheList.reduce((sum, d) => sum + getCmpValOfRow(d, column.prop), 0);
|
||||
if (!isSummary(column.prop)) sumVal = '';
|
||||
sumVal = Number.isNaN(sumVal) ? '' : sumVal;
|
||||
const realVal = sumVal && !Number.isInteger(sumVal) ? Number(sumVal).toFixed(2) : sumVal;
|
||||
sums[index] = useThousands(column.prop) ? thousandsFormat(realVal) : realVal;
|
||||
});
|
||||
if ([1, 2].includes(state.columnData.type) && unref(getChildTableStyle) === 2 && state.childColumnList.length) sums.unshift('');
|
||||
return sums;
|
||||
});
|
||||
|
||||
function getCmpValOfRow(row, key) {
|
||||
const isSummary = key => state.columnData.summaryField.includes(key);
|
||||
if (!state.columnData.summaryField.length || !isSummary(key)) return 0;
|
||||
const target = row[key];
|
||||
if (!target) return 0;
|
||||
const data = isNaN(target) ? 0 : Number(target);
|
||||
return data;
|
||||
}
|
||||
function getTableActions(record): ActionItem[] {
|
||||
return [{ label: t('common.detailText'), onClick: columnBtnsHandle.bind(null, 'detail', record) }];
|
||||
}
|
||||
// 行按钮点击事件
|
||||
function columnBtnsHandle(key, record) {
|
||||
if (key === 'detail') return goDetail(record);
|
||||
}
|
||||
// 查看详情
|
||||
function goDetail(record) {
|
||||
const formConf = cloneDeep(state.formConf);
|
||||
formConf.fields = getRealFields(formConf.fields);
|
||||
const data = {
|
||||
id: record.id,
|
||||
formConf: formConf,
|
||||
modelId: props.modelId,
|
||||
useFormPermission: false,
|
||||
encryption: props.config.encryption,
|
||||
};
|
||||
detailRef.value?.init(data);
|
||||
}
|
||||
function validFields(o) {
|
||||
if (!o.__config__ || !o.__config__.yunzhupaasKey) return true;
|
||||
const yunzhupaasKey = o.__config__.yunzhupaasKey;
|
||||
if (validFieldsList.includes(yunzhupaasKey) || (selectFieldsList.includes(yunzhupaasKey) && o.__config__.dataType === 'static')) return true;
|
||||
return false;
|
||||
}
|
||||
function getRealFields(list) {
|
||||
let newList = list.filter(item => validFields(item));
|
||||
newList.forEach(o => o.__config__?.children && Array.isArray(o.__config__.children) && (o.__config__.children = getRealFields(o.__config__.children)));
|
||||
return newList;
|
||||
}
|
||||
function init() {
|
||||
state.config = {
|
||||
modelId: props.modelId,
|
||||
isPreview: props.isPreview,
|
||||
...props.config,
|
||||
};
|
||||
searchInfo.modelId = props.modelId;
|
||||
if (!state.config.columnData || (state.config.webType != '4' && !state.config.formData)) return;
|
||||
state.columnData = JSON.parse(state.config.columnData);
|
||||
state.columnData.type = 1;
|
||||
state.columnData.searchList = state.realSearchList;
|
||||
state.columnData.columnList = state.realColumnList;
|
||||
if (state.columnData.type === 3) {
|
||||
state.columnData.columnList = state.columnData.columnList.filter(o => o.prop != state.columnData.groupField);
|
||||
}
|
||||
state.hasBatchBtn = state.columnData.btnsList.some(o => ['batchRemove', 'batchPrint'].includes(o.value));
|
||||
state.formConf = state.config.formData ? JSON.parse(state.config.formData) : {};
|
||||
state.formConf.popupType = 'general';
|
||||
state.columnOptions = state.columnData.columnOptions || [];
|
||||
setLoading(true);
|
||||
if (state.columnData.funcs.rowStyle) {
|
||||
state.customRow = (record, index) => {
|
||||
const data = { row: record, rowIndex: index };
|
||||
const func: any = getScriptFunc(state.columnData.funcs.rowStyle);
|
||||
const style: any = func ? func(data) : null;
|
||||
if (!style) return {};
|
||||
return { style };
|
||||
};
|
||||
}
|
||||
if (state.columnData.funcs.cellStyle) {
|
||||
state.customCell = (record, rowIndex, column) => {
|
||||
const data = { row: record, rowIndex, column, columnIndex: column.key };
|
||||
const func: any = getScriptFunc(state.columnData.funcs.cellStyle);
|
||||
const style: any = func ? func(data) : null;
|
||||
if (!style) return {};
|
||||
return { style };
|
||||
};
|
||||
}
|
||||
getSearchSchemas();
|
||||
getColumnList();
|
||||
if (props.isPreview) return setLoading(false);
|
||||
nextTick(() => {
|
||||
state.columnData.searchList?.length ? searchFormSubmit() : reload({ page: 1 });
|
||||
});
|
||||
}
|
||||
function getSearchSchemas() {
|
||||
if (state.columnData.treeRelation) {
|
||||
for (let i = 0; i < state.columnData.searchList.length; i++) {
|
||||
const e = state.columnData.searchList[i];
|
||||
if (e.id === state.columnData.treeRelation) {
|
||||
state.treeRelationObj = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 搜索字段里无左侧树关联字段时,去全部字段里获取关联字段属性
|
||||
if (!state.treeRelationObj) {
|
||||
for (let i = 0; i < state.columnData.columnOptions.length; i++) {
|
||||
const e = state.columnData.columnOptions[i];
|
||||
if (e.id === state.columnData.treeRelation) {
|
||||
state.treeRelationObj = { ...e, searchMultiple: false, yunzhupaasKey: e.__config__.yunzhupaasKey };
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const schemas = getSearchFormSchemas(state.columnData.searchList);
|
||||
state.searchSchemas = schemas;
|
||||
}
|
||||
function getColumnList() {
|
||||
let columnList: any[] = [];
|
||||
columnList = state.columnData.columnList;
|
||||
state.exportList = columnList;
|
||||
let columns = columnList.map(o => ({
|
||||
...o,
|
||||
title: o.label,
|
||||
dataIndex: o.prop,
|
||||
align: o.align,
|
||||
fixed: o.fixed == 'none' ? false : o.fixed,
|
||||
sorter: o.sortable,
|
||||
width: o.width || 100,
|
||||
customCell: state.customCell || null,
|
||||
}));
|
||||
if (state.columnData.type !== 3 && state.columnData.type !== 5) columns = getComplexColumns(columns);
|
||||
state.columns = columns.filter(o => o.prop.indexOf('-') < 0);
|
||||
getChildComplexColumns(columns);
|
||||
}
|
||||
function getComplexColumns(columns) {
|
||||
let complexHeaderList: any[] = state.columnData.complexHeaderList || [];
|
||||
if (!complexHeaderList.length) return columns;
|
||||
let childColumns: any[] = [];
|
||||
let firstChildColumns: string[] = [];
|
||||
for (let i = 0; i < complexHeaderList.length; i++) {
|
||||
const e = complexHeaderList[i];
|
||||
e.title = e.fullNameI18nCode ? t(e.fullNameI18nCode, e.fullName) : e.fullName;
|
||||
e.align = e.align;
|
||||
e.dataIndex = e.id;
|
||||
e.prop = e.id;
|
||||
e.children = [];
|
||||
e.yunzhupaasKey = 'complexHeader';
|
||||
if (e.childColumns?.length) {
|
||||
childColumns.push(...e.childColumns);
|
||||
for (let k = 0; k < e.childColumns.length; k++) {
|
||||
const item = e.childColumns[k];
|
||||
for (let j = 0; j < columns.length; j++) {
|
||||
const o = columns[j];
|
||||
if (o.prop == item && o.fixed !== 'left' && o.fixed !== 'right') e.children.push({ ...o });
|
||||
}
|
||||
}
|
||||
}
|
||||
if (e.children.length) firstChildColumns.push(e.children[0].prop);
|
||||
}
|
||||
complexHeaderList = complexHeaderList.filter(o => o.children.length);
|
||||
let list: any[] = [];
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
const e = columns[i];
|
||||
if (!childColumns.includes(e.prop) || e.fixed === 'left' || e.fixed === 'right') {
|
||||
list.push(e);
|
||||
} else {
|
||||
if (firstChildColumns.includes(e.prop)) {
|
||||
const item = complexHeaderList.find(o => o.childColumns.includes(e.prop));
|
||||
list.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
function getChildComplexColumns(columnList) {
|
||||
let list: any[] = [];
|
||||
for (let i = 0; i < columnList.length; i++) {
|
||||
const e = columnList[i];
|
||||
if (!e.prop.includes('-')) {
|
||||
list.push(e);
|
||||
} else {
|
||||
let prop = e.prop.split('-')[0];
|
||||
let vModel = e.prop.split('-')[1];
|
||||
let label = e.label.split('-')[0];
|
||||
let childLabel = e.label.replace(label + '-', '');
|
||||
if (e.fullNameI18nCode && Array.isArray(e.fullNameI18nCode) && e.fullNameI18nCode[0]) label = t(e.fullNameI18nCode[0], label);
|
||||
let newItem = {
|
||||
align: 'center',
|
||||
yunzhupaasKey: 'table',
|
||||
prop,
|
||||
label,
|
||||
title: label,
|
||||
dataIndex: prop,
|
||||
children: [],
|
||||
customCell: state.customCell || null,
|
||||
};
|
||||
e.dataIndex = vModel;
|
||||
e.title = e.__config__?.labelI18nCode ? t(e.__config__.labelI18nCode, childLabel) : childLabel;
|
||||
if (!state.expandObj.hasOwnProperty(`${prop}Expand`)) state.expandObj[`${prop}Expand`] = false;
|
||||
if (!list.some(o => o.prop === prop)) list.push(newItem);
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].prop === prop) {
|
||||
list[i].children.push(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (unref(getChildTableStyle) != 2) getMergeList(list);
|
||||
state.complexColumns = list;
|
||||
state.childColumnList = list.filter(o => o.yunzhupaasKey === 'table');
|
||||
}
|
||||
function getMergeList(list) {
|
||||
list.forEach(item => {
|
||||
if (item.yunzhupaasKey === 'table' && item.children && item.children.length) {
|
||||
item.children.forEach((child, index) => {
|
||||
if (index == 0) {
|
||||
child.customCell = (record, rowIndex, column) => ({
|
||||
...(state.customCell ? state.customCell(record, rowIndex, column) : {}),
|
||||
...{
|
||||
rowspan: 1,
|
||||
colspan: item.children.length,
|
||||
class: 'child-table-box',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
child.customCell = () => ({
|
||||
rowspan: 0,
|
||||
colspan: 0,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
function toggleExpand(row, field) {
|
||||
row[field] = !row[field];
|
||||
}
|
||||
function handleColumnChange(data) {
|
||||
state.columnSettingList = data;
|
||||
}
|
||||
function handleSearchSubmit(data) {
|
||||
let obj = {};
|
||||
for (let [key, value] of Object.entries(data)) {
|
||||
if (value) {
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length) obj[key] = value;
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
searchInfo.queryJson = JSON.stringify(obj) === '{}' ? '' : JSON.stringify(obj);
|
||||
reload({ page: 1 });
|
||||
}
|
||||
function handleSearchReset() {
|
||||
searchFormSubmit();
|
||||
}
|
||||
function unLock() {
|
||||
if (!state.password) return createMessage.error(t('views.dynamicModel.passwordPlaceholder'));
|
||||
state.btnLoading = true;
|
||||
const query = {
|
||||
id: state.shortLinkId,
|
||||
type: 1,
|
||||
encryption: props.config.encryption,
|
||||
password: encryptByMd5(state.password),
|
||||
};
|
||||
checkPwd(query)
|
||||
.then(() => {
|
||||
state.btnLoading = false;
|
||||
state.columnPassUse = 0;
|
||||
init();
|
||||
})
|
||||
.catch(() => {
|
||||
state.btnLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getConfig(props.modelId, props.config.encryption).then(res => {
|
||||
state.columnLink = res.data.columnLink || '';
|
||||
state.shortLinkId = res.data.id || '';
|
||||
state.columnPassUse = res.data.columnPassUse || 0;
|
||||
state.realSearchList = res.data.columnCondition ? JSON.parse(res.data.columnCondition) : [];
|
||||
state.realColumnList = res.data.columnText ? JSON.parse(res.data.columnText) : [];
|
||||
if (state.columnPassUse) return;
|
||||
init();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
Reference in New Issue
Block a user