新增前端vue
This commit is contained in:
6
web-vue/packages/core/components/Description/index.ts
Normal file
6
web-vue/packages/core/components/Description/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { withInstall } from '@jeesite/core/utils';
|
||||
import description from './src/Description.vue';
|
||||
|
||||
export * from './src/typing';
|
||||
export { useDescription } from './src/useDescription';
|
||||
export const Description = withInstall(description);
|
||||
183
web-vue/packages/core/components/Description/src/Description.vue
Normal file
183
web-vue/packages/core/components/Description/src/Description.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<script lang="tsx">
|
||||
import type { DescriptionProps, DescInstance, DescItem } from './typing';
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
|
||||
import type { CSSProperties } from 'vue';
|
||||
import type { CollapseContainerOptions } from '@jeesite/core/components/Container';
|
||||
import { defineComponent, computed, ref, unref } from 'vue';
|
||||
import { get } from 'lodash-es';
|
||||
import { Descriptions } from 'ant-design-vue';
|
||||
import { CollapseContainer } from '@jeesite/core/components/Container';
|
||||
import { useDesign } from '@jeesite/core/hooks/web/useDesign';
|
||||
import { isFunction } from '@jeesite/core/utils/is';
|
||||
import { getSlot } from '@jeesite/core/utils/helper/tsxHelper';
|
||||
import { useAttrs } from '@jeesite/core/hooks/core/useAttrs';
|
||||
|
||||
const props = {
|
||||
useCollapse: { type: Boolean, default: true },
|
||||
title: { type: String, default: '' },
|
||||
size: {
|
||||
type: String,
|
||||
validator: (v) => ['small', 'default', 'middle', undefined].includes(v),
|
||||
default: 'small',
|
||||
},
|
||||
bordered: { type: Boolean, default: true },
|
||||
column: {
|
||||
type: [Number, Object] as PropType<number | Recordable>,
|
||||
default: () => {
|
||||
return { xxl: 4, xl: 3, lg: 3, md: 3, sm: 2, xs: 1 };
|
||||
},
|
||||
},
|
||||
collapseOptions: {
|
||||
type: Object as PropType<CollapseContainerOptions>,
|
||||
default: null,
|
||||
},
|
||||
schema: {
|
||||
type: Array as PropType<DescItem[]>,
|
||||
default: () => [],
|
||||
},
|
||||
data: { type: Object },
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
name: 'Description',
|
||||
props,
|
||||
emits: ['register'],
|
||||
setup(props, { slots, emit }) {
|
||||
const propsRef = ref<Partial<DescriptionProps> | null>(null);
|
||||
|
||||
const { prefixCls } = useDesign('description');
|
||||
const attrs = useAttrs();
|
||||
|
||||
// Custom title component: get title
|
||||
const getMergeProps = computed(() => {
|
||||
return {
|
||||
...props,
|
||||
...(unref(propsRef) as Recordable),
|
||||
} as DescriptionProps;
|
||||
});
|
||||
|
||||
const getProps = computed(() => {
|
||||
const opt = {
|
||||
...unref(getMergeProps),
|
||||
title: undefined,
|
||||
};
|
||||
return opt as DescriptionProps;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description: Whether to setting title
|
||||
*/
|
||||
const useWrapper = computed(() => !!unref(getMergeProps).title || !!slots.title);
|
||||
|
||||
/**
|
||||
* @description: Get configuration Collapse
|
||||
*/
|
||||
const getCollapseOptions = computed((): CollapseContainerOptions => {
|
||||
return {
|
||||
// Cannot be expanded by default
|
||||
canExpand: false,
|
||||
expand: true,
|
||||
...unref(getProps).collapseOptions,
|
||||
};
|
||||
});
|
||||
|
||||
const getDescriptionsProps = computed(() => {
|
||||
return { ...unref(attrs), ...unref(getProps) } as DescriptionsProps;
|
||||
});
|
||||
|
||||
/**
|
||||
* @description:设置desc
|
||||
*/
|
||||
function setDescProps(descProps: Partial<DescriptionProps>): void {
|
||||
// Keep the last setDescProps
|
||||
propsRef.value = { ...(unref(propsRef) as Recordable), ...descProps } as Recordable;
|
||||
}
|
||||
|
||||
// Prevent line breaks
|
||||
function renderLabel({ label, labelMinWidth, labelStyle }: DescItem) {
|
||||
if (!labelStyle && !labelMinWidth) {
|
||||
return label;
|
||||
}
|
||||
|
||||
const labelStyles: CSSProperties = {
|
||||
...labelStyle,
|
||||
minWidth: `${labelMinWidth}px `,
|
||||
};
|
||||
return <div style={labelStyles}>{label}</div>;
|
||||
}
|
||||
|
||||
function renderItem() {
|
||||
const { schema, data } = unref(getProps);
|
||||
return unref(schema)
|
||||
.map((item) => {
|
||||
const { render, field, span, show, contentMinWidth } = item;
|
||||
|
||||
if (show && isFunction(show) && !show(data)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const getContent = () => {
|
||||
const _data = unref(getProps)?.data;
|
||||
if (!_data) {
|
||||
return null;
|
||||
}
|
||||
const getField = get(_data, field);
|
||||
return isFunction(render) ? render(getField, _data) : (getField ?? '');
|
||||
};
|
||||
|
||||
const width = contentMinWidth;
|
||||
return (
|
||||
<Descriptions.Item label={renderLabel(item)} key={field} span={span}>
|
||||
{() => {
|
||||
if (!contentMinWidth) {
|
||||
return getContent();
|
||||
}
|
||||
const style: CSSProperties = {
|
||||
minWidth: `${width}px`,
|
||||
};
|
||||
return <div style={style}>{getContent()}</div>;
|
||||
}}
|
||||
</Descriptions.Item>
|
||||
);
|
||||
})
|
||||
.filter((item) => !!item);
|
||||
}
|
||||
|
||||
const renderDesc = () => {
|
||||
return (
|
||||
<Descriptions class={`${prefixCls}`} {...(unref(getDescriptionsProps) as any)}>
|
||||
{renderItem()}
|
||||
</Descriptions>
|
||||
);
|
||||
};
|
||||
|
||||
const renderContainer = () => {
|
||||
const content = props.useCollapse ? renderDesc() : <div>{renderDesc()}</div>;
|
||||
// Reduce the dom level
|
||||
if (!props.useCollapse) {
|
||||
return content;
|
||||
}
|
||||
|
||||
const { canExpand, expand, helpMessage } = unref(getCollapseOptions);
|
||||
const { title } = unref(getMergeProps);
|
||||
|
||||
return (
|
||||
<CollapseContainer title={title} canExpan={canExpand} expand={expand} helpMessage={helpMessage}>
|
||||
{{
|
||||
default: () => content,
|
||||
action: () => getSlot(slots, 'action'),
|
||||
title: () => getSlot(slots, 'title'),
|
||||
}}
|
||||
</CollapseContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const methods: DescInstance = {
|
||||
setDescProps,
|
||||
};
|
||||
|
||||
emit('register', methods);
|
||||
return () => (unref(useWrapper) ? renderContainer() : renderDesc());
|
||||
},
|
||||
});
|
||||
</script>
|
||||
47
web-vue/packages/core/components/Description/src/typing.ts
Normal file
47
web-vue/packages/core/components/Description/src/typing.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import type { VNode, CSSProperties } from 'vue';
|
||||
import type { CollapseContainerOptions } from '@jeesite/core/components/Container';
|
||||
import type { DescriptionsProps } from 'ant-design-vue/es/descriptions';
|
||||
|
||||
export interface DescItem {
|
||||
labelMinWidth?: number;
|
||||
contentMinWidth?: number;
|
||||
labelStyle?: CSSProperties;
|
||||
field: string;
|
||||
label: string | VNode | JSX.Element;
|
||||
// Merge column
|
||||
span?: number;
|
||||
show?: (...arg: any) => boolean;
|
||||
// render
|
||||
render?: (val: any, data: Recordable) => VNode | undefined | JSX.Element | Element | string | number;
|
||||
}
|
||||
|
||||
export interface DescriptionProps extends DescriptionsProps {
|
||||
// Whether to include the collapse component
|
||||
useCollapse?: boolean;
|
||||
/**
|
||||
* item configuration
|
||||
* @type DescItem
|
||||
*/
|
||||
schema: DescItem[];
|
||||
/**
|
||||
* 数据
|
||||
* @type object
|
||||
*/
|
||||
data: Recordable;
|
||||
/**
|
||||
* Built-in CollapseContainer component configuration
|
||||
* @type CollapseContainerOptions
|
||||
*/
|
||||
collapseOptions?: CollapseContainerOptions;
|
||||
}
|
||||
|
||||
export interface DescInstance {
|
||||
setDescProps(descProps: Partial<DescriptionProps>): void;
|
||||
}
|
||||
|
||||
export type Register = (descInstance: DescInstance) => void;
|
||||
|
||||
/**
|
||||
* @description:
|
||||
*/
|
||||
export type UseDescReturnType = [Register, DescInstance];
|
||||
@@ -0,0 +1,28 @@
|
||||
import type { DescriptionProps, DescInstance, UseDescReturnType } from './typing';
|
||||
import { ref, getCurrentInstance, unref } from 'vue';
|
||||
import { isProdMode } from '@jeesite/core/utils/env';
|
||||
|
||||
export function useDescription(props?: Partial<DescriptionProps>): UseDescReturnType {
|
||||
if (!getCurrentInstance()) {
|
||||
throw new Error('useDescription() can only be used inside setup() or functional components!');
|
||||
}
|
||||
const desc = ref<Nullable<DescInstance>>(null);
|
||||
const loaded = ref(false);
|
||||
|
||||
function register(instance: DescInstance) {
|
||||
if (unref(loaded) && isProdMode()) {
|
||||
return;
|
||||
}
|
||||
desc.value = instance;
|
||||
props && instance.setDescProps(props);
|
||||
loaded.value = true;
|
||||
}
|
||||
|
||||
const methods: DescInstance = {
|
||||
setDescProps: (descProps: Partial<DescriptionProps>): void => {
|
||||
unref(desc)?.setDescProps(descProps);
|
||||
},
|
||||
};
|
||||
|
||||
return [register, methods];
|
||||
}
|
||||
Reference in New Issue
Block a user