初始代码

This commit is contained in:
wangmingwei
2026-04-21 17:48:26 +08:00
parent d3631949e9
commit 7f9e424a5c
1822 changed files with 288292 additions and 0 deletions

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

File diff suppressed because it is too large Load Diff

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>