初始版本提交

This commit is contained in:
yaoyn
2024-02-05 09:15:37 +08:00
parent b52d4414be
commit 445292105f
1848 changed files with 236859 additions and 75 deletions

View File

@ -0,0 +1,10 @@
import { useAppProviderContext } from '/@/components/Application';
import { computed, unref } from 'vue';
export function useAppInject() {
const values = useAppProviderContext();
return {
getIsMobile: computed(() => unref(values.isMobile)),
};
}

View File

@ -0,0 +1,190 @@
import { ComputedRef, isRef, nextTick, Ref, ref, unref, watch } from 'vue';
import { onMountedOrActivated } from '/@/hooks/core/onMountedOrActivated';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { useLayoutHeight } from '/@/layouts/default/content/useContentViewHeight';
import { getViewportOffset } from '/@/utils/domUtils';
import { isNumber, isString } from '/@/utils/is';
export interface CompensationHeight {
// 使用 layout Footer 高度作为判断补偿高度的条件
useLayoutFooter: boolean;
// refs HTMLElement
elements?: Ref[];
}
type Upward = number | string | null | undefined;
/**
* 动态计算内容高度根据锚点dom最下坐标到屏幕最下坐标根据传入dom的高度、padding、margin等值进行动态计算
* 最终获取合适的内容高度
*
* @param flag 用于开启计算的响应式标识
* @param anchorRef 锚点组件 Ref<ElRef | ComponentRef>
* @param subtractHeightRefs 待减去高度的组件列表 Ref<ElRef | ComponentRef>
* @param substractSpaceRefs 待减去空闲空间(margins/paddings)的组件列表 Ref<ElRef | ComponentRef>
* @param offsetHeightRef 计算偏移的响应式高度,计算高度时将直接减去此值
* @param upwardSpace 向上递归减去空闲空间的 层级 或 直到指定class为止 数值为2代表向上递归两次|数值为ant-layout表示向上递归直到碰见.ant-layout为止
* @returns 响应式高度
*/
export function useContentHeight(
flag: ComputedRef<Boolean>,
anchorRef: Ref,
subtractHeightRefs: Ref[],
substractSpaceRefs: Ref[],
upwardSpace: Ref<Upward> | ComputedRef<Upward> | Upward = 0,
offsetHeightRef: Ref<number> = ref(0),
) {
const contentHeight: Ref<Nullable<number>> = ref(null);
const { footerHeightRef: layoutFooterHeightRef } = useLayoutHeight();
let compensationHeight: CompensationHeight = {
useLayoutFooter: true,
};
const setCompensation = (params: CompensationHeight) => {
compensationHeight = params;
};
function redoHeight() {
nextTick(() => {
calcContentHeight();
});
}
function calcSubtractSpace(
element: Element | null | undefined,
direction: 'all' | 'top' | 'bottom' = 'all',
): number {
function numberPx(px: string) {
return Number(px.replace(/[^\d]/g, ''));
}
let subtractHeight = 0;
const ZERO_PX = '0px';
if (element) {
const cssStyle = getComputedStyle(element);
const marginTop = numberPx(cssStyle?.marginTop ?? ZERO_PX);
const marginBottom = numberPx(cssStyle?.marginBottom ?? ZERO_PX);
const paddingTop = numberPx(cssStyle?.paddingTop ?? ZERO_PX);
const paddingBottom = numberPx(cssStyle?.paddingBottom ?? ZERO_PX);
if (direction === 'all') {
subtractHeight += marginTop;
subtractHeight += marginBottom;
subtractHeight += paddingTop;
subtractHeight += paddingBottom;
} else if (direction === 'top') {
subtractHeight += marginTop;
subtractHeight += paddingTop;
} else {
subtractHeight += marginBottom;
subtractHeight += paddingBottom;
}
}
return subtractHeight;
}
function getEl(element: any): Nullable<HTMLDivElement> {
if (element == null) {
return null;
}
return (element instanceof HTMLDivElement ? element : element.$el) as HTMLDivElement;
}
async function calcContentHeight() {
if (!flag.value) {
return;
}
// Add a delay to get the correct height
await nextTick();
const anchorEl = getEl(unref(anchorRef));
if (!anchorEl) {
return;
}
const { bottomIncludeBody } = getViewportOffset(anchorEl);
// substract elements height
let substractHeight = 0;
subtractHeightRefs.forEach((item) => {
substractHeight += getEl(unref(item))?.offsetHeight ?? 0;
});
// subtract margins / paddings
let substractSpaceHeight = calcSubtractSpace(anchorEl) ?? 0;
substractSpaceRefs.forEach((item) => {
substractSpaceHeight += calcSubtractSpace(getEl(unref(item)));
});
// upwardSpace
let upwardSpaceHeight = 0;
function upward(element: Element | null, upwardLvlOrClass: number | string | null | undefined) {
if (element && upwardLvlOrClass) {
const parent = element.parentElement;
if (parent) {
if (isString(upwardLvlOrClass)) {
if (!parent.classList.contains(upwardLvlOrClass)) {
upwardSpaceHeight += calcSubtractSpace(parent, 'bottom');
upward(parent, upwardLvlOrClass);
} else {
upwardSpaceHeight += calcSubtractSpace(parent, 'bottom');
}
} else if (isNumber(upwardLvlOrClass)) {
if (upwardLvlOrClass > 0) {
upwardSpaceHeight += calcSubtractSpace(parent, 'bottom');
upward(parent, --upwardLvlOrClass);
}
}
}
}
}
if (isRef(upwardSpace)) {
upward(anchorEl, unref(upwardSpace));
} else {
upward(anchorEl, upwardSpace);
}
let height =
bottomIncludeBody -
unref(layoutFooterHeightRef) -
unref(offsetHeightRef) -
substractHeight -
substractSpaceHeight -
upwardSpaceHeight;
// compensation height
const calcCompensationHeight = () => {
compensationHeight.elements?.forEach((item) => {
height += getEl(unref(item))?.offsetHeight ?? 0;
});
};
if (compensationHeight.useLayoutFooter && unref(layoutFooterHeightRef) > 0) {
calcCompensationHeight();
} else {
calcCompensationHeight();
}
contentHeight.value = height;
}
onMountedOrActivated(() => {
nextTick(() => {
calcContentHeight();
});
});
useWindowSizeFn(
() => {
calcContentHeight();
},
50,
{ immediate: true },
);
watch(
() => [layoutFooterHeightRef.value],
() => {
calcContentHeight();
},
{
flush: 'post',
immediate: true,
},
);
return { redoHeight, setCompensation, contentHeight };
}

View File

@ -0,0 +1,12 @@
import { onUnmounted, getCurrentInstance } from 'vue';
import { createContextMenu, destroyContextMenu } from '/@/components/ContextMenu';
import type { ContextMenuItem } from '/@/components/ContextMenu';
export type { ContextMenuItem };
export function useContextMenu(authRemove = true) {
if (getCurrentInstance() && authRemove) {
onUnmounted(() => {
destroyContextMenu();
});
}
return [createContextMenu, destroyContextMenu];
}

View File

@ -0,0 +1,69 @@
import { ref, watch } from 'vue';
import { isDef } from '/@/utils/is';
interface Options {
target?: HTMLElement;
}
export function useCopyToClipboard(initial?: string) {
const clipboardRef = ref(initial || '');
const isSuccessRef = ref(false);
const copiedRef = ref(false);
watch(
clipboardRef,
(str?: string) => {
if (isDef(str)) {
copiedRef.value = true;
isSuccessRef.value = copyTextToClipboard(str);
}
},
{ immediate: !!initial, flush: 'sync' },
);
return { clipboardRef, isSuccessRef, copiedRef };
}
export function copyTextToClipboard(input: string, { target = document.body }: Options = {}) {
const element = document.createElement('textarea');
const previouslyFocusedElement = document.activeElement;
element.value = input;
element.setAttribute('readonly', '');
(element.style as any).contain = 'strict';
element.style.position = 'absolute';
element.style.left = '-9999px';
element.style.fontSize = '12pt';
const selection = document.getSelection();
let originalRange;
if (selection && selection.rangeCount > 0) {
originalRange = selection.getRangeAt(0);
}
target.append(element);
element.select();
element.selectionStart = 0;
element.selectionEnd = input.length;
let isSuccess = false;
try {
isSuccess = document.execCommand('copy');
} catch (e: any) {
throw new Error(e);
}
element.remove();
if (originalRange && selection) {
selection.removeAllRanges();
selection.addRange(originalRange);
}
if (previouslyFocusedElement) {
(previouslyFocusedElement as HTMLElement).focus();
}
return isSuccess;
}

View File

@ -0,0 +1,22 @@
import { useAppProviderContext } from '/@/components/Application';
// import { computed } from 'vue';
// import { lowerFirst } from 'lodash-es';
export function useDesign(scope: string) {
const values = useAppProviderContext();
// const $style = cssModule ? useCssModule() : {};
// const style: Record<string, string> = {};
// if (cssModule) {
// Object.keys($style).forEach((key) => {
// // const moduleCls = $style[key];
// const k = key.replace(new RegExp(`^${values.prefixCls}-?`, 'ig'), '');
// style[lowerFirst(k)] = $style[key];
// });
// }
return {
// prefixCls: computed(() => `${values.prefixCls}-${scope}`),
prefixCls: `${values.prefixCls}-${scope}`,
prefixVar: values.prefixCls,
// style,
};
}

116
src/hooks/web/useECharts.ts Normal file
View File

@ -0,0 +1,116 @@
import type { EChartsOption } from 'echarts';
import type { Ref } from 'vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
import { tryOnUnmounted } from '@vueuse/core';
import { unref, nextTick, watch, computed, ref } from 'vue';
import { useDebounceFn } from '@vueuse/core';
import { useEventListener } from '/@/hooks/event/useEventListener';
import { useBreakpoint } from '/@/hooks/event/useBreakpoint';
import echarts from '/@/utils/lib/echarts';
import { useRootSetting } from '/@/hooks/setting/useRootSetting';
export function useECharts(
elRef: Ref<HTMLDivElement>,
theme: 'light' | 'dark' | 'default' = 'default',
) {
const { getDarkMode: getSysDarkMode } = useRootSetting();
const getDarkMode = computed(() => {
return theme === 'default' ? getSysDarkMode.value : theme;
});
let chartInstance: echarts.ECharts | null = null;
let resizeFn: Fn = resize;
const cacheOptions = ref({}) as Ref<EChartsOption>;
let removeResizeFn: Fn = () => {};
resizeFn = useDebounceFn(resize, 200);
const getOptions = computed(() => {
if (getDarkMode.value !== 'dark') {
return cacheOptions.value as EChartsOption;
}
return {
backgroundColor: 'transparent',
...cacheOptions.value,
} as EChartsOption;
});
function initCharts(t = theme) {
const el = unref(elRef);
if (!el || !unref(el)) {
return;
}
chartInstance = echarts.init(el, t);
const { removeEvent } = useEventListener({
el: window,
name: 'resize',
listener: resizeFn,
});
removeResizeFn = removeEvent;
const { widthRef, screenEnum } = useBreakpoint();
if (unref(widthRef) <= screenEnum.MD || el.offsetHeight === 0) {
useTimeoutFn(() => {
resizeFn();
}, 30);
}
}
function setOptions(options: EChartsOption, clear = true) {
cacheOptions.value = options;
if (unref(elRef)?.offsetHeight === 0) {
useTimeoutFn(() => {
setOptions(unref(getOptions));
}, 30);
return;
}
nextTick(() => {
useTimeoutFn(() => {
if (!chartInstance) {
initCharts(getDarkMode.value as 'default');
if (!chartInstance) return;
}
clear && chartInstance?.clear();
chartInstance?.setOption(unref(getOptions));
}, 30);
});
}
function resize() {
chartInstance?.resize();
}
watch(
() => getDarkMode.value,
(theme) => {
if (chartInstance) {
chartInstance.dispose();
initCharts(theme as 'default');
setOptions(cacheOptions.value);
}
},
);
tryOnUnmounted(() => {
if (!chartInstance) return;
removeResizeFn();
chartInstance.dispose();
chartInstance = null;
});
function getInstance(): echarts.ECharts | null {
if (!chartInstance) {
initCharts(getDarkMode.value as 'default');
}
return chartInstance;
}
return {
setOptions,
resize,
echarts,
getInstance,
};
}

View File

@ -0,0 +1,129 @@
import { apiConfigFunc } from '/@/utils/event/design';
import { execute } from '/@/api/liteflow';
export enum EventType {
CREATE = 0, //初始化表单
GET, // 获取表单数据
LOAD, //加载表单
SUBMIT, //提交表单
}
//初始化表单
export async function createFormEvent(
formEventConfig,
formModels,
formRef,
schemas,
isCustomForm = false,
) {
try {
if (
formEventConfig &&
formEventConfig[EventType.CREATE] &&
Array.isArray(formEventConfig[EventType.CREATE])
) {
await executeFormEvent(
formEventConfig[EventType.CREATE],
formModels,
formRef,
schemas,
isCustomForm,
);
}
} catch (error) {}
}
//获取表单数据
export async function getFormDataEvent(
formEventConfig,
formModels,
formRef,
schemas,
isCustomForm = false,
) {
try {
if (
formEventConfig &&
formEventConfig[EventType.GET] &&
Array.isArray(formEventConfig[EventType.GET])
) {
await executeFormEvent(
formEventConfig[EventType.GET],
formModels,
formRef,
schemas,
isCustomForm,
);
}
} catch (error) {}
}
//加载表单
export async function loadFormEvent(
formEventConfig,
formModels,
formRef,
schemas,
isCustomForm = false,
) {
try {
if (
formEventConfig &&
formEventConfig[EventType.LOAD] &&
Array.isArray(formEventConfig[EventType.LOAD])
) {
await executeFormEvent(
formEventConfig[EventType.LOAD],
formModels,
formRef,
schemas,
isCustomForm,
);
}
} catch (error) {}
}
//提交表单
export async function submitFormEvent(
formEventConfig,
formModels,
formRef,
schemas,
isCustomForm = false,
) {
try {
if (
formEventConfig &&
formEventConfig[EventType.SUBMIT] &&
Array.isArray(formEventConfig[EventType.SUBMIT])
) {
await executeFormEvent(
formEventConfig[EventType.SUBMIT],
formModels,
formRef,
schemas,
isCustomForm,
);
}
} catch (error) {}
}
export async function executeFormEvent(
formEventConfig,
formModel,
formRef,
schemas,
isCustomForm = false,
) {
if (!formEventConfig.length) return;
formEventConfig.map((x) => {
x.nodeInfo?.processEvent?.map(async (config) => {
if (config.operateType === 'api') {
await apiConfigFunc(config.operateConfig, isCustomForm, formModel);
} else if (config.operateType === 'liteflow') {
await execute(config.operateConfig, formModel);
} else if (config.operateType === 'js') {
const event = new Function('schema', 'formModel', 'formActionType', config.operateConfig);
event(schemas, formModel, formRef);
}
});
});
}

View File

@ -0,0 +1,28 @@
import { computed, unref } from 'vue';
import { useAppStore } from '/@/store/modules/app';
import { useRouter } from 'vue-router';
/**
* @description: Full screen display content
*/
export const useFullContent = () => {
const appStore = useAppStore();
const router = useRouter();
const { currentRoute } = router;
// Whether to display the content in full screen without displaying the menu
const getFullContent = computed(() => {
// Query parameters, the full screen is displayed when the address bar has a full parameter
const route = unref(currentRoute);
const query = route.query;
if (query && Reflect.has(query, '__full__')) {
return true;
}
// Return to the configuration in the configuration file
return appStore.getProjectConfig.fullContent;
});
return { getFullContent };
};

56
src/hooks/web/useI18n.ts Normal file
View File

@ -0,0 +1,56 @@
import { i18n } from '/@/locales/setupI18n';
type I18nGlobalTranslation = {
(key: string): string;
(key: string, locale: string): string;
(key: string, locale: string, list: unknown[]): string;
(key: string, locale: string, named: Record<string, unknown>): string;
(key: string, list: unknown[]): string;
(key: string, named: Record<string, unknown>): string;
};
type I18nTranslationRestParameters = [string, any];
function getKey(namespace: string | undefined, key: string) {
if (!namespace) {
return key;
}
if (key.startsWith(namespace)) {
return key;
}
return `${namespace}.${key}`;
}
export function useI18n(namespace?: string): {
t: I18nGlobalTranslation;
} {
const normalFn = {
t: (key: string) => {
return getKey(namespace, key);
},
};
if (!i18n) {
return normalFn;
}
const { t, ...methods } = i18n.global;
const tFn: I18nGlobalTranslation = (key: string, ...arg: any[]) => {
if (!key) return '';
//TODO 后端获取翻译 注释下一行代码
// if (!key.includes('.') && !namespace) return key;
return t(getKey(namespace, key), ...(arg as I18nTranslationRestParameters));
};
return {
...methods,
t: tFn,
};
}
// Why write this function
// Mainly to configure the vscode i18nn ally plugin. This function is only used for routing and menus. Please use useI18n for other places
// 为什么要编写此函数?
// 主要用于配合vscode i18nn ally插件。此功能仅用于路由和菜单。请在其他地方使用useI18n
export const t = (key: string) => key;

View File

@ -0,0 +1,72 @@
import { computed, onUnmounted, unref, watchEffect } from 'vue';
import { useThrottleFn } from '@vueuse/core';
import { useAppStore } from '/@/store/modules/app';
import { useLockStore } from '/@/store/modules/lock';
import { useUserStore } from '/@/store/modules/user';
import { useRootSetting } from '../setting/useRootSetting';
export function useLockPage() {
const { getLockTime } = useRootSetting();
const lockStore = useLockStore();
const userStore = useUserStore();
const appStore = useAppStore();
let timeId: TimeoutHandle;
function clear(): void {
window.clearTimeout(timeId);
}
function resetCalcLockTimeout(): void {
// not login
if (!userStore.getToken) {
clear();
return;
}
const lockTime = appStore.getProjectConfig.lockTime;
if (!lockTime || lockTime < 1) {
clear();
return;
}
clear();
timeId = setTimeout(() => {
lockPage();
}, lockTime * 60 * 1000);
}
function lockPage(): void {
lockStore.setLockInfo({
isLock: true,
pwd: undefined,
});
}
watchEffect((onClean) => {
if (userStore.getToken) {
resetCalcLockTimeout();
} else {
clear();
}
onClean(() => {
clear();
});
});
onUnmounted(() => {
clear();
});
const keyupFn = useThrottleFn(resetCalcLockTimeout, 2000);
return computed(() => {
if (unref(getLockTime)) {
return { onKeyup: keyupFn, onMousemove: keyupFn };
} else {
clear();
return {};
}
});
}

View File

@ -0,0 +1,123 @@
import type { ModalFunc, ModalFuncProps } from 'ant-design-vue/lib/modal/Modal';
import { Modal, message as Message, notification } from 'ant-design-vue';
import { InfoCircleFilled, CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons-vue';
import { NotificationArgsProps, ConfigProps } from 'ant-design-vue/lib/notification';
import { useI18n } from './useI18n';
import { isString } from '/@/utils/is';
export interface NotifyApi {
info(config: NotificationArgsProps): void;
success(config: NotificationArgsProps): void;
error(config: NotificationArgsProps): void;
warn(config: NotificationArgsProps): void;
warning(config: NotificationArgsProps): void;
open(args: NotificationArgsProps): void;
close(key: String): void;
config(options: ConfigProps): void;
destroy(): void;
}
export declare type NotificationPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
export declare type IconType = 'success' | 'info' | 'error' | 'warning';
export interface ModalOptionsEx extends Omit<ModalFuncProps, 'iconType'> {
iconType: 'warning' | 'success' | 'error' | 'info';
}
export type ModalOptionsPartial = Partial<ModalOptionsEx> & Pick<ModalOptionsEx, 'content'>;
interface ConfirmOptions {
info: ModalFunc;
success: ModalFunc;
error: ModalFunc;
warn: ModalFunc;
warning: ModalFunc;
}
function getIcon(iconType: string) {
if (iconType === 'warning') {
return <InfoCircleFilled class="modal-icon-warning" />;
} else if (iconType === 'success') {
return <CheckCircleFilled class="modal-icon-success" />;
} else if (iconType === 'info') {
return <InfoCircleFilled class="modal-icon-info" />;
} else {
return <CloseCircleFilled class="modal-icon-error" />;
}
}
function renderContent({ content }: Pick<ModalOptionsEx, 'content'>) {
if (isString(content)) {
return <div innerHTML={`<div>${content as string}</div>`}></div>;
} else {
return content;
}
}
/**
* @description: Create confirmation box
*/
function createConfirm(options: ModalOptionsEx): ConfirmOptions {
const iconType = options.iconType || 'warning';
Reflect.deleteProperty(options, 'iconType');
const opt: ModalFuncProps = {
centered: true,
icon: getIcon(iconType),
...options,
content: renderContent(options),
};
return Modal.confirm(opt) as unknown as ConfirmOptions;
}
const getBaseOptions = () => {
const { t } = useI18n();
return {
okText: t('确认'),
centered: true,
};
};
function createModalOptions(options: ModalOptionsPartial, icon: string): ModalOptionsPartial {
return {
...getBaseOptions(),
...options,
content: renderContent(options),
icon: getIcon(icon),
};
}
function createSuccessModal(options: ModalOptionsPartial) {
return Modal.success(createModalOptions(options, 'success'));
}
function createErrorModal(options: ModalOptionsPartial) {
return Modal.error(createModalOptions(options, 'close'));
}
function createInfoModal(options: ModalOptionsPartial) {
return Modal.info(createModalOptions(options, 'info'));
}
function createWarningModal(options: ModalOptionsPartial) {
return Modal.warning(createModalOptions(options, 'warning'));
}
notification.config({
placement: 'topRight',
duration: 3,
});
/**
* @description: message
*/
export function useMessage() {
return {
createMessage: Message,
notification: notification as NotifyApi,
createConfirm: createConfirm,
createSuccessModal,
createErrorModal,
createInfoModal,
createWarningModal,
};
}

62
src/hooks/web/usePage.ts Normal file
View File

@ -0,0 +1,62 @@
import type { RouteLocationRaw, Router } from 'vue-router';
import { PageEnum } from '/@/enums/pageEnum';
import { isString } from '/@/utils/is';
import { unref } from 'vue';
import { useRouter } from 'vue-router';
import { REDIRECT_NAME } from '/@/router/constant';
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
function handleError(e: Error) {
console.error(e);
}
// page switch
export function useGo(_router?: Router) {
let router;
if (!_router) {
router = useRouter();
}
const { push, replace } = _router || router;
function go(opt: PageEnum | RouteLocationRawEx | string = PageEnum.BASE_HOME, isReplace = false) {
if (!opt) {
return;
}
if (isString(opt)) {
isReplace ? replace(opt).catch(handleError) : push(opt).catch(handleError);
} else {
const o = opt as RouteLocationRaw;
isReplace ? replace(o).catch(handleError) : push(o).catch(handleError);
}
}
return go;
}
/**
* @description: redo current page
*/
export const useRedo = (_router?: Router) => {
const { push, currentRoute } = _router || useRouter();
const { query, params = {}, name, fullPath } = unref(currentRoute.value);
function redo(): Promise<boolean> {
return new Promise((resolve) => {
if (name === REDIRECT_NAME) {
resolve(false);
return;
}
if (name && Object.keys(params).length > 0) {
params['_redirect_type'] = 'name';
params['path'] = String(name);
} else {
params['_redirect_type'] = 'path';
params['path'] = fullPath;
}
push({ name: REDIRECT_NAME, params, query }).then(() => resolve(true));
});
}
return redo;
};

View File

@ -0,0 +1,34 @@
import type { Ref } from 'vue';
import { ref, unref, computed } from 'vue';
function pagination<T = any>(list: T[], pageNo: number, pageSize: number): T[] {
const offset = (pageNo - 1) * Number(pageSize);
const ret =
offset + Number(pageSize) >= list.length
? list.slice(offset, list.length)
: list.slice(offset, offset + Number(pageSize));
return ret;
}
export function usePagination<T = any>(list: Ref<T[]>, pageSize: number) {
const currentPage = ref(1);
const pageSizeRef = ref(pageSize);
const getPaginationList = computed(() => {
return pagination(unref(list), unref(currentPage), unref(pageSizeRef));
});
const getTotal = computed(() => {
return unref(list).length;
});
function setCurrentPage(page: number) {
currentPage.value = page;
}
function setPageSize(pageSize: number) {
pageSizeRef.value = pageSize;
}
return { setCurrentPage, getTotal, setPageSize, getPaginationList };
}

View File

@ -0,0 +1,279 @@
import { RouteRecordRaw } from 'vue-router';
import { useAppStore } from '/@/store/modules/app';
import { usePermissionStore } from '/@/store/modules/permission';
import { useUserStore } from '/@/store/modules/user';
import { useTabs } from './useTabs';
import { router, resetRouter } from '/@/router';
// import { RootRoute } from '/@/router/routes';
import projectSetting from '/@/settings/projectSetting';
import { PermissionModeEnum } from '/@/enums/appEnum';
import { RoleEnum } from '/@/enums/roleEnum';
import { intersection } from 'lodash-es';
import { isArray } from '/@/utils/is';
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
import { MenuAuthModel } from '/@/api/system/login/model';
import { BasicColumn, FormSchema } from '/@/components/Table';
import { toRaw } from 'vue';
import { camelCaseString } from '/@/utils/event/design';
import { ButtonConfig } from '/@/model/generator/listConfig';
// User permissions related operations
export function usePermission() {
const userStore = useUserStore();
const appStore = useAppStore();
//获取到当前路由
const currentRoute = router.currentRoute;
const permissionStore = usePermissionStore();
const { closeAll } = useTabs(router);
/**
* Change permission mode
*/
async function togglePermissionMode() {
appStore.setProjectConfig({
permissionMode:
projectSetting.permissionMode === PermissionModeEnum.BACK
? PermissionModeEnum.ROUTE_MAPPING
: PermissionModeEnum.BACK,
});
location.reload();
}
/**
* Reset and regain authority resource information
* @param id
*/
async function resume() {
const tabStore = useMultipleTabStore();
tabStore.clearCacheTabs();
resetRouter();
const routes = await permissionStore.buildRoutesAction();
routes.forEach((route) => {
router.addRoute(route as unknown as RouteRecordRaw);
});
permissionStore.setLastBuildMenuTime();
closeAll();
}
/**
* Determine whether there is permission
*/
function hasPermission(value?: RoleEnum | RoleEnum[] | string | string[], def = true): boolean {
// Visible by default
if (!value) {
return def;
}
const permMode = projectSetting.permissionMode;
if ([PermissionModeEnum.ROUTE_MAPPING, PermissionModeEnum.ROLE].includes(permMode)) {
if (!isArray(value)) {
return userStore.getRoleList?.includes(value as RoleEnum);
}
return (intersection(value, userStore.getRoleList) as RoleEnum[]).length > 0;
}
if (PermissionModeEnum.BACK === permMode) {
const allPerm = permissionStore.getPermCodeList as MenuAuthModel[];
const { menuId } = currentRoute.value.meta;
const thisMenuAuth = allPerm.find((p) => p.menuId === menuId);
return thisMenuAuth?.buttonAuthCode?.includes(value as string) || false;
// if (!isArray(allPerm)) {
// const { menuId } = currentRoute.value.meta;
// const auth = allPerm.find((p) => p.menuId === menuId);
// return allCodeList.includes(value);
// }
// return (intersection(value, allCodeList) as string[]).length > 0;
}
return true;
}
/**
* Change roles
* @param roles
*/
async function changeRole(roles: RoleEnum | RoleEnum[]): Promise<void> {
if (projectSetting.permissionMode !== PermissionModeEnum.ROUTE_MAPPING) {
throw new Error(
'Please switch PermissionModeEnum to ROUTE_MAPPING mode in the configuration to operate!',
);
}
if (!isArray(roles)) {
roles = [roles];
}
userStore.setRoleList(roles);
await resume();
}
/**
* refresh menu data
*/
async function refreshMenu() {
resume();
}
/**
* refresh menu data
*/
async function changeMenu() {}
/**
* button
* @param buttonList 未过滤的
* @returns 过滤后的
*/
function filterButtonAuth(buttonList: ButtonConfig[]) {
//获取到当前菜单id
const menuId = currentRoute.value.meta.menuId;
const perm = permissionStore.getPermCodeList.find((x) => x.menuId === menuId);
if (!perm) return buttonList;
let buttonAuth = perm?.buttonAuthCode || [];
if (!buttonAuth || buttonAuth?.length === 0) {
return [];
}
buttonAuth = buttonAuth.map((x) => {
const btnList = x.split(':');
btnList.shift();
return btnList.join('');
});
const result = buttonList.filter((btn) => buttonAuth.includes(btn.code));
return result;
}
/**
* BasicColumn
* @param columns 未过滤的
* @param convertCamel 是否需要转驼峰
* @returns 过滤后的
*/
function filterColumnAuth(columns: BasicColumn[], convertCamel = false): BasicColumn[] {
//获取到当前菜单id
const menuId = currentRoute.value.meta.menuId;
const perm = permissionStore.getPermCodeList.find((x) => x.menuId === menuId);
if (!perm) return columns;
let columnAuth = perm?.columnAuthCode || [];
if (!columnAuth || columnAuth?.length === 0) {
return [];
}
if (convertCamel) {
columnAuth = columnAuth.map((x) => camelCaseString(x)!);
}
const result = columns.filter(
(col) => col.dataIndex && toRaw(columnAuth).includes(col.dataIndex as string),
);
return result;
}
/**
* FormSchema权限过滤
* @param formSchema 未过滤的FormSchema
* @param convertCamel 是否需要转换驼峰
* @returns 返回过滤过的FormSchema
*/
function filterFormSchemaAuth(formSchema: FormSchema[], convertCamel = false): FormSchema[] {
const menuId = currentRoute.value.meta.menuId;
const perm = permissionStore.getPermCodeList.find((x) => x.menuId === menuId);
if (!perm) return formSchema;
let formAuth: any[] = perm?.formAuthCode || [];
if (!formAuth || formAuth?.length === 0) {
return [];
}
if (convertCamel) {
formAuth = formAuth.map((x) => {
if (typeof x === 'object' && !!Object.keys(x)[0]) {
const subformField = camelCaseString(Object.keys(x)[0])!;
x[subformField!] = x[subformField!]?.map((field) => camelCaseString(field));
return {
[subformField]: x[subformField!],
};
} else {
return camelCaseString(x)!;
}
});
}
const result = formSchema.filter((schema) => {
return findLayoutComponent(schema, formAuth);
});
return result;
}
function findLayoutComponent(schema, formAuth) {
if (['card', 'tab', 'grid'].includes(schema.type)) {
if (schema.children && schema.children.length > 0) {
return schema.children.filter((children) => {
children.list = children.list.filter((x) => findLayoutComponent(x, formAuth));
return children.list;
});
}
} else if (schema.type === 'table-layout') {
if (schema.children && schema.children.length > 0) {
return schema.children.filter((children) => {
children.list = children.list.filter((chil) => {
chil.children = chil.children.filter((x) => findLayoutComponent(x, formAuth));
return chil.children;
});
return children.list;
});
}
} else if (schema.type === 'form') {
if (schema.componentProps.columns && schema.componentProps.columns.length) {
const subformField = schema.field.substring(0, schema.field.length - 4);
const currentSubFormAuth = formAuth.find((x) => {
return typeof x === 'object' && Object.keys(x)[0] === subformField;
});
if (!currentSubFormAuth) {
return false;
} else {
schema.componentProps.columns = schema.componentProps.columns.filter((x) => {
return x.componentType
? findLayoutComponent(x, Object.values(currentSubFormAuth)[0])
: true;
});
return true;
}
}
} else if (schema.type === 'one-for-one') {
if (schema.componentProps.childSchemas && schema.componentProps.childSchemas.length) {
const subformField = schema.field.substring(0, schema.field.length - 4);
const currentSubFormAuth = formAuth.find((x) => {
return typeof x === 'object' && Object.keys(x)[0] === subformField;
});
if (!currentSubFormAuth) {
return false;
} else {
schema.componentProps.childSchemas = schema.componentProps.childSchemas.filter((x) => {
return x.component
? findLayoutComponent(x, Object.values(currentSubFormAuth)[0])
: true;
});
return true;
}
}
} else {
const field = schema.field || schema.dataIndex || schema.key;
return formAuth?.includes(field);
}
}
return {
changeRole,
hasPermission,
togglePermissionMode,
refreshMenu,
changeMenu,
filterButtonAuth,
filterColumnAuth,
filterFormSchemaAuth,
};
}

View File

@ -0,0 +1,46 @@
import { onMounted, onUnmounted, ref } from 'vue';
interface ScriptOptions {
src: string;
}
export function useScript(opts: ScriptOptions) {
const isLoading = ref(false);
const error = ref(false);
const success = ref(false);
let script: HTMLScriptElement;
const promise = new Promise((resolve, reject) => {
onMounted(() => {
script = document.createElement('script');
script.type = 'text/javascript';
script.onload = function () {
isLoading.value = false;
success.value = true;
error.value = false;
resolve('');
};
script.onerror = function (err) {
isLoading.value = false;
success.value = false;
error.value = true;
reject(err);
};
script.src = opts.src;
document.head.appendChild(script);
});
});
onUnmounted(() => {
script && script.remove();
});
return {
isLoading,
error,
success,
toPromise: () => promise,
};
}

View File

@ -0,0 +1,21 @@
import { nextTick, unref } from 'vue';
import type { Ref } from 'vue';
import type { Options } from 'sortablejs';
export function useSortable(el: HTMLElement | Ref<HTMLElement>, options?: Options) {
function initSortable() {
nextTick(async () => {
if (!el) return;
const Sortable = (await import('sortablejs')).default;
Sortable.create(unref(el), {
animation: 500,
delay: 400,
delayOnTouchOnly: true,
...options,
});
});
}
return { initSortable };
}

103
src/hooks/web/useTabs.ts Normal file
View File

@ -0,0 +1,103 @@
import type { RouteLocationNormalized, Router } from 'vue-router';
import { useRouter } from 'vue-router';
import { unref } from 'vue';
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
import { useAppStore } from '/@/store/modules/app';
enum TableActionEnum {
REFRESH,
CLOSE_ALL,
CLOSE_LEFT,
CLOSE_RIGHT,
CLOSE_OTHER,
CLOSE_CURRENT,
CLOSE,
}
export function useTabs(_router?: Router) {
const appStore = useAppStore();
function canIUseTabs(): boolean {
const { show } = appStore.getMultiTabsSetting;
if (!show) {
throw new Error('The multi-tab page is currently not open, please open it in the settings');
}
return !!show;
}
const tabStore = useMultipleTabStore();
const router = _router || useRouter();
const { currentRoute } = router;
function getCurrentTab() {
const route = unref(currentRoute);
return tabStore.getTabList.find((item) => item.fullPath === route.fullPath)!;
}
async function updateTabTitle(title: string, tab?: RouteLocationNormalized) {
const canIUse = canIUseTabs;
if (!canIUse) {
return;
}
const targetTab = tab || getCurrentTab();
await tabStore.setTabTitle(title, targetTab);
}
async function updateTabPath(path: string, tab?: RouteLocationNormalized) {
const canIUse = canIUseTabs;
if (!canIUse) {
return;
}
const targetTab = tab || getCurrentTab();
await tabStore.updateTabPath(path, targetTab);
}
async function handleTabAction(action: TableActionEnum, tab?: RouteLocationNormalized) {
const canIUse = canIUseTabs;
if (!canIUse) {
return;
}
const currentTab = getCurrentTab();
switch (action) {
case TableActionEnum.REFRESH:
await tabStore.refreshPage(router);
break;
case TableActionEnum.CLOSE_ALL:
await tabStore.closeAllTab(router);
break;
case TableActionEnum.CLOSE_LEFT:
await tabStore.closeLeftTabs(currentTab, router);
break;
case TableActionEnum.CLOSE_RIGHT:
await tabStore.closeRightTabs(currentTab, router);
break;
case TableActionEnum.CLOSE_OTHER:
await tabStore.closeOtherTabs(currentTab, router);
break;
case TableActionEnum.CLOSE_CURRENT:
case TableActionEnum.CLOSE:
await tabStore.closeTab(tab || currentTab, router);
break;
}
}
return {
refreshPage: () => handleTabAction(TableActionEnum.REFRESH),
closeAll: () => handleTabAction(TableActionEnum.CLOSE_ALL),
closeLeft: () => handleTabAction(TableActionEnum.CLOSE_LEFT),
closeRight: () => handleTabAction(TableActionEnum.CLOSE_RIGHT),
closeOther: () => handleTabAction(TableActionEnum.CLOSE_OTHER),
closeCurrent: () => handleTabAction(TableActionEnum.CLOSE_CURRENT),
close: (tab?: RouteLocationNormalized) => handleTabAction(TableActionEnum.CLOSE, tab),
setTitle: (title: string, tab?: RouteLocationNormalized) => updateTabTitle(title, tab),
updatePath: (fullPath: string, tab?: RouteLocationNormalized) => updateTabPath(fullPath, tab),
};
}

37
src/hooks/web/useTitle.ts Normal file
View File

@ -0,0 +1,37 @@
import { watch, unref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { useTitle as usePageTitle } from '@vueuse/core';
import { useGlobSetting } from '/@/hooks/setting';
import { useRouter } from 'vue-router';
import { useLocaleStore } from '/@/store/modules/locale';
import { useAppStore } from '/@/store/modules/app';
import { REDIRECT_NAME } from '/@/router/constant';
/**
* Listening to page changes and dynamically changing site titles
*/
export function useTitle() {
const { title } = useGlobSetting();
const { t } = useI18n();
const { currentRoute } = useRouter();
const localeStore = useLocaleStore();
const appStore = useAppStore();
const pageTitle = usePageTitle();
watch(
[() => currentRoute.value.path, () => localeStore.getLocale],
() => {
const route = unref(currentRoute);
if (route.name === REDIRECT_NAME) {
return;
}
const tTitle = t(route?.meta?.title as string);
pageTitle.value = tTitle
? ` ${tTitle} - ${appStore.getLogoConfig.shortName || title} `
: `${appStore.getLogoConfig.shortName || title}`;
},
{ immediate: true },
);
}

View File

@ -0,0 +1,100 @@
import { getCurrentInstance, onBeforeUnmount, ref, Ref, shallowRef, unref } from 'vue';
import { useRafThrottle } from '/@/utils/domUtils';
import { addResizeListener, removeResizeListener } from '/@/utils/event';
import { isDef } from '/@/utils/is';
const domSymbol = Symbol('watermark-dom');
export function useWatermark(
appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement>,
) {
const func = useRafThrottle(function () {
const el = unref(appendEl);
if (!el) return;
const { clientHeight: height, clientWidth: width } = el;
updateWatermark({ height, width });
});
const id = domSymbol.toString();
const watermarkEl = shallowRef<HTMLElement>();
const clear = () => {
const domId = unref(watermarkEl);
watermarkEl.value = undefined;
const el = unref(appendEl);
if (!el) return;
domId && el.removeChild(domId);
removeResizeListener(el, func);
};
function createBase64(str: string) {
const can = document.createElement('canvas');
const width = 300;
const height = 240;
Object.assign(can, { width, height });
const cans = can.getContext('2d');
if (cans) {
cans.rotate((-20 * Math.PI) / 120);
cans.font = '15px Vedana';
cans.fillStyle = 'rgba(0, 0, 0, 0.15)';
cans.textAlign = 'left';
cans.textBaseline = 'middle';
cans.fillText(str, width / 20, height);
}
return can.toDataURL('image/png');
}
function updateWatermark(
options: {
width?: number;
height?: number;
str?: string;
} = {},
) {
const el = unref(watermarkEl);
if (!el) return;
if (isDef(options.width)) {
el.style.width = `${options.width}px`;
}
if (isDef(options.height)) {
el.style.height = `${options.height}px`;
}
if (isDef(options.str)) {
el.style.background = `url(${createBase64(options.str)}) left top repeat`;
}
}
const createWatermark = (str: string) => {
if (unref(watermarkEl)) {
updateWatermark({ str });
return id;
}
const div = document.createElement('div');
watermarkEl.value = div;
div.id = id;
div.style.pointerEvents = 'none';
div.style.top = '0px';
div.style.left = '0px';
div.style.position = 'absolute';
div.style.zIndex = '100000';
const el = unref(appendEl);
if (!el) return id;
const { clientHeight: height, clientWidth: width } = el;
updateWatermark({ str, width, height });
el.appendChild(div);
return id;
};
function setWatermark(str: string) {
createWatermark(str);
addResizeListener(document.documentElement, func);
const instance = getCurrentInstance();
if (instance) {
onBeforeUnmount(() => {
clear();
});
}
}
return { setWatermark, clear };
}

View File

@ -0,0 +1,499 @@
import { buildOption } from '/@/utils/helper/designHelper';
import { FormProps } from '/@/components/Form';
import { separator } from '/@/views/workflow/design/bpmn/config/info';
import { TaskApproveOpinion, WorkFlowFormParams } from '/@/model/workflow/bpmnConfig';
import { FormConfigItem } from '/@/model/workflow/formSetting';
import { FormSchema } from '/@/components/Form';
import { TableItem } from '/@/model/workflow/formSetting';
import { disableTypes, hiddenComponentType, requiredDisabled } from '/@bpmn/config/formPermission';
/*
**根据权限 修改JSON配置**
data{
formJson 表单json配置
formConfigChildren 工作流节点表单权限配置
formConfigKey 唯一节点表单key
opinions 意见簿意见
opinionsComponents 意见簿组件
}
isViewProcess 是否查看流程
uploadComponentIds: 所有上传文件的文件夹id 【因为流程需要统计每个节点的附件汇总】
*/
export function changeFormJson(
data: {
formJson: any; // 表单json配置 FormJson | FormProps
formConfigChildren: Array<FormConfigItem>; // 工作流节点表单权限配置
formConfigKey: String; //唯一节点表单key
opinions?: Array<TaskApproveOpinion> | undefined; //意见簿意见
opinionsComponents?: Array<string> | undefined; //意见簿组件
},
isViewProcess: Boolean,
uploadComponentIds: Array<string>,
) {
const returnData = handlerFormPermission(
data.formJson.list ? buildOption(data.formJson, false) : data.formJson,
data.formConfigChildren,
data.formConfigKey,
isViewProcess,
{ uploadComponentIds, opinions: data.opinions, opinionsComponents: data.opinionsComponents },
);
// uploadComponent 带上所有上传文件的文件夹id
return returnData;
}
// 获取工作流表单权限map
/*
返回Map
[
字段唯一Key:{
{
edit:是否可编辑权限,
view:是否可查看权限,
disabled:是否禁用权限,
children:[] 子表数据
}
}
]
*/
export function getPermissionConfigMap(formConfigChildren) {
const map = new Map();
formConfigChildren.forEach((element) => {
map.set(element.key, element);
});
return map;
}
function handlerFormPermission(
buildOptionJson: FormProps,
formConfigChildren,
formKey,
isViewProcess,
otherParams,
) {
const permissionConfigMap = getPermissionConfigMap(formConfigChildren);
const formShow: Number = 0; //表单是否有显示的组件有则大于0
buildOptionJson.schemas = schemeList(
buildOptionJson.schemas,
permissionConfigMap,
formKey,
formShow,
isViewProcess,
otherParams,
);
return {
buildOptionJson,
uploadComponentIds: otherParams.uploadComponentIds,
};
}
function schemeList(
schemas,
permissionConfigMap,
formKey,
formShow,
isViewProcess,
otherParams,
tableName?,
) {
schemas = schemas.map((schema) => {
if (['Card', 'Grid', 'Tab'].includes(schema.component)) {
formShow += 1;
if (schema.children && schema.children.length > 0) {
schema.children.forEach((ele2) => {
if (ele2.list && ele2.list.length > 0) {
ele2.list = schemeList(
ele2.list,
permissionConfigMap,
formKey,
formShow,
isViewProcess,
otherParams,
tableName,
);
}
});
}
} else if (schema.component === 'TableLayout') {
formShow += 1;
if (schema.children && schema.children.length > 0) {
schema.children.forEach((ele2) => {
if (ele2.list && ele2.list.length > 0) {
ele2.list.forEach((ele3) => {
if (ele3.children && ele3.children.length > 0) {
ele3.children = schemeList(
ele3.children,
permissionConfigMap,
formKey,
formShow,
isViewProcess,
otherParams,
tableName,
);
}
});
}
});
}
} else if (schema.component == 'SubForm') {
if (
schema.componentProps &&
schema.componentProps.columns &&
schema.componentProps.columns.length > 0
) {
const permissionConfig = permissionConfigMap.has(schema.key)
? permissionConfigMap.get(schema.key)
: null;
// 查看流程
if (isViewProcess) {
schema.dynamicDisabled = true;
}
if (!permissionConfig?.view) {
schema.show = false;
} else {
formShow += 1;
if (!permissionConfig?.edit) {
schema.dynamicDisabled = true;
}
}
schema.componentProps.columns = schema.componentProps.columns.map((ele3) => {
const tableItemPermissionConfig = permissionConfig.children.find((x) => {
if (x.key) {
return x.key === ele3.key;
}
if (x.fieldId) {
return x.fieldId === ele3.dataIndex;
}
});
return getSchemePermissionItem(
ele3,
tableItemPermissionConfig,
formKey,
isViewProcess,
otherParams,
);
});
}
} else if (schema.component == 'OneForOne') {
const permissionConfig = permissionConfigMap.has(schema.key)
? permissionConfigMap.get(schema.key)
: null;
if (!permissionConfig?.view) {
schema.show = false;
} else {
formShow += 1;
}
if (schema.componentProps?.childSchemas?.length > 0) {
const onePermissionConfigMap = getPermissionConfigMap(permissionConfig.children);
schema.componentProps.childSchemas = schemeList(
schema.componentProps.childSchemas,
onePermissionConfigMap,
formKey,
formShow,
isViewProcess,
otherParams,
schema.field,
);
}
} else {
const permissionConfig = permissionConfigMap.has(schema.key)
? permissionConfigMap.get(schema.key)
: null;
schema = getSchemePermissionItem(
schema,
permissionConfig,
formKey,
isViewProcess,
otherParams,
tableName,
);
if (permissionConfig?.view) {
formShow += 1;
}
}
return schema;
});
return formShow > 0 ? schemas : null;
}
function getSchemePermissionItem(
schema,
permissionConfig,
formKey,
isViewProcess,
otherParams,
tableName?,
) {
if (permissionConfig) {
//查看
schema.show = permissionConfig.view;
// 必填
if (schema.componentProps) schema.componentProps.required = permissionConfig.required;
schema.required = permissionConfig.required;
//编辑
if (schema.componentProps) schema.componentProps.disabled = !permissionConfig.edit;
schema.dynamicDisabled = !permissionConfig.edit;
// 查看流程
if (isViewProcess) {
schema.dynamicDisabled = true;
}
} else {
schema.show = false;
schema.dynamicDisabled = true;
}
// 修改意见簿
if (schema.component == 'Opinion') {
const key = formKey + separator + schema.key;
schema.defaultValue = [];
if (otherParams.opinionsComponents?.includes(key)) {
schema.defaultValue = otherParams.opinions;
}
}
// 上传组件集合
if (schema.component == 'Upload') {
let key = '';
if (tableName) {
key = formKey + separator + tableName + separator + schema.field;
} else {
key = formKey + separator + schema.field;
}
otherParams.uploadComponentIds.push(key);
}
if (permissionConfig?.isSubTable) {
// 子表单上传组件集合
if (schema.componentType == 'Upload') {
const key =
formKey + separator + permissionConfig.tableName + separator + permissionConfig.fieldId;
otherParams.uploadComponentIds.push(key);
}
}
return schema;
}
// 根据工作流页面权限,设置表单属性(必填,禁用,显示)
export function changeWorkFlowForm(formProps: FormProps, obj: WorkFlowFormParams) {
const {
formConfigChildren,
formConfigKey,
opinions,
opinionsComponents,
isViewProcess,
uploadIds,
formModels,
} = obj;
const returnData: {
buildOptionJson: FormProps;
uploadComponentIds: Array<string>;
formModels: any;
isViewProcess: boolean;
} = {
buildOptionJson: {}, //重新的配置Json文件
uploadComponentIds: [], //
formModels,
isViewProcess: false,
};
try {
const { buildOptionJson, uploadComponentIds } = changeFormJson(
{
formJson: formProps,
formConfigChildren,
formConfigKey: formConfigKey,
opinions,
opinionsComponents,
},
isViewProcess,
uploadIds,
);
returnData.buildOptionJson = buildOptionJson;
returnData.uploadComponentIds = uploadComponentIds;
return returnData;
} catch (error) {
return returnData;
}
}
// 根据表单json 生成权限配置
export function getWorkflowPermissionConfig(schema) {
let children: Array<TableItem> = [];
if (schema.length > 0) {
children = getSchemasConfig(schema);
}
return children;
}
function getSchemasConfig(list: FormSchema[]) {
const arr: Array<TableItem> = [];
if (list && list.length > 0) {
list.forEach((ele1) => {
if (['Card', 'Grid', 'Tab'].includes(ele1.component)) {
if (ele1.children && ele1.children.length > 0) {
ele1.children.forEach((ele2) => {
if (ele2.list && ele2.list.length > 0) {
arr.push(...getSchemasConfig(ele2.list));
}
});
}
} else if (ele1.component === 'TableLayout') {
if (ele1.children && ele1.children.length > 0) {
ele1.children.forEach((ele2) => {
if (ele2.list && ele2.list.length > 0) {
ele2.list.forEach((ele3) => {
if (ele3.children && ele3.children.length > 0) {
arr.push(...getSchemasConfig(ele3.children));
}
});
}
});
}
} else if (ele1.component == 'SubForm') {
arr.push(getTableConfig(ele1));
} else if (ele1.component === 'OneForOne') {
const obj: TableItem = getItemConfig(ele1);
obj.isSubTable = true;
obj.showChildren = false;
const tempList: FormSchema[] = ele1.componentProps?.childSchemas || [];
obj.children.push(...getSchemasConfig(tempList));
arr.push(obj);
} else {
arr.push(getItemConfig(ele1));
}
});
}
return arr;
}
function getItemConfig(element) {
const children: Array<TableItem> = [];
let required = true;
let view = true;
let edit = true;
let disabled = false;
let isSaveTable = false; //组件是否存表
const type = element.type;
if (disableTypes.includes(element.type)) {
required = false;
view = true;
edit = false;
disabled = true;
}
if (requiredDisabled.includes(element.type)) {
required = false;
}
// 隐藏组件权限不要设置
if (element.type === hiddenComponentType) {
required = false;
view = false;
edit = false;
disabled = true;
}
if (element.type === 'input' && element.componentProps.isSave) {
required = false;
view = true;
edit = false;
disabled = true;
isSaveTable = true;
}
return {
required,
view,
edit,
disabled,
isSaveTable,
tableName: '',
fieldName: element.label,
fieldId: element.field,
isSubTable: false,
showChildren: true,
type,
key: element.key,
children,
};
}
function getTableConfig(element) {
const children: Array<TableItem> = [];
if (
element.componentProps &&
element.componentProps.columns &&
element.componentProps.columns.length > 0
) {
element.componentProps.columns.forEach((ele2) => {
if (ele2.dataIndex) children.push(getTableItemConfig(element.field, ele2));
});
}
return {
required: true,
view: true,
edit: true,
disabled: false,
isSubTable: true,
showChildren: false,
tableName: element.field,
fieldName: element.label,
fieldId: element.field,
type: element.type,
key: element.key,
children,
};
}
function getTableItemConfig(tableName: string, element) {
let isSaveTable = false; //组件是否存表
if (element.type === 'input' && element.componentProps.isSave) {
isSaveTable = true;
}
return {
required: true,
view: true,
edit: true,
disabled: false,
isSubTable: true,
isSaveTable,
showChildren: false,
tableName: tableName,
fieldName: element.title,
fieldId: element.dataIndex,
type: element.type,
key: element.key,
children: [],
};
}
// 辅助设置表单Disabled
export function changeSchemaDisabled(schemas) {
const layoutComponents = ['tab', 'grid', 'card'];
schemas?.map((info) => {
if (layoutComponents.includes(info.type!)) {
info.children?.map((childInfo) => {
childInfo.list.map((com) => {
if (layoutComponents.includes(com.type)) {
changeSchemaDisabled(childInfo.list);
} else {
com.dynamicDisabled = true;
}
});
});
} else if (info.type == 'table-layout') {
info.children?.map((childInfo) => {
childInfo.list.map((com) => {
com.children.map((el) => {
if (layoutComponents.includes(el.type) || el.type == 'table-layout') {
changeSchemaDisabled(com.children);
} else {
el.dynamicDisabled = true;
}
});
});
});
} else if (info.type == 'one-for-one') {
changeSchemaDisabled(info.componentProps.childSchemas);
} else {
info.dynamicDisabled = true;
}
});
return schemas;
}