mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-11-30 14:32:09 +08:00
feat(design,editor,element-plus-adapter,form,table,tdesign-vue-next-adapter): 重构table组件,适配tdesign
This commit is contained in:
parent
566b754887
commit
5b16ec00e1
@ -30,3 +30,11 @@ const clickHandler = (...args: any[]) => {
|
|||||||
emit('click', ...args);
|
emit('click', ...args);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.tmagic-design-button {
|
||||||
|
.t-button__text {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -19,3 +19,16 @@ const uiComponent = ui?.component || 'el-icon';
|
|||||||
const props = defineProps<IconProps>();
|
const props = defineProps<IconProps>();
|
||||||
const uiProps = computed<IconProps>(() => ui?.props(props) || props);
|
const uiProps = computed<IconProps>(() => ui?.props(props) || props);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.t-t-design-adapter-icon {
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref, watchEffect } from 'vue';
|
import { computed, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
import { getDesignConfig } from './config';
|
import { getDesignConfig } from './config';
|
||||||
import type { TableProps } from './types';
|
import type { TableProps } from './types';
|
||||||
@ -37,7 +37,7 @@ const uiProps = computed<TableProps>(() => ui?.props(props) || props);
|
|||||||
|
|
||||||
const emit = defineEmits(['select', 'sort-change', 'expand-change', 'cell-click']);
|
const emit = defineEmits(['select', 'sort-change', 'expand-change', 'cell-click']);
|
||||||
|
|
||||||
const table = ref<any>();
|
const tableRef = useTemplateRef('table');
|
||||||
|
|
||||||
const selectHandler = (...args: any[]) => {
|
const selectHandler = (...args: any[]) => {
|
||||||
emit('select', ...args);
|
emit('select', ...args);
|
||||||
@ -55,27 +55,21 @@ const cellClickHandler = (...args: any[]) => {
|
|||||||
emit('cell-click', ...args);
|
emit('cell-click', ...args);
|
||||||
};
|
};
|
||||||
|
|
||||||
let $el: HTMLDivElement | undefined;
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
$el = table.value?.$el;
|
|
||||||
});
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
instance: table,
|
getEl: () => tableRef.value?.getTableRef().$el,
|
||||||
|
|
||||||
$el,
|
getTableRef: () => tableRef.value.getTableRef(),
|
||||||
|
|
||||||
clearSelection(...args: any[]) {
|
clearSelection(...args: any[]) {
|
||||||
return table.value?.clearSelection(...args);
|
return tableRef.value?.clearSelection(...args);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleRowSelection(...args: any[]) {
|
toggleRowSelection(...args: any[]) {
|
||||||
return table.value?.toggleRowSelection(...args);
|
return tableRef.value?.toggleRowSelection(...args);
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleRowExpansion(...args: any[]) {
|
toggleRowExpansion(...args: any[]) {
|
||||||
return table.value?.toggleRowExpansion(...args);
|
return tableRef.value?.toggleRowExpansion(...args);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
<template>
|
|
||||||
<component :is="uiComponent" v-bind="uiProps">
|
|
||||||
<template #default="{ $index, row }">
|
|
||||||
<!-- eslint-disable-next-line vue/valid-attribute-name -->
|
|
||||||
<slot :$index="$index" :row="row"></slot>
|
|
||||||
</template>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed } from 'vue';
|
|
||||||
|
|
||||||
import { getDesignConfig } from './config';
|
|
||||||
import type { TableColumnProps } from './types';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'TMTableColumn',
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = defineProps<TableColumnProps>();
|
|
||||||
|
|
||||||
const ui = getDesignConfig('components')?.tableColumn;
|
|
||||||
|
|
||||||
const uiComponent = ui?.component || 'el-table-column';
|
|
||||||
|
|
||||||
const uiProps = computed<TableColumnProps>(() => ui?.props(props) || props);
|
|
||||||
</script>
|
|
||||||
@ -1,110 +0,0 @@
|
|||||||
<template>
|
|
||||||
<component
|
|
||||||
class="tmagic-design-tree"
|
|
||||||
ref="tree"
|
|
||||||
:is="uiComponent"
|
|
||||||
v-bind="uiProps"
|
|
||||||
@node-click="nodeClickHandler"
|
|
||||||
@node-contextmenu="contextmenu"
|
|
||||||
@node-drag-end="handleDragEnd"
|
|
||||||
@node-collapse="handleCollapse"
|
|
||||||
@node-expand="handleExpand"
|
|
||||||
@check="checkHandler"
|
|
||||||
@mousedown="mousedownHandler"
|
|
||||||
@mouseup="mouseupHandler"
|
|
||||||
>
|
|
||||||
<template #default="{ data, node }">
|
|
||||||
<slot :data="data" :node="node"></slot>
|
|
||||||
</template>
|
|
||||||
</component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, ref } from 'vue';
|
|
||||||
|
|
||||||
import { getDesignConfig } from './config';
|
|
||||||
import type { TreeProps } from './types';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'TMTree',
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = defineProps<TreeProps>();
|
|
||||||
|
|
||||||
const ui = getDesignConfig('components')?.tree;
|
|
||||||
|
|
||||||
const uiComponent = ui?.component || 'el-tree';
|
|
||||||
|
|
||||||
const uiProps = computed<TreeProps>(() => ui?.props(props) || props);
|
|
||||||
|
|
||||||
const emit = defineEmits([
|
|
||||||
'node-click',
|
|
||||||
'node-contextmenu',
|
|
||||||
'node-drag-end',
|
|
||||||
'node-collapse',
|
|
||||||
'node-expand',
|
|
||||||
'check',
|
|
||||||
'mousedown',
|
|
||||||
'mouseup',
|
|
||||||
]);
|
|
||||||
|
|
||||||
const nodeClickHandler = (...args: any[]) => {
|
|
||||||
emit('node-click', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const contextmenu = (...args: any[]) => {
|
|
||||||
emit('node-contextmenu', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDragEnd = (...args: any[]) => {
|
|
||||||
emit('node-drag-end', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCollapse = (...args: any[]) => {
|
|
||||||
emit('node-collapse', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleExpand = (...args: any[]) => {
|
|
||||||
emit('node-expand', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const checkHandler = (...args: any[]) => {
|
|
||||||
emit('check', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const mousedownHandler = (...args: any[]) => {
|
|
||||||
emit('mousedown', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const mouseupHandler = (...args: any[]) => {
|
|
||||||
emit('mouseup', ...args);
|
|
||||||
};
|
|
||||||
|
|
||||||
const tree = ref<any>();
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
getData() {
|
|
||||||
return tree.value?.data;
|
|
||||||
},
|
|
||||||
|
|
||||||
getStore() {
|
|
||||||
return tree.value?.store;
|
|
||||||
},
|
|
||||||
|
|
||||||
filter(...args: any[]) {
|
|
||||||
return tree.value?.filter(...args);
|
|
||||||
},
|
|
||||||
|
|
||||||
getNode(...args: any[]) {
|
|
||||||
return tree.value?.getNode(...args);
|
|
||||||
},
|
|
||||||
|
|
||||||
setCheckedKeys(...args: any[]) {
|
|
||||||
return tree.value?.setCheckedKeys(...args);
|
|
||||||
},
|
|
||||||
|
|
||||||
setCurrentKey(...args: any[]) {
|
|
||||||
return tree.value?.setCurrentKey(...args);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@ -46,13 +46,11 @@ export { default as TMagicStep } from './Step.vue';
|
|||||||
export { default as TMagicSteps } from './Steps.vue';
|
export { default as TMagicSteps } from './Steps.vue';
|
||||||
export { default as TMagicSwitch } from './Switch.vue';
|
export { default as TMagicSwitch } from './Switch.vue';
|
||||||
export { default as TMagicTable } from './Table.vue';
|
export { default as TMagicTable } from './Table.vue';
|
||||||
export { default as TMagicTableColumn } from './TableColumn.vue';
|
|
||||||
export { default as TMagicTabPane } from './TabPane.vue';
|
export { default as TMagicTabPane } from './TabPane.vue';
|
||||||
export { default as TMagicTabs } from './Tabs.vue';
|
export { default as TMagicTabs } from './Tabs.vue';
|
||||||
export { default as TMagicTag } from './Tag.vue';
|
export { default as TMagicTag } from './Tag.vue';
|
||||||
export { default as TMagicTimePicker } from './TimePicker.vue';
|
export { default as TMagicTimePicker } from './TimePicker.vue';
|
||||||
export { default as TMagicTooltip } from './Tooltip.vue';
|
export { default as TMagicTooltip } from './Tooltip.vue';
|
||||||
export { default as TMagicTree } from './Tree.vue';
|
|
||||||
export { default as TMagicUpload } from './Upload.vue';
|
export { default as TMagicUpload } from './Upload.vue';
|
||||||
|
|
||||||
export const tMagicMessage = {
|
export const tMagicMessage = {
|
||||||
|
|||||||
@ -182,6 +182,7 @@ export interface FormItemProps {
|
|||||||
prop?: string;
|
prop?: string;
|
||||||
labelWidth?: string | number;
|
labelWidth?: string | number;
|
||||||
rules?: any;
|
rules?: any;
|
||||||
|
extra?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InputProps {
|
export interface InputProps {
|
||||||
@ -295,17 +296,36 @@ export interface SwitchProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TableProps {
|
export interface TableProps {
|
||||||
|
columns?: TableColumnOptions[];
|
||||||
data?: any[];
|
data?: any[];
|
||||||
border?: boolean;
|
border?: boolean;
|
||||||
maxHeight?: number | string;
|
maxHeight?: number | string;
|
||||||
defaultExpandAll?: boolean;
|
defaultExpandAll?: boolean;
|
||||||
|
showHeader?: boolean;
|
||||||
|
rowKey?: string;
|
||||||
|
treeProps?: Record<string, any>;
|
||||||
|
emptyText?: string;
|
||||||
|
tooltipEffect?: string;
|
||||||
|
tooltipOptions?: any;
|
||||||
|
showOverflowTooltip?: boolean;
|
||||||
|
spanMethod?: (data: any) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableColumnProps {
|
export interface TableColumnOptions<T = any> {
|
||||||
label?: string;
|
props: {
|
||||||
align?: string;
|
class?: string;
|
||||||
fixed?: string | boolean;
|
label?: string;
|
||||||
width?: string | number;
|
fixed?: 'left' | 'right' | boolean;
|
||||||
|
width?: number | string;
|
||||||
|
type?: 'default' | 'selection' | 'index' | 'expand';
|
||||||
|
prop?: string;
|
||||||
|
align?: string;
|
||||||
|
headerAlign?: string;
|
||||||
|
sortable?: boolean;
|
||||||
|
sortOrders?: Array<'ascending' | 'descending'>;
|
||||||
|
selectable?: (row: T, index: number) => boolean;
|
||||||
|
};
|
||||||
|
cell?: (scope: { row: T; $index: number }) => any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TabPaneProps {
|
export interface TabPaneProps {
|
||||||
@ -342,33 +362,6 @@ export interface TooltipProps {
|
|||||||
offset?: number;
|
offset?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeProps {
|
|
||||||
data?: any[];
|
|
||||||
emptyText?: string;
|
|
||||||
nodeKey?: string;
|
|
||||||
props?: any;
|
|
||||||
renderAfterExpand?: boolean;
|
|
||||||
load?: any;
|
|
||||||
renderContent?: any;
|
|
||||||
highlightCurrent?: boolean;
|
|
||||||
defaultExpandAll?: boolean;
|
|
||||||
checkOnClickNode?: boolean;
|
|
||||||
autoExpandParent?: boolean;
|
|
||||||
defaultExpandedKeys?: any[];
|
|
||||||
showCheckbox?: boolean;
|
|
||||||
checkStrictly?: boolean;
|
|
||||||
defaultCheckedKeys?: any[];
|
|
||||||
currentNodeKey?: string | number;
|
|
||||||
filterNodeMethod?: (value: any, data: any, node: any) => boolean;
|
|
||||||
accordion?: boolean;
|
|
||||||
indent?: number;
|
|
||||||
icon?: any;
|
|
||||||
lazy?: boolean;
|
|
||||||
draggable?: boolean;
|
|
||||||
allowDrag?: (node: any) => boolean;
|
|
||||||
allowDrop?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UploadProps {
|
export interface UploadProps {
|
||||||
action?: string;
|
action?: string;
|
||||||
autoUpload?: boolean;
|
autoUpload?: boolean;
|
||||||
@ -635,8 +628,8 @@ export interface Components {
|
|||||||
| DefineComponent<
|
| DefineComponent<
|
||||||
TableProps,
|
TableProps,
|
||||||
{
|
{
|
||||||
instance: any;
|
getEl: () => HTMLElement | undefined;
|
||||||
$el: HTMLDivElement | undefined;
|
getTableRef: () => any;
|
||||||
clearSelection: (...args: any[]) => void;
|
clearSelection: (...args: any[]) => void;
|
||||||
toggleRowSelection: (...args: any[]) => void;
|
toggleRowSelection: (...args: any[]) => void;
|
||||||
toggleRowExpansion: (...args: any[]) => void;
|
toggleRowExpansion: (...args: any[]) => void;
|
||||||
@ -647,11 +640,6 @@ export interface Components {
|
|||||||
props: (props: TableProps) => TableProps;
|
props: (props: TableProps) => TableProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
tableColumn: {
|
|
||||||
component: DefineComponent<TableColumnProps, {}, any> | string;
|
|
||||||
props: (props: TableColumnProps) => TableColumnProps;
|
|
||||||
};
|
|
||||||
|
|
||||||
tabPane: {
|
tabPane: {
|
||||||
component: DefineComponent<TabPaneProps, {}, any> | string;
|
component: DefineComponent<TabPaneProps, {}, any> | string;
|
||||||
props: (props: TabPaneProps) => TabPaneProps;
|
props: (props: TabPaneProps) => TabPaneProps;
|
||||||
@ -677,24 +665,6 @@ export interface Components {
|
|||||||
props: (props: TooltipProps) => TooltipProps;
|
props: (props: TooltipProps) => TooltipProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
tree: {
|
|
||||||
component:
|
|
||||||
| DefineComponent<
|
|
||||||
TreeProps,
|
|
||||||
{
|
|
||||||
getData: () => TreeProps['data'];
|
|
||||||
getStore: () => any;
|
|
||||||
filter: (...args: any[]) => any;
|
|
||||||
getNode: (...args: any[]) => any;
|
|
||||||
setCheckedKeys: (...args: any[]) => any;
|
|
||||||
setCurrentKey: (...args: any[]) => any;
|
|
||||||
},
|
|
||||||
any
|
|
||||||
>
|
|
||||||
| string;
|
|
||||||
props: (props: TreeProps) => TreeProps;
|
|
||||||
};
|
|
||||||
|
|
||||||
upload: {
|
upload: {
|
||||||
component:
|
component:
|
||||||
| DefineComponent<
|
| DefineComponent<
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicCollapse class="m-fields-style-setter" :model-value="collapseValue">
|
<TMagicCollapse class="m-fields-style-setter" v-model="collapseValue">
|
||||||
<template v-for="(item, index) in list" :key="index">
|
<template v-for="(item, index) in list" :key="index">
|
||||||
<TMagicCollapseItem :name="`${index}`">
|
<TMagicCollapseItem :name="`${index}`">
|
||||||
<template #title><MIcon :icon="Grid"></MIcon>{{ item.title }}</template>
|
<template #title><MIcon :icon="Grid"></MIcon>{{ item.title }}</template>
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
<SearchInput @search="filterTextChangeHandler"></SearchInput>
|
||||||
|
|
||||||
<slot name="component-list" :component-group-list="list">
|
<slot name="component-list" :component-group-list="list">
|
||||||
<TMagicCollapse class="ui-component-panel" :model-value="collapseValue">
|
<TMagicCollapse class="ui-component-panel" v-model="collapseValue">
|
||||||
<template v-for="(group, index) in list">
|
<template v-for="(group, index) in list">
|
||||||
<TMagicCollapseItem v-if="group.items && group.items.length" :key="index" :name="`${index}`">
|
<TMagicCollapseItem v-if="group.items && group.items.length" :key="index" :name="`${index}`">
|
||||||
<template #title><MIcon :icon="Grid"></MIcon>{{ group.title }}</template>
|
<template #title><MIcon :icon="Grid"></MIcon>{{ group.title }}</template>
|
||||||
@ -34,7 +34,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, inject, ref } from 'vue';
|
import { computed, inject, ref, watch } from 'vue';
|
||||||
import { Grid } from '@element-plus/icons-vue';
|
import { Grid } from '@element-plus/icons-vue';
|
||||||
import serialize from 'serialize-javascript';
|
import serialize from 'serialize-javascript';
|
||||||
|
|
||||||
@ -74,10 +74,19 @@ const list = computed<ComponentGroup[]>(() =>
|
|||||||
items: group.items.filter((item: ComponentItem) => item.text.includes(searchText.value)),
|
items: group.items.filter((item: ComponentItem) => item.text.includes(searchText.value)),
|
||||||
})),
|
})),
|
||||||
);
|
);
|
||||||
const collapseValue = computed(() =>
|
|
||||||
Array(list.value?.length)
|
const collapseValue = ref();
|
||||||
.fill(1)
|
|
||||||
.map((x, i) => `${i}`),
|
watch(
|
||||||
|
list,
|
||||||
|
() => {
|
||||||
|
collapseValue.value = Array(list.value?.length)
|
||||||
|
.fill(1)
|
||||||
|
.map((x, i) => `${i}`);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let timeout: ReturnType<typeof setTimeout> | undefined;
|
let timeout: ReturnType<typeof setTimeout> | undefined;
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
@use "../common/var" as *;
|
||||||
|
|
||||||
.background-position-container {
|
.background-position-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -6,8 +8,8 @@
|
|||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: auto;
|
height: auto;
|
||||||
.el-button {
|
.tmagic-design-button {
|
||||||
& + .el-button {
|
& + .tmagic-design-button {
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
}
|
}
|
||||||
&:nth-child(3n + 1) {
|
&:nth-child(3n + 1) {
|
||||||
@ -15,13 +17,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.t-button--variant-text {
|
||||||
|
padding-left: 2px;
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
.position-icon {
|
.position-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
border: 1px solid #1d1f24;
|
border: 1px solid #1d1f24;
|
||||||
&.active {
|
&.active {
|
||||||
background-color: var(--el-color-primary);
|
background-color: var($theme-color);
|
||||||
&::after {
|
&::after {
|
||||||
border: 1px solid #fff;
|
border: 1px solid #fff;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
/*
|
/*
|
||||||
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
* Tencent is pleased to support the open source community by making TMagicEditor available.
|
||||||
*
|
*
|
||||||
|
|||||||
26
packages/element-plus-adapter/src/FormItem.vue
Normal file
26
packages/element-plus-adapter/src/FormItem.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<ElFormItem v-bind="itemProps">
|
||||||
|
<template #label>
|
||||||
|
<slot name="label"></slot>
|
||||||
|
</template>
|
||||||
|
<slot></slot>
|
||||||
|
<div v-if="extra" v-html="extra" class="m-form-tip"></div>
|
||||||
|
</ElFormItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { FormItemProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TElAdapterFormItem',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<FormItemProps>();
|
||||||
|
|
||||||
|
const itemProps = computed(() => {
|
||||||
|
const { extra, ...rest } = props;
|
||||||
|
return rest;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
92
packages/element-plus-adapter/src/Table.vue
Normal file
92
packages/element-plus-adapter/src/Table.vue
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<ElTable
|
||||||
|
ref="table"
|
||||||
|
:data="data"
|
||||||
|
:border="border"
|
||||||
|
:max-height="maxHeight"
|
||||||
|
:default-expand-all="defaultExpandAll"
|
||||||
|
:show-header="showHeader"
|
||||||
|
:row-key="rowKey"
|
||||||
|
:tree-props="treeProps"
|
||||||
|
:empty-text="emptyText"
|
||||||
|
:show-overflow-tooltip="showOverflowTooltip"
|
||||||
|
:tooltip-effect="tooltipEffect"
|
||||||
|
:tooltip-options="tooltipOptions"
|
||||||
|
:span-method="spanMethod"
|
||||||
|
@sort-change="sortChange"
|
||||||
|
@select="selectHandler"
|
||||||
|
@select-all="selectAllHandler"
|
||||||
|
@selection-change="selectionChangeHandler"
|
||||||
|
@cell-click="cellClickHandler"
|
||||||
|
@expand-change="expandChange"
|
||||||
|
>
|
||||||
|
<template v-for="(item, columnIndex) in columns" :key="columnIndex">
|
||||||
|
<ElTableColumn v-bind="item.props || {}">
|
||||||
|
<template #default="scope" v-if="item.cell">
|
||||||
|
<component :is="item.cell(scope)"></component>
|
||||||
|
</template>
|
||||||
|
</ElTableColumn>
|
||||||
|
</template>
|
||||||
|
</ElTable>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useTemplateRef } from 'vue';
|
||||||
|
import { ElTable, ElTableColumn } from 'element-plus';
|
||||||
|
|
||||||
|
import type { TableProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TElAdapterTable',
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['sort-change', 'select', 'select-all', 'selection-change', 'expand-change', 'cell-click']);
|
||||||
|
|
||||||
|
defineProps<TableProps>();
|
||||||
|
|
||||||
|
const tableRef = useTemplateRef('table');
|
||||||
|
|
||||||
|
const sortChange = (data: any) => {
|
||||||
|
emit('sort-change', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectHandler = (...args: any[]) => {
|
||||||
|
emit('select', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectAllHandler = (...args: any[]) => {
|
||||||
|
emit('select-all', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectionChangeHandler = (...args: any[]) => {
|
||||||
|
emit('selection-change', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cellClickHandler = (...args: any[]) => {
|
||||||
|
emit('cell-click', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandChange = (...args: any[]) => {
|
||||||
|
emit('expand-change', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleRowSelection = (row: any, selected: boolean) => {
|
||||||
|
tableRef.value?.toggleRowSelection(row, selected);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleRowExpansion = (row: any, expanded: boolean) => {
|
||||||
|
tableRef.value?.toggleRowExpansion(row, expanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSelection = () => {
|
||||||
|
tableRef.value?.clearSelection();
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getEl: () => tableRef.value?.$el,
|
||||||
|
getTableRef: () => tableRef.value,
|
||||||
|
clearSelection,
|
||||||
|
toggleRowSelection,
|
||||||
|
toggleRowExpansion,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@ -18,7 +18,6 @@ import {
|
|||||||
ElDropdownItem,
|
ElDropdownItem,
|
||||||
ElDropdownMenu,
|
ElDropdownMenu,
|
||||||
ElForm,
|
ElForm,
|
||||||
ElFormItem,
|
|
||||||
ElIcon,
|
ElIcon,
|
||||||
ElInput,
|
ElInput,
|
||||||
ElInputNumber,
|
ElInputNumber,
|
||||||
@ -37,14 +36,11 @@ import {
|
|||||||
ElStep,
|
ElStep,
|
||||||
ElSteps,
|
ElSteps,
|
||||||
ElSwitch,
|
ElSwitch,
|
||||||
ElTable,
|
|
||||||
ElTableColumn,
|
|
||||||
ElTabPane,
|
ElTabPane,
|
||||||
ElTabs,
|
ElTabs,
|
||||||
ElTag,
|
ElTag,
|
||||||
ElTimePicker,
|
ElTimePicker,
|
||||||
ElTooltip,
|
ElTooltip,
|
||||||
ElTree,
|
|
||||||
ElUpload,
|
ElUpload,
|
||||||
useZIndex,
|
useZIndex,
|
||||||
} from 'element-plus';
|
} from 'element-plus';
|
||||||
@ -83,17 +79,18 @@ import type {
|
|||||||
StepProps,
|
StepProps,
|
||||||
StepsProps,
|
StepsProps,
|
||||||
SwitchProps,
|
SwitchProps,
|
||||||
TableColumnProps,
|
|
||||||
TableProps,
|
TableProps,
|
||||||
TabPaneProps,
|
TabPaneProps,
|
||||||
TabsProps,
|
TabsProps,
|
||||||
TagProps,
|
TagProps,
|
||||||
TimePickerProps,
|
TimePickerProps,
|
||||||
TooltipProps,
|
TooltipProps,
|
||||||
TreeProps,
|
|
||||||
UploadProps,
|
UploadProps,
|
||||||
} from '@tmagic/design';
|
} from '@tmagic/design';
|
||||||
|
|
||||||
|
import FormItem from './FormItem.vue';
|
||||||
|
import Table from './Table.vue';
|
||||||
|
|
||||||
const adapter: DesignPluginOptions = {
|
const adapter: DesignPluginOptions = {
|
||||||
useZIndex,
|
useZIndex,
|
||||||
message: ElMessage,
|
message: ElMessage,
|
||||||
@ -195,7 +192,7 @@ const adapter: DesignPluginOptions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
formItem: {
|
formItem: {
|
||||||
component: ElFormItem as any,
|
component: FormItem as any,
|
||||||
props: (props: FormItemProps) => props,
|
props: (props: FormItemProps) => props,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -275,15 +272,10 @@ const adapter: DesignPluginOptions = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
table: {
|
table: {
|
||||||
component: ElTable as any,
|
component: Table as any,
|
||||||
props: (props: TableProps) => props,
|
props: (props: TableProps) => props,
|
||||||
},
|
},
|
||||||
|
|
||||||
tableColumn: {
|
|
||||||
component: ElTableColumn as any,
|
|
||||||
props: (props: TableColumnProps) => props,
|
|
||||||
},
|
|
||||||
|
|
||||||
tabPane: {
|
tabPane: {
|
||||||
component: ElTabPane as any,
|
component: ElTabPane as any,
|
||||||
props: (props: TabPaneProps) => props,
|
props: (props: TabPaneProps) => props,
|
||||||
@ -309,11 +301,6 @@ const adapter: DesignPluginOptions = {
|
|||||||
props: (props: TooltipProps) => props,
|
props: (props: TooltipProps) => props,
|
||||||
},
|
},
|
||||||
|
|
||||||
tree: {
|
|
||||||
component: ElTree as any,
|
|
||||||
props: (props: TreeProps) => props,
|
|
||||||
},
|
|
||||||
|
|
||||||
upload: {
|
upload: {
|
||||||
component: ElUpload as any,
|
component: ElUpload as any,
|
||||||
props: (props: UploadProps) => props,
|
props: (props: UploadProps) => props,
|
||||||
|
|||||||
@ -700,6 +700,7 @@ export interface TableConfig extends FormItem {
|
|||||||
onSelect?: (mForm: FormState | undefined, data: any) => any;
|
onSelect?: (mForm: FormState | undefined, data: any) => any;
|
||||||
defautSort?: SortProp;
|
defautSort?: SortProp;
|
||||||
defaultSort?: SortProp;
|
defaultSort?: SortProp;
|
||||||
|
/** 是否支持拖拽排序 */
|
||||||
dropSort?: boolean;
|
dropSort?: boolean;
|
||||||
/** 是否显示全屏按钮 */
|
/** 是否显示全屏按钮 */
|
||||||
enableFullscreen?: boolean;
|
enableFullscreen?: boolean;
|
||||||
|
|||||||
@ -46,8 +46,6 @@
|
|||||||
@change="onChangeHandler"
|
@change="onChangeHandler"
|
||||||
@addDiffCount="onAddDiffCount"
|
@addDiffCount="onAddDiffCount"
|
||||||
></component>
|
></component>
|
||||||
|
|
||||||
<div v-if="extra && type !== 'table'" v-html="extra" class="m-form-tip"></div>
|
|
||||||
</TMagicFormItem>
|
</TMagicFormItem>
|
||||||
|
|
||||||
<TMagicTooltip v-if="config.tip" placement="left">
|
<TMagicTooltip v-if="config.tip" placement="left">
|
||||||
@ -74,8 +72,6 @@
|
|||||||
</TMagicTooltip>
|
</TMagicTooltip>
|
||||||
|
|
||||||
<component v-else v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
|
<component v-else v-bind="fieldsProps" :is="tagName" :model="lastValues" @change="onChangeHandler"></component>
|
||||||
|
|
||||||
<div v-if="extra" v-html="extra" class="m-form-tip"></div>
|
|
||||||
</TMagicFormItem>
|
</TMagicFormItem>
|
||||||
|
|
||||||
<TMagicTooltip v-if="config.tip" placement="left">
|
<TMagicTooltip v-if="config.tip" placement="left">
|
||||||
@ -100,8 +96,6 @@
|
|||||||
</TMagicTooltip>
|
</TMagicTooltip>
|
||||||
|
|
||||||
<component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
|
<component v-else v-bind="fieldsProps" :is="tagName" :model="model" @change="onChangeHandler"></component>
|
||||||
|
|
||||||
<div v-if="extra" v-html="extra" class="m-form-tip"></div>
|
|
||||||
</TMagicFormItem>
|
</TMagicFormItem>
|
||||||
|
|
||||||
<TMagicTooltip v-if="config.tip" placement="left">
|
<TMagicTooltip v-if="config.tip" placement="left">
|
||||||
@ -231,8 +225,6 @@ const text = computed(() => filterFunction(mForm, props.config.text, props));
|
|||||||
|
|
||||||
const tooltip = computed(() => filterFunction(mForm, props.config.tooltip, props));
|
const tooltip = computed(() => filterFunction(mForm, props.config.tooltip, props));
|
||||||
|
|
||||||
const extra = computed(() => filterFunction(mForm, props.config.extra, props));
|
|
||||||
|
|
||||||
const rule = computed(() => getRules(mForm, props.config.rules, props));
|
const rule = computed(() => getRules(mForm, props.config.rules, props));
|
||||||
|
|
||||||
const type = computed((): string => {
|
const type = computed((): string => {
|
||||||
@ -266,6 +258,7 @@ const formItemProps = computed(() => ({
|
|||||||
labelWidth: itemLabelWidth.value,
|
labelWidth: itemLabelWidth.value,
|
||||||
labelPosition: props.config.labelPosition,
|
labelPosition: props.config.labelPosition,
|
||||||
rules: rule.value,
|
rules: rule.value,
|
||||||
|
extra: filterFunction(mForm, props.config.extra, props),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const itemLabelWidth = computed(() => props.config.labelWidth ?? props.labelWidth);
|
const itemLabelWidth = computed(() => props.config.labelWidth ?? props.labelWidth);
|
||||||
|
|||||||
@ -1,681 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="m-fields-table-wrap">
|
|
||||||
<teleport to="body" :disabled="!isFullscreen">
|
|
||||||
<div ref="mTable" class="m-fields-table" :class="{ 'm-fields-table-item-extra': config.itemExtra }">
|
|
||||||
<span v-if="config.extra" style="color: rgba(0, 0, 0, 0.45)" v-html="config.extra"></span>
|
|
||||||
<TMagicTooltip content="拖拽可排序" placement="left-start" :disabled="config.dropSort !== true">
|
|
||||||
<TMagicTable
|
|
||||||
v-if="model[modelName]"
|
|
||||||
ref="tMagicTable"
|
|
||||||
style="width: 100%"
|
|
||||||
:row-key="config.rowKey || 'id'"
|
|
||||||
:data="data"
|
|
||||||
:lastData="lastData"
|
|
||||||
:border="config.border"
|
|
||||||
:max-height="config.maxHeight"
|
|
||||||
:default-expand-all="true"
|
|
||||||
:key="updateKey"
|
|
||||||
@select="selectHandle"
|
|
||||||
@sort-change="sortChange"
|
|
||||||
>
|
|
||||||
<TMagicTableColumn v-if="config.itemExtra && !config.dropSort" :fixed="'left'" width="30" type="expand">
|
|
||||||
<template v-slot="scope">
|
|
||||||
<span v-html="itemExtra(config.itemExtra, scope.$index)" class="m-form-tip"></span>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
|
|
||||||
<TMagicTableColumn
|
|
||||||
label="操作"
|
|
||||||
:width="config.operateColWidth || 100"
|
|
||||||
align="center"
|
|
||||||
:fixed="config.fixed === false ? undefined : 'left'"
|
|
||||||
>
|
|
||||||
<template v-slot="scope">
|
|
||||||
<slot name="operateCol" :scope="scope"></slot>
|
|
||||||
<TMagicButton
|
|
||||||
v-show="showDelete(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
size="small"
|
|
||||||
type="danger"
|
|
||||||
link
|
|
||||||
title="删除"
|
|
||||||
:icon="Delete"
|
|
||||||
@click="removeHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
></TMagicButton>
|
|
||||||
|
|
||||||
<TMagicButton
|
|
||||||
v-if="copyable(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
link
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
title="复制"
|
|
||||||
:icon="DocumentCopy"
|
|
||||||
:disabled="disabled"
|
|
||||||
@click="copyHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
></TMagicButton>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
|
|
||||||
<TMagicTableColumn v-if="sort && model[modelName] && model[modelName].length > 1" label="排序" width="60">
|
|
||||||
<template v-slot="scope">
|
|
||||||
<TMagicTooltip
|
|
||||||
v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== 0"
|
|
||||||
content="点击上移,双击置顶"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<TMagicButton
|
|
||||||
plain
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
:icon="ArrowUp"
|
|
||||||
:disabled="disabled"
|
|
||||||
link
|
|
||||||
@click="upHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
@dblclick="topHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
></TMagicButton>
|
|
||||||
</TMagicTooltip>
|
|
||||||
<TMagicTooltip
|
|
||||||
v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== model[modelName].length - 1"
|
|
||||||
content="点击下移,双击置底"
|
|
||||||
placement="top"
|
|
||||||
>
|
|
||||||
<TMagicButton
|
|
||||||
plain
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
:icon="ArrowDown"
|
|
||||||
:disabled="disabled"
|
|
||||||
link
|
|
||||||
@click="downHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
@dblclick="bottomHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
|
|
||||||
></TMagicButton>
|
|
||||||
</TMagicTooltip>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
|
|
||||||
<TMagicTableColumn
|
|
||||||
v-if="selection"
|
|
||||||
align="center"
|
|
||||||
header-align="center"
|
|
||||||
type="selection"
|
|
||||||
width="45"
|
|
||||||
></TMagicTableColumn>
|
|
||||||
|
|
||||||
<TMagicTableColumn width="60" label="序号" v-if="showIndex && config.showIndex">
|
|
||||||
<template v-slot="scope">{{ scope.$index + 1 + pagecontext * pagesize }}</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
|
|
||||||
<template v-for="(column, index) in config.items">
|
|
||||||
<TMagicTableColumn
|
|
||||||
v-if="column.type !== 'hidden' && display(column.display)"
|
|
||||||
:prop="column.name"
|
|
||||||
:width="column.width"
|
|
||||||
:label="column.label"
|
|
||||||
:sortable="column.sortable"
|
|
||||||
:sort-orders="['ascending', 'descending']"
|
|
||||||
:key="column[mForm?.keyProp || '__key'] ?? index"
|
|
||||||
:class-name="config.dropSort === true ? 'el-table__column--dropable' : ''"
|
|
||||||
>
|
|
||||||
<template #default="scope">
|
|
||||||
<Container
|
|
||||||
v-if="scope.$index > -1"
|
|
||||||
labelWidth="0"
|
|
||||||
:disabled="disabled"
|
|
||||||
:prop="getProp(scope.$index)"
|
|
||||||
:rules="column.rules"
|
|
||||||
:config="makeConfig(column, scope.row)"
|
|
||||||
:model="scope.row"
|
|
||||||
:lastValues="lastData[scope.$index]"
|
|
||||||
:is-compare="isCompare"
|
|
||||||
:size="size"
|
|
||||||
@change="changeHandler"
|
|
||||||
@addDiffCount="onAddDiffCount()"
|
|
||||||
></Container>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
</template>
|
|
||||||
</TMagicTable>
|
|
||||||
</TMagicTooltip>
|
|
||||||
<slot></slot>
|
|
||||||
|
|
||||||
<div style="display: flex; justify-content: space-between; margin: 10px 0">
|
|
||||||
<TMagicButton v-if="addable" size="small" type="primary" :disabled="disabled" plain @click="newHandler()"
|
|
||||||
>新增一行</TMagicButton
|
|
||||||
>
|
|
||||||
|
|
||||||
<div style="display: flex">
|
|
||||||
<TMagicButton
|
|
||||||
:icon="Grid"
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
@click="toggleMode"
|
|
||||||
v-if="enableToggleMode && config.enableToggleMode !== false && !isFullscreen"
|
|
||||||
>展开配置</TMagicButton
|
|
||||||
>
|
|
||||||
<TMagicButton
|
|
||||||
:icon="FullScreen"
|
|
||||||
size="small"
|
|
||||||
type="primary"
|
|
||||||
@click="toggleFullscreen"
|
|
||||||
v-if="config.enableFullscreen !== false"
|
|
||||||
>
|
|
||||||
{{ isFullscreen ? '退出全屏' : '全屏编辑' }}
|
|
||||||
</TMagicButton>
|
|
||||||
<TMagicUpload
|
|
||||||
v-if="importable"
|
|
||||||
style="display: inline-block"
|
|
||||||
ref="excelBtn"
|
|
||||||
action="/noop"
|
|
||||||
:disabled="disabled"
|
|
||||||
:on-change="excelHandler"
|
|
||||||
:auto-upload="false"
|
|
||||||
>
|
|
||||||
<TMagicButton size="small" type="success" :disabled="disabled" plain>导入EXCEL</TMagicButton>
|
|
||||||
</TMagicUpload>
|
|
||||||
<TMagicButton
|
|
||||||
v-if="importable"
|
|
||||||
size="small"
|
|
||||||
type="warning"
|
|
||||||
:disabled="disabled"
|
|
||||||
plain
|
|
||||||
@click="clearHandler()"
|
|
||||||
>清空</TMagicButton
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="bottom" style="text-align: right" v-if="config.pagination">
|
|
||||||
<TMagicPagination
|
|
||||||
layout="total, sizes, prev, pager, next, jumper"
|
|
||||||
:hide-on-single-page="model[modelName].length < pagesize"
|
|
||||||
:current-page="pagecontext + 1"
|
|
||||||
:page-sizes="[pagesize, 60, 120, 300]"
|
|
||||||
:page-size="pagesize"
|
|
||||||
:total="model[modelName].length"
|
|
||||||
@size-change="handleSizeChange"
|
|
||||||
@current-change="handleCurrentChange"
|
|
||||||
>
|
|
||||||
</TMagicPagination>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</teleport>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { computed, inject, onMounted, ref, toRefs, watchEffect } from 'vue';
|
|
||||||
import { ArrowDown, ArrowUp, Delete, DocumentCopy, FullScreen, Grid } from '@element-plus/icons-vue';
|
|
||||||
import { cloneDeep } from 'lodash-es';
|
|
||||||
import Sortable, { SortableEvent } from 'sortablejs';
|
|
||||||
|
|
||||||
import {
|
|
||||||
TMagicButton,
|
|
||||||
tMagicMessage,
|
|
||||||
TMagicPagination,
|
|
||||||
TMagicTable,
|
|
||||||
TMagicTableColumn,
|
|
||||||
TMagicTooltip,
|
|
||||||
TMagicUpload,
|
|
||||||
useZIndex,
|
|
||||||
} from '@tmagic/design';
|
|
||||||
import { asyncLoadJs } from '@tmagic/utils';
|
|
||||||
|
|
||||||
import type { ContainerChangeEventData, FormState, SortProp, TableColumnConfig, TableConfig } from '../schema';
|
|
||||||
import { display as displayFunc, initValue } from '../utils/form';
|
|
||||||
|
|
||||||
import Container from './Container.vue';
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'MFormTable',
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = withDefaults(
|
|
||||||
defineProps<{
|
|
||||||
model: any;
|
|
||||||
lastValues?: any;
|
|
||||||
isCompare?: boolean;
|
|
||||||
config: TableConfig;
|
|
||||||
name: string;
|
|
||||||
prop?: string;
|
|
||||||
labelWidth?: string;
|
|
||||||
sort?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
sortKey?: string;
|
|
||||||
text?: string;
|
|
||||||
size?: string;
|
|
||||||
enableToggleMode?: boolean;
|
|
||||||
showIndex?: boolean;
|
|
||||||
}>(),
|
|
||||||
{
|
|
||||||
prop: '',
|
|
||||||
sortKey: '',
|
|
||||||
enableToggleMode: true,
|
|
||||||
showIndex: true,
|
|
||||||
lastValues: () => ({}),
|
|
||||||
isCompare: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const emit = defineEmits(['change', 'select', 'addDiffCount']);
|
|
||||||
|
|
||||||
let timer: any | null = null;
|
|
||||||
const mForm = inject<FormState | undefined>('mForm');
|
|
||||||
|
|
||||||
const { nextZIndex } = useZIndex();
|
|
||||||
|
|
||||||
const tMagicTable = ref<InstanceType<typeof TMagicTable>>();
|
|
||||||
const excelBtn = ref<InstanceType<typeof TMagicUpload>>();
|
|
||||||
const mTable = ref<HTMLDivElement>();
|
|
||||||
|
|
||||||
const pagesize = ref(10);
|
|
||||||
const pagecontext = ref(0);
|
|
||||||
const updateKey = ref(1);
|
|
||||||
const isFullscreen = ref(false);
|
|
||||||
|
|
||||||
const modelName = computed(() => props.name || props.config.name || '');
|
|
||||||
|
|
||||||
const getDataByPage = (data: any[] = []) =>
|
|
||||||
data.filter(
|
|
||||||
(item: any, index: number) =>
|
|
||||||
index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value,
|
|
||||||
);
|
|
||||||
|
|
||||||
const pageinationData = computed(() => getDataByPage(props.model[modelName.value]));
|
|
||||||
|
|
||||||
const data = computed(() => (props.config.pagination ? pageinationData.value : props.model[modelName.value]));
|
|
||||||
|
|
||||||
const lastData = computed(() =>
|
|
||||||
props.config.pagination ? getDataByPage(props.lastValues[modelName.value]) : props.lastValues[modelName.value] || [],
|
|
||||||
);
|
|
||||||
|
|
||||||
const sortChange = ({ prop, order }: SortProp) => {
|
|
||||||
if (order === 'ascending') {
|
|
||||||
props.model[modelName.value] = props.model[modelName.value].sort((a: any, b: any) => a[prop] - b[prop]);
|
|
||||||
} else if (order === 'descending') {
|
|
||||||
props.model[modelName.value] = props.model[modelName.value].sort((a: any, b: any) => b[prop] - a[prop]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const swapArray = (index1: number, index2: number) => {
|
|
||||||
props.model[modelName.value].splice(index1, 0, props.model[modelName.value].splice(index2, 1)[0]);
|
|
||||||
|
|
||||||
if (props.sortKey) {
|
|
||||||
for (let i = props.model[modelName.value].length - 1, v = 0; i >= 0; i--, v++) {
|
|
||||||
props.model[modelName.value][v][props.sortKey] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
|
|
||||||
};
|
|
||||||
|
|
||||||
let sortable: Sortable | undefined;
|
|
||||||
const rowDrop = () => {
|
|
||||||
sortable?.destroy();
|
|
||||||
const tableEl = tMagicTable.value?.instance.$el;
|
|
||||||
const tBodyEl = tableEl?.querySelector('.el-table__body > tbody');
|
|
||||||
if (!tBodyEl) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sortable = Sortable.create(tBodyEl, {
|
|
||||||
draggable: '.tmagic-design-table-row',
|
|
||||||
filter: 'input', // 表单组件选字操作和触发拖拽会冲突,优先保证选字操作
|
|
||||||
preventOnFilter: false, // 允许选字
|
|
||||||
direction: 'vertical',
|
|
||||||
onEnd: ({ newIndex, oldIndex }: SortableEvent) => {
|
|
||||||
if (typeof newIndex === 'undefined') return;
|
|
||||||
if (typeof oldIndex === 'undefined') return;
|
|
||||||
swapArray(newIndex, oldIndex);
|
|
||||||
emit('change', props.model[modelName.value]);
|
|
||||||
mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const newHandler = async (row?: any) => {
|
|
||||||
if (props.config.max && props.model[modelName.value].length >= props.config.max) {
|
|
||||||
tMagicMessage.error(`最多新增配置不能超过${props.config.max}条`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof props.config.beforeAddRow === 'function') {
|
|
||||||
const beforeCheckRes = props.config.beforeAddRow(mForm, {
|
|
||||||
model: props.model[modelName.value],
|
|
||||||
formValue: mForm?.values,
|
|
||||||
prop: props.prop,
|
|
||||||
});
|
|
||||||
if (!beforeCheckRes) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = props.config.items;
|
|
||||||
const enumValues = props.config.enum || [];
|
|
||||||
let enumV = [];
|
|
||||||
const { length } = props.model[modelName.value];
|
|
||||||
const key = props.config.key || 'id';
|
|
||||||
let inputs: any = {};
|
|
||||||
|
|
||||||
if (enumValues.length) {
|
|
||||||
if (length >= enumValues.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
enumV = enumValues.filter((item) => {
|
|
||||||
let i = 0;
|
|
||||||
for (; i < length; i++) {
|
|
||||||
if (item[key] === props.model[modelName.value][i][key]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return i === length;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (enumV.length > 0) {
|
|
||||||
// eslint-disable-next-line prefer-destructuring
|
|
||||||
inputs = enumV[0];
|
|
||||||
}
|
|
||||||
} else if (Array.isArray(row)) {
|
|
||||||
columns.forEach((column, index) => {
|
|
||||||
column.name && (inputs[column.name] = row[index]);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (typeof props.config.defaultAdd === 'function') {
|
|
||||||
inputs = await props.config.defaultAdd(mForm, {
|
|
||||||
model: props.model[modelName.value],
|
|
||||||
formValue: mForm?.values,
|
|
||||||
});
|
|
||||||
} else if (props.config.defaultAdd) {
|
|
||||||
inputs = props.config.defaultAdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputs = await initValue(mForm, {
|
|
||||||
config: columns,
|
|
||||||
initValues: inputs,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.sortKey && length) {
|
|
||||||
inputs[props.sortKey] = props.model[modelName.value][length - 1][props.sortKey] - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
props.model[modelName.value].push(inputs);
|
|
||||||
|
|
||||||
emit('change', props.model[modelName.value], {
|
|
||||||
changeRecords: [
|
|
||||||
{
|
|
||||||
propPath: `${props.prop}.${props.model[modelName.value].length - 1}`,
|
|
||||||
value: inputs,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
if (props.config.defautSort) {
|
|
||||||
sortChange(props.config.defautSort);
|
|
||||||
} else if (props.config.defaultSort) {
|
|
||||||
sortChange(props.config.defaultSort);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.sort && props.sortKey) {
|
|
||||||
props.model[modelName.value].sort((a: any, b: any) => b[props.sortKey] - a[props.sortKey]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (props.config.dropSort) {
|
|
||||||
rowDrop();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const addable = computed(() => {
|
|
||||||
if (!props.model[modelName.value].length) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (typeof props.config.addable === 'function') {
|
|
||||||
return props.config.addable(mForm, {
|
|
||||||
model: props.model[modelName.value],
|
|
||||||
formValue: mForm?.values,
|
|
||||||
prop: props.prop,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return typeof props.config.addable === 'undefined' ? true : props.config.addable;
|
|
||||||
});
|
|
||||||
|
|
||||||
const selection = computed(() => {
|
|
||||||
if (typeof props.config.selection === 'function') {
|
|
||||||
return props.config.selection(mForm, { model: props.model[modelName.value] });
|
|
||||||
}
|
|
||||||
return props.config.selection;
|
|
||||||
});
|
|
||||||
|
|
||||||
const importable = computed(() => {
|
|
||||||
if (typeof props.config.importable === 'function') {
|
|
||||||
return props.config.importable(mForm, {
|
|
||||||
formValue: mForm?.values,
|
|
||||||
model: props.model[modelName.value],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return typeof props.config.importable === 'undefined' ? false : props.config.importable;
|
|
||||||
});
|
|
||||||
|
|
||||||
const display = (fuc: any) => displayFunc(mForm, fuc, props);
|
|
||||||
|
|
||||||
const itemExtra = (fuc: any, index: number) => {
|
|
||||||
if (typeof fuc === 'function') {
|
|
||||||
return fuc(mForm, {
|
|
||||||
values: mForm?.initValues,
|
|
||||||
model: props.model,
|
|
||||||
formValue: mForm ? mForm.values : props.model,
|
|
||||||
prop: props.prop,
|
|
||||||
index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return fuc;
|
|
||||||
};
|
|
||||||
|
|
||||||
const removeHandler = (index: number) => {
|
|
||||||
if (props.disabled) return;
|
|
||||||
props.model[modelName.value].splice(index, 1);
|
|
||||||
emit('change', props.model[modelName.value]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const selectHandle = (selection: any, row: any) => {
|
|
||||||
if (typeof props.config.selection === 'string' && props.config.selection === 'single') {
|
|
||||||
tMagicTable.value?.clearSelection();
|
|
||||||
tMagicTable.value?.toggleRowSelection(row, true);
|
|
||||||
}
|
|
||||||
emit('select', selection, row);
|
|
||||||
if (typeof props.config.onSelect === 'function') {
|
|
||||||
props.config.onSelect(mForm, { selection, row, config: props.config });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleRowSelection = (row: any, selected: boolean) => {
|
|
||||||
tMagicTable.value?.toggleRowSelection.call(tMagicTable.value, row, selected);
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeConfig = (config: TableColumnConfig, row: any) => {
|
|
||||||
const newConfig = cloneDeep(config);
|
|
||||||
if (typeof config.itemsFunction === 'function') {
|
|
||||||
newConfig.items = config.itemsFunction(row);
|
|
||||||
}
|
|
||||||
delete newConfig.display;
|
|
||||||
return newConfig;
|
|
||||||
};
|
|
||||||
|
|
||||||
const upHandler = (index: number) => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
swapArray(index, index - 1);
|
|
||||||
timer = undefined;
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
const topHandler = (index: number) => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 首先判断当前元素需要上移几个位置,置底移动到数组的第一位
|
|
||||||
const moveNum = index;
|
|
||||||
|
|
||||||
// 循环出需要一个一个上移的次数
|
|
||||||
for (let i = 0; i < moveNum; i++) {
|
|
||||||
swapArray(index, index - 1);
|
|
||||||
index -= 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const downHandler = (index: number) => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
swapArray(index, index + 1);
|
|
||||||
timer = undefined;
|
|
||||||
}, 300);
|
|
||||||
};
|
|
||||||
|
|
||||||
const bottomHandler = (index: number) => {
|
|
||||||
if (timer) {
|
|
||||||
clearTimeout(timer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 首先判断当前元素需要上移几个位置,置底移动到数组的第一位
|
|
||||||
const moveNum = props.model[modelName.value].length - 1 - index;
|
|
||||||
|
|
||||||
// 循环出需要一个一个上移的次数
|
|
||||||
for (let i = 0; i < moveNum; i++) {
|
|
||||||
swapArray(index, index + 1);
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 希望支持单行可控制是否显示删除按钮,不会影响现有逻辑
|
|
||||||
const showDelete = (index: number) => {
|
|
||||||
const deleteFunc = props.config.delete;
|
|
||||||
if (deleteFunc && typeof deleteFunc === 'function') {
|
|
||||||
return deleteFunc(props.model[modelName.value], index, mForm?.values);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const copyable = (index: number) => {
|
|
||||||
const copyableFunc = props.config.copyable;
|
|
||||||
if (copyableFunc && typeof copyableFunc === 'function') {
|
|
||||||
return copyableFunc(mForm, {
|
|
||||||
values: mForm?.initValues || {},
|
|
||||||
model: props.model,
|
|
||||||
parent: mForm?.parentValues || {},
|
|
||||||
formValue: mForm?.values || props.model,
|
|
||||||
prop: props.prop,
|
|
||||||
config: props.config,
|
|
||||||
index,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearHandler = () => {
|
|
||||||
const len = props.model[modelName.value].length;
|
|
||||||
props.model[modelName.value].splice(0, len);
|
|
||||||
mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const excelHandler = async (file: any) => {
|
|
||||||
if (!file?.raw) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(globalThis as any).XLSX) {
|
|
||||||
await asyncLoadJs('https://cdn.bootcdn.net/ajax/libs/xlsx/0.17.0/xlsx.full.min.js');
|
|
||||||
}
|
|
||||||
|
|
||||||
const reader = new FileReader();
|
|
||||||
reader.onload = () => {
|
|
||||||
const data = reader.result;
|
|
||||||
const pdata = (globalThis as any).XLSX.read(data, { type: 'array' });
|
|
||||||
pdata.SheetNames.forEach((sheetName: string) => {
|
|
||||||
const arr = (globalThis as any).XLSX.utils.sheet_to_json(pdata.Sheets[sheetName], { header: 1 });
|
|
||||||
if (arr?.[0]) {
|
|
||||||
arr.forEach((row: any) => {
|
|
||||||
newHandler(row);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setTimeout(() => {
|
|
||||||
excelBtn.value?.clearFiles();
|
|
||||||
}, 300);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
reader.readAsArrayBuffer(file.raw);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSizeChange = (val: number) => {
|
|
||||||
pagesize.value = val;
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleCurrentChange = (val: number) => {
|
|
||||||
pagecontext.value = val - 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const copyHandler = (index: number) => {
|
|
||||||
props.model[modelName.value].push(cloneDeep(props.model[modelName.value][index]));
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleMode = () => {
|
|
||||||
const calcLabelWidth = (label: string) => {
|
|
||||||
if (!label) return '0px';
|
|
||||||
const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
|
|
||||||
const chLength = label.length - zhLength;
|
|
||||||
return `${Math.max(chLength * 8 + zhLength * 20, 80)}px`;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 切换为groupList的形式
|
|
||||||
props.config.type = 'groupList';
|
|
||||||
props.config.enableToggleMode = true;
|
|
||||||
props.config.tableItems = props.config.items;
|
|
||||||
props.config.items =
|
|
||||||
props.config.groupItems ||
|
|
||||||
props.config.items.map((item: any) => {
|
|
||||||
const text = item.text || item.label;
|
|
||||||
const labelWidth = calcLabelWidth(text);
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
text,
|
|
||||||
labelWidth,
|
|
||||||
span: item.span || 12,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleFullscreen = () => {
|
|
||||||
if (!mTable.value) return;
|
|
||||||
|
|
||||||
if (isFullscreen.value) {
|
|
||||||
mTable.value.classList.remove('fixed');
|
|
||||||
isFullscreen.value = false;
|
|
||||||
} else {
|
|
||||||
mTable.value.classList.add('fixed');
|
|
||||||
mTable.value.style.zIndex = `${nextZIndex()}`;
|
|
||||||
isFullscreen.value = true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getProp = (index: number) => {
|
|
||||||
const { prop } = toRefs(props);
|
|
||||||
return `${prop.value}${prop.value ? '.' : ''}${index + 1 + pagecontext.value * pagesize.value - 1}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onAddDiffCount = () => emit('addDiffCount');
|
|
||||||
|
|
||||||
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
|
|
||||||
emit('change', props.model, eventData);
|
|
||||||
};
|
|
||||||
|
|
||||||
defineExpose({
|
|
||||||
toggleRowSelection,
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@ -5,7 +5,7 @@
|
|||||||
:is="itemComponent"
|
:is="itemComponent"
|
||||||
:value="option.value"
|
:value="option.value"
|
||||||
:key="`${option.value}`"
|
:key="`${option.value}`"
|
||||||
@click.prevent="clickHandler(option.value)"
|
@click="clickHandler(option.value)"
|
||||||
>
|
>
|
||||||
<TMagicTooltip :disabled="!Boolean(option.tooltip)" placement="top-start" :content="option.tooltip">
|
<TMagicTooltip :disabled="!Boolean(option.tooltip)" placement="top-start" :content="option.tooltip">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@ -24,7 +24,6 @@ import GroupList from './containers/GroupList.vue';
|
|||||||
import Panel from './containers/Panel.vue';
|
import Panel from './containers/Panel.vue';
|
||||||
import Row from './containers/Row.vue';
|
import Row from './containers/Row.vue';
|
||||||
import MStep from './containers/Step.vue';
|
import MStep from './containers/Step.vue';
|
||||||
import Table from './containers/Table.vue';
|
|
||||||
import Tabs from './containers/Tabs.vue';
|
import Tabs from './containers/Tabs.vue';
|
||||||
import Cascader from './fields/Cascader.vue';
|
import Cascader from './fields/Cascader.vue';
|
||||||
import Checkbox from './fields/Checkbox.vue';
|
import Checkbox from './fields/Checkbox.vue';
|
||||||
@ -46,6 +45,7 @@ import Text from './fields/Text.vue';
|
|||||||
import Textarea from './fields/Textarea.vue';
|
import Textarea from './fields/Textarea.vue';
|
||||||
import Time from './fields/Time.vue';
|
import Time from './fields/Time.vue';
|
||||||
import Timerange from './fields/Timerange.vue';
|
import Timerange from './fields/Timerange.vue';
|
||||||
|
import Table from './table/Table.vue';
|
||||||
import { setConfig } from './utils/config';
|
import { setConfig } from './utils/config';
|
||||||
import Form from './Form.vue';
|
import Form from './Form.vue';
|
||||||
import FormDialog from './FormDialog.vue';
|
import FormDialog from './FormDialog.vue';
|
||||||
@ -66,7 +66,7 @@ export { default as MFieldset } from './containers/Fieldset.vue';
|
|||||||
export { default as MPanel } from './containers/Panel.vue';
|
export { default as MPanel } from './containers/Panel.vue';
|
||||||
export { default as MRow } from './containers/Row.vue';
|
export { default as MRow } from './containers/Row.vue';
|
||||||
export { default as MTabs } from './containers/Tabs.vue';
|
export { default as MTabs } from './containers/Tabs.vue';
|
||||||
export { default as MTable } from './containers/Table.vue';
|
export { default as MTable } from './table/Table.vue';
|
||||||
export { default as MGroupList } from './containers/GroupList.vue';
|
export { default as MGroupList } from './containers/GroupList.vue';
|
||||||
export { default as MText } from './fields/Text.vue';
|
export { default as MText } from './fields/Text.vue';
|
||||||
export { default as MNumber } from './fields/Number.vue';
|
export { default as MNumber } from './fields/Number.vue';
|
||||||
|
|||||||
84
packages/form/src/table/ActionsColumn.vue
Normal file
84
packages/form/src/table/ActionsColumn.vue
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<slot name="operateCol" :scope="{ $index: index, row: row }"></slot>
|
||||||
|
<TMagicButton
|
||||||
|
v-show="showDelete(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
|
link
|
||||||
|
title="删除"
|
||||||
|
:icon="Delete"
|
||||||
|
@click="removeHandler(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
></TMagicButton>
|
||||||
|
|
||||||
|
<TMagicButton
|
||||||
|
v-if="copyable(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
title="复制"
|
||||||
|
:icon="DocumentCopy"
|
||||||
|
:disabled="disabled"
|
||||||
|
@click="copyHandler(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
></TMagicButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { inject } from 'vue';
|
||||||
|
import { Delete, DocumentCopy } from '@element-plus/icons-vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import { TMagicButton } from '@tmagic/design';
|
||||||
|
|
||||||
|
import type { FormState, TableConfig } from '../schema';
|
||||||
|
|
||||||
|
const emit = defineEmits(['change']);
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
config: TableConfig;
|
||||||
|
model: any;
|
||||||
|
name: string | number;
|
||||||
|
disabled?: boolean;
|
||||||
|
currentPage: number;
|
||||||
|
pageSize: number;
|
||||||
|
index: number;
|
||||||
|
row: any;
|
||||||
|
prop?: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
|
||||||
|
const removeHandler = (index: number) => {
|
||||||
|
if (props.disabled) return;
|
||||||
|
props.model[props.name].splice(index, 1);
|
||||||
|
emit('change', props.model[props.name]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyHandler = (index: number) => {
|
||||||
|
props.model[props.name].push(cloneDeep(props.model[props.name][index]));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 希望支持单行可控制是否显示删除按钮,不会影响现有逻辑
|
||||||
|
const showDelete = (index: number) => {
|
||||||
|
const deleteFunc = props.config.delete;
|
||||||
|
if (deleteFunc && typeof deleteFunc === 'function') {
|
||||||
|
return deleteFunc(props.model[props.name], index, mForm?.values);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const copyable = (index: number) => {
|
||||||
|
const copyableFunc = props.config.copyable;
|
||||||
|
if (copyableFunc && typeof copyableFunc === 'function') {
|
||||||
|
return copyableFunc(mForm, {
|
||||||
|
values: mForm?.initValues || {},
|
||||||
|
model: props.model,
|
||||||
|
parent: mForm?.parentValues || {},
|
||||||
|
formValue: mForm?.values || props.model,
|
||||||
|
prop: props.prop,
|
||||||
|
config: props.config,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
101
packages/form/src/table/SortColumn.vue
Normal file
101
packages/form/src/table/SortColumn.vue
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<template>
|
||||||
|
<TMagicTooltip v-if="index + 1 + currentPage * pageSize - 1 !== 0" content="点击上移,双击置顶" placement="top">
|
||||||
|
<TMagicButton
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
:icon="ArrowUp"
|
||||||
|
:disabled="disabled"
|
||||||
|
link
|
||||||
|
@click="upHandler(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
@dblclick="topHandler(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
></TMagicButton>
|
||||||
|
</TMagicTooltip>
|
||||||
|
<TMagicTooltip
|
||||||
|
v-if="index + 1 + currentPage * pageSize - 1 !== model[name].length - 1"
|
||||||
|
content="点击下移,双击置底"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<TMagicButton
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
:icon="ArrowDown"
|
||||||
|
:disabled="disabled"
|
||||||
|
link
|
||||||
|
@click="downHandler(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
@dblclick="bottomHandler(index + 1 + currentPage * pageSize - 1)"
|
||||||
|
></TMagicButton>
|
||||||
|
</TMagicTooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ArrowDown, ArrowUp } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { TMagicButton, TMagicTooltip } from '@tmagic/design';
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
index: number;
|
||||||
|
disabled?: boolean;
|
||||||
|
currentPage: number;
|
||||||
|
pageSize: number;
|
||||||
|
name: string | number;
|
||||||
|
model: any;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['swap']);
|
||||||
|
|
||||||
|
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||||
|
|
||||||
|
const upHandler = (index: number) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
emit('swap', index, index - 1);
|
||||||
|
timer = null;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const topHandler = (index: number) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先判断当前元素需要上移几个位置,置底移动到数组的第一位
|
||||||
|
const moveNum = index;
|
||||||
|
|
||||||
|
// 循环出需要一个一个上移的次数
|
||||||
|
for (let i = 0; i < moveNum; i++) {
|
||||||
|
emit('swap', index, index - 1);
|
||||||
|
index -= 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const downHandler = (index: number) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = setTimeout(() => {
|
||||||
|
emit('swap', index, index + 1);
|
||||||
|
timer = null;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const bottomHandler = (index: number) => {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 首先判断当前元素需要上移几个位置,置底移动到数组的第一位
|
||||||
|
const moveNum = props.model[props.name].length - 1 - index;
|
||||||
|
|
||||||
|
// 循环出需要一个一个上移的次数
|
||||||
|
for (let i = 0; i < moveNum; i++) {
|
||||||
|
emit('swap', index, index + 1);
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
170
packages/form/src/table/Table.vue
Normal file
170
packages/form/src/table/Table.vue
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
<template>
|
||||||
|
<div class="m-fields-table-wrap">
|
||||||
|
<teleport to="body" :disabled="!isFullscreen">
|
||||||
|
<div ref="mTable" class="m-fields-table" :class="{ 'm-fields-table-item-extra': config.itemExtra }">
|
||||||
|
<span v-if="config.extra" style="color: rgba(0, 0, 0, 0.45)" v-html="config.extra"></span>
|
||||||
|
<TMagicTooltip content="拖拽可排序" placement="left-start" :disabled="config.dropSort !== true">
|
||||||
|
<TMagicTable
|
||||||
|
v-if="model[modelName]"
|
||||||
|
ref="tMagicTable"
|
||||||
|
style="width: 100%"
|
||||||
|
show-header
|
||||||
|
:row-key="config.rowKey || 'id'"
|
||||||
|
:columns="columns"
|
||||||
|
:data="data"
|
||||||
|
:border="config.border"
|
||||||
|
:max-height="config.maxHeight"
|
||||||
|
:default-expand-all="true"
|
||||||
|
:key="updateKey"
|
||||||
|
@select="selectHandle"
|
||||||
|
@sort-change="sortChangeHandler"
|
||||||
|
></TMagicTable>
|
||||||
|
</TMagicTooltip>
|
||||||
|
|
||||||
|
<slot></slot>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: space-between; margin: 10px 0">
|
||||||
|
<TMagicButton v-if="addable" size="small" type="primary" :disabled="disabled" plain @click="newHandler()"
|
||||||
|
>新增一行</TMagicButton
|
||||||
|
>
|
||||||
|
|
||||||
|
<div style="display: flex">
|
||||||
|
<TMagicButton
|
||||||
|
:icon="Grid"
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
@click="toggleMode"
|
||||||
|
v-if="enableToggleMode && config.enableToggleMode !== false && !isFullscreen"
|
||||||
|
>展开配置</TMagicButton
|
||||||
|
>
|
||||||
|
<TMagicButton
|
||||||
|
:icon="FullScreen"
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
@click="toggleFullscreen"
|
||||||
|
v-if="config.enableFullscreen !== false"
|
||||||
|
>
|
||||||
|
{{ isFullscreen ? '退出全屏' : '全屏编辑' }}
|
||||||
|
</TMagicButton>
|
||||||
|
<TMagicUpload
|
||||||
|
v-if="importable"
|
||||||
|
style="display: inline-block"
|
||||||
|
ref="excelBtn"
|
||||||
|
action="/noop"
|
||||||
|
:disabled="disabled"
|
||||||
|
:on-change="excelHandler"
|
||||||
|
:auto-upload="false"
|
||||||
|
>
|
||||||
|
<TMagicButton size="small" type="success" :disabled="disabled" plain>导入EXCEL</TMagicButton>
|
||||||
|
</TMagicUpload>
|
||||||
|
<TMagicButton v-if="importable" size="small" type="warning" :disabled="disabled" plain @click="clearHandler"
|
||||||
|
>清空</TMagicButton
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="bottom" style="text-align: right" v-if="config.pagination">
|
||||||
|
<TMagicPagination
|
||||||
|
layout="total, sizes, prev, pager, next, jumper"
|
||||||
|
:hide-on-single-page="model[modelName].length < pageSize"
|
||||||
|
:current-page="currentPage + 1"
|
||||||
|
:page-sizes="[pageSize, 60, 120, 300]"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="model[modelName].length"
|
||||||
|
@size-change="handleSizeChange"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
>
|
||||||
|
</TMagicPagination>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</teleport>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, ref, useTemplateRef } from 'vue';
|
||||||
|
import { FullScreen, Grid } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { TMagicButton, TMagicPagination, TMagicTable, TMagicTooltip, TMagicUpload } from '@tmagic/design';
|
||||||
|
|
||||||
|
import type { SortProp } from '../schema';
|
||||||
|
import { sortChange } from '../utils/form';
|
||||||
|
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
import { useAdd } from './useAdd';
|
||||||
|
import { useFullscreen } from './useFullscreen';
|
||||||
|
import { useImport } from './useImport';
|
||||||
|
import { usePagination } from './usePagination';
|
||||||
|
import { useSelection } from './useSelection';
|
||||||
|
import { useSortable } from './useSortable';
|
||||||
|
import { useTableColumns } from './useTableColumns';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MFormTable',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<TableProps>(), {
|
||||||
|
prop: '',
|
||||||
|
sortKey: '',
|
||||||
|
enableToggleMode: true,
|
||||||
|
showIndex: true,
|
||||||
|
lastValues: () => ({}),
|
||||||
|
isCompare: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'select', 'addDiffCount']);
|
||||||
|
|
||||||
|
const modelName = computed(() => props.name || props.config.name || '');
|
||||||
|
const tMagicTableRef = useTemplateRef<InstanceType<typeof TMagicTable>>('tMagicTable');
|
||||||
|
|
||||||
|
const { pageSize, currentPage, paginationData, handleSizeChange, handleCurrentChange } = usePagination(
|
||||||
|
props,
|
||||||
|
modelName,
|
||||||
|
);
|
||||||
|
|
||||||
|
const { addable, newHandler } = useAdd(props, emit);
|
||||||
|
const { columns } = useTableColumns(props, emit, currentPage, pageSize, modelName);
|
||||||
|
useSortable(props, emit, tMagicTableRef, modelName);
|
||||||
|
const { isFullscreen, toggleFullscreen } = useFullscreen();
|
||||||
|
const { importable, excelHandler, clearHandler } = useImport(props, emit, newHandler);
|
||||||
|
const { selectHandle, toggleRowSelection } = useSelection(props, emit, tMagicTableRef);
|
||||||
|
|
||||||
|
const updateKey = ref(1);
|
||||||
|
|
||||||
|
const data = computed(() => (props.config.pagination ? paginationData.value : props.model[modelName.value]));
|
||||||
|
|
||||||
|
const toggleMode = () => {
|
||||||
|
const calcLabelWidth = (label: string) => {
|
||||||
|
if (!label) return '0px';
|
||||||
|
const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
|
||||||
|
const chLength = label.length - zhLength;
|
||||||
|
return `${Math.max(chLength * 8 + zhLength * 20, 80)}px`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 切换为groupList的形式
|
||||||
|
props.config.type = 'groupList';
|
||||||
|
props.config.enableToggleMode = true;
|
||||||
|
props.config.tableItems = props.config.items;
|
||||||
|
props.config.items =
|
||||||
|
props.config.groupItems ||
|
||||||
|
props.config.items.map((item: any) => {
|
||||||
|
const text = item.text || item.label;
|
||||||
|
const labelWidth = calcLabelWidth(text);
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
text,
|
||||||
|
labelWidth,
|
||||||
|
span: item.span || 12,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const sortChangeHandler = (sortOptions: SortProp) => {
|
||||||
|
const modelName = props.name || props.config.name || '';
|
||||||
|
sortChange(props.model[modelName], sortOptions);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
toggleRowSelection,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
18
packages/form/src/table/type.ts
Normal file
18
packages/form/src/table/type.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { TableConfig } from '@tmagic/form-schema';
|
||||||
|
|
||||||
|
export interface TableProps {
|
||||||
|
model: any;
|
||||||
|
lastValues?: any;
|
||||||
|
isCompare?: boolean;
|
||||||
|
config: TableConfig;
|
||||||
|
name: string;
|
||||||
|
prop?: string;
|
||||||
|
labelWidth?: string;
|
||||||
|
sort?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
|
sortKey?: string;
|
||||||
|
text?: string;
|
||||||
|
size?: string;
|
||||||
|
enableToggleMode?: boolean;
|
||||||
|
showIndex?: boolean;
|
||||||
|
}
|
||||||
111
packages/form/src/table/useAdd.ts
Normal file
111
packages/form/src/table/useAdd.ts
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import { computed, inject } from 'vue';
|
||||||
|
|
||||||
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
|
import type { FormState } from '@tmagic/form-schema';
|
||||||
|
|
||||||
|
import { initValue } from '../utils/form';
|
||||||
|
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
|
||||||
|
export const useAdd = (
|
||||||
|
props: TableProps,
|
||||||
|
emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void,
|
||||||
|
) => {
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
|
||||||
|
const addable = computed(() => {
|
||||||
|
const modelName = props.name || props.config.name || '';
|
||||||
|
if (!props.model[modelName].length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeof props.config.addable === 'function') {
|
||||||
|
return props.config.addable(mForm, {
|
||||||
|
model: props.model[modelName],
|
||||||
|
formValue: mForm?.values,
|
||||||
|
prop: props.prop,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return typeof props.config.addable === 'undefined' ? true : props.config.addable;
|
||||||
|
});
|
||||||
|
|
||||||
|
const newHandler = async (row?: any) => {
|
||||||
|
const modelName = props.name || props.config.name || '';
|
||||||
|
|
||||||
|
if (props.config.max && props.model[modelName].length >= props.config.max) {
|
||||||
|
tMagicMessage.error(`最多新增配置不能超过${props.config.max}条`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof props.config.beforeAddRow === 'function') {
|
||||||
|
const beforeCheckRes = props.config.beforeAddRow(mForm, {
|
||||||
|
model: props.model[modelName],
|
||||||
|
formValue: mForm?.values,
|
||||||
|
prop: props.prop,
|
||||||
|
});
|
||||||
|
if (!beforeCheckRes) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = props.config.items;
|
||||||
|
const enumValues = props.config.enum || [];
|
||||||
|
let enumV = [];
|
||||||
|
const { length } = props.model[modelName];
|
||||||
|
const key = props.config.key || 'id';
|
||||||
|
let inputs: any = {};
|
||||||
|
|
||||||
|
if (enumValues.length) {
|
||||||
|
if (length >= enumValues.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enumV = enumValues.filter((item) => {
|
||||||
|
let i = 0;
|
||||||
|
for (; i < length; i++) {
|
||||||
|
if (item[key] === props.model[modelName][i][key]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i === length;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (enumV.length > 0) {
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
inputs = enumV[0];
|
||||||
|
}
|
||||||
|
} else if (Array.isArray(row)) {
|
||||||
|
columns.forEach((column, index) => {
|
||||||
|
column.name && (inputs[column.name] = row[index]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (typeof props.config.defaultAdd === 'function') {
|
||||||
|
inputs = await props.config.defaultAdd(mForm, {
|
||||||
|
model: props.model[modelName],
|
||||||
|
formValue: mForm?.values,
|
||||||
|
});
|
||||||
|
} else if (props.config.defaultAdd) {
|
||||||
|
inputs = props.config.defaultAdd;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputs = await initValue(mForm, {
|
||||||
|
config: columns,
|
||||||
|
initValues: inputs,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.sortKey && length) {
|
||||||
|
inputs[props.sortKey] = props.model[modelName][length - 1][props.sortKey] - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('change', [...props.model[modelName], inputs], {
|
||||||
|
changeRecords: [
|
||||||
|
{
|
||||||
|
propPath: `${props.prop}.${props.model[modelName].length}`,
|
||||||
|
value: inputs,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
addable,
|
||||||
|
newHandler,
|
||||||
|
};
|
||||||
|
};
|
||||||
28
packages/form/src/table/useFullscreen.ts
Normal file
28
packages/form/src/table/useFullscreen.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { ref, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
|
import { useZIndex } from '@tmagic/design';
|
||||||
|
|
||||||
|
export const useFullscreen = () => {
|
||||||
|
const isFullscreen = ref(false);
|
||||||
|
const mTableEl = useTemplateRef<HTMLDivElement>('mTable');
|
||||||
|
|
||||||
|
const { nextZIndex } = useZIndex();
|
||||||
|
|
||||||
|
const toggleFullscreen = () => {
|
||||||
|
if (!mTableEl.value) return;
|
||||||
|
|
||||||
|
if (isFullscreen.value) {
|
||||||
|
mTableEl.value.classList.remove('fixed');
|
||||||
|
isFullscreen.value = false;
|
||||||
|
} else {
|
||||||
|
mTableEl.value.classList.add('fixed');
|
||||||
|
mTableEl.value.style.zIndex = `${nextZIndex()}`;
|
||||||
|
isFullscreen.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isFullscreen,
|
||||||
|
toggleFullscreen,
|
||||||
|
};
|
||||||
|
};
|
||||||
68
packages/form/src/table/useImport.ts
Normal file
68
packages/form/src/table/useImport.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { computed, inject, useTemplateRef } from 'vue';
|
||||||
|
|
||||||
|
import type { TMagicUpload } from '@tmagic/design';
|
||||||
|
import type { FormState } from '@tmagic/form-schema';
|
||||||
|
import { asyncLoadJs } from '@tmagic/utils';
|
||||||
|
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
|
||||||
|
export const useImport = (
|
||||||
|
props: TableProps,
|
||||||
|
emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void,
|
||||||
|
newHandler: (row: any) => void,
|
||||||
|
) => {
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
const modelName = computed(() => props.name || props.config.name || '');
|
||||||
|
const importable = computed(() => {
|
||||||
|
if (typeof props.config.importable === 'function') {
|
||||||
|
return props.config.importable(mForm, {
|
||||||
|
formValue: mForm?.values,
|
||||||
|
model: props.model[modelName.value],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return typeof props.config.importable === 'undefined' ? false : props.config.importable;
|
||||||
|
});
|
||||||
|
|
||||||
|
const excelBtn = useTemplateRef<InstanceType<typeof TMagicUpload>>('excelBtn');
|
||||||
|
|
||||||
|
const excelHandler = async (file: any) => {
|
||||||
|
if (!file?.raw) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(globalThis as any).XLSX) {
|
||||||
|
await asyncLoadJs('https://cdn.bootcdn.net/ajax/libs/xlsx/0.17.0/xlsx.full.min.js');
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = () => {
|
||||||
|
const data = reader.result;
|
||||||
|
const pdata = (globalThis as any).XLSX.read(data, { type: 'array' });
|
||||||
|
pdata.SheetNames.forEach((sheetName: string) => {
|
||||||
|
const arr = (globalThis as any).XLSX.utils.sheet_to_json(pdata.Sheets[sheetName], { header: 1 });
|
||||||
|
if (arr?.[0]) {
|
||||||
|
arr.forEach((row: any) => {
|
||||||
|
newHandler(row);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
excelBtn.value?.clearFiles();
|
||||||
|
}, 300);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file.raw);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearHandler = () => {
|
||||||
|
emit('change', []);
|
||||||
|
mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
importable,
|
||||||
|
excelHandler,
|
||||||
|
clearHandler,
|
||||||
|
};
|
||||||
|
};
|
||||||
30
packages/form/src/table/usePagination.ts
Normal file
30
packages/form/src/table/usePagination.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { computed, type Ref, ref } from 'vue';
|
||||||
|
|
||||||
|
import { getDataByPage } from '../utils/form';
|
||||||
|
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
|
||||||
|
export const usePagination = (props: TableProps, modelName: Ref<string | number>) => {
|
||||||
|
const pageSize = ref(10);
|
||||||
|
/**
|
||||||
|
* 当前页码
|
||||||
|
*/
|
||||||
|
const currentPage = ref(0);
|
||||||
|
|
||||||
|
const paginationData = computed(() => getDataByPage(props.model[modelName.value], currentPage.value, pageSize.value));
|
||||||
|
const handleSizeChange = (val: number) => {
|
||||||
|
pageSize.value = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCurrentChange = (val: number) => {
|
||||||
|
currentPage.value = val - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
pageSize,
|
||||||
|
currentPage,
|
||||||
|
paginationData,
|
||||||
|
handleSizeChange,
|
||||||
|
handleCurrentChange,
|
||||||
|
};
|
||||||
|
};
|
||||||
34
packages/form/src/table/useSelection.ts
Normal file
34
packages/form/src/table/useSelection.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { inject, type ShallowRef } from 'vue';
|
||||||
|
|
||||||
|
import type { TMagicTable } from '@tmagic/design';
|
||||||
|
import type { FormState } from '@tmagic/form-schema';
|
||||||
|
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
|
||||||
|
export const useSelection = (
|
||||||
|
props: TableProps,
|
||||||
|
emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void,
|
||||||
|
tMagicTableRef: ShallowRef<InstanceType<typeof TMagicTable> | null>,
|
||||||
|
) => {
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
|
||||||
|
const selectHandle = (selection: any, row: any) => {
|
||||||
|
if (typeof props.config.selection === 'string' && props.config.selection === 'single') {
|
||||||
|
tMagicTableRef.value?.clearSelection();
|
||||||
|
tMagicTableRef.value?.toggleRowSelection(row, true);
|
||||||
|
}
|
||||||
|
emit('select', selection, row);
|
||||||
|
if (typeof props.config.onSelect === 'function') {
|
||||||
|
props.config.onSelect(mForm, { selection, row, config: props.config });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleRowSelection = (row: any, selected: boolean) => {
|
||||||
|
tMagicTableRef.value?.toggleRowSelection.call(tMagicTableRef.value?.getTableRef(), row, selected);
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
selectHandle,
|
||||||
|
toggleRowSelection,
|
||||||
|
};
|
||||||
|
};
|
||||||
48
packages/form/src/table/useSortable.ts
Normal file
48
packages/form/src/table/useSortable.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { inject, type Ref, type ShallowRef, watchEffect } from 'vue';
|
||||||
|
import Sortable, { type SortableEvent } from 'sortablejs';
|
||||||
|
|
||||||
|
import { type TMagicTable } from '@tmagic/design';
|
||||||
|
import type { FormState } from '@tmagic/form-schema';
|
||||||
|
|
||||||
|
import { sortArray } from '../utils/form';
|
||||||
|
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
|
||||||
|
export const useSortable = (
|
||||||
|
props: TableProps,
|
||||||
|
emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void,
|
||||||
|
tMagicTableRef: ShallowRef<InstanceType<typeof TMagicTable> | null>,
|
||||||
|
modelName: Ref<string | number>,
|
||||||
|
) => {
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
|
||||||
|
let sortable: Sortable | undefined;
|
||||||
|
const rowDrop = () => {
|
||||||
|
sortable?.destroy();
|
||||||
|
const tableEl = tMagicTableRef.value?.getEl();
|
||||||
|
const tBodyEl = tableEl?.querySelector('.el-table__body > tbody');
|
||||||
|
if (!tBodyEl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sortable = Sortable.create(tBodyEl, {
|
||||||
|
draggable: '.tmagic-design-table-row',
|
||||||
|
filter: 'input', // 表单组件选字操作和触发拖拽会冲突,优先保证选字操作
|
||||||
|
preventOnFilter: false, // 允许选字
|
||||||
|
direction: 'vertical',
|
||||||
|
onEnd: ({ newIndex, oldIndex }: SortableEvent) => {
|
||||||
|
if (typeof newIndex === 'undefined') return;
|
||||||
|
if (typeof oldIndex === 'undefined') return;
|
||||||
|
const newData = sortArray(props.model[modelName.value], newIndex, oldIndex, props.sortKey);
|
||||||
|
|
||||||
|
emit('change', newData);
|
||||||
|
mForm?.$emit('field-change', newData);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (props.config.dropSort) {
|
||||||
|
rowDrop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
193
packages/form/src/table/useTableColumns.ts
Normal file
193
packages/form/src/table/useTableColumns.ts
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
import { computed, h, inject, type Ref } from 'vue';
|
||||||
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
|
import type { TableColumnOptions } from '@tmagic/design';
|
||||||
|
import type { FormState, TableColumnConfig } from '@tmagic/form-schema';
|
||||||
|
|
||||||
|
import Container from '../containers/Container.vue';
|
||||||
|
import type { ContainerChangeEventData } from '../schema';
|
||||||
|
import { display as displayFunc, getDataByPage, sortArray } from '../utils/form';
|
||||||
|
|
||||||
|
import ActionsColumn from './ActionsColumn.vue';
|
||||||
|
import SortColumn from './SortColumn.vue';
|
||||||
|
import type { TableProps } from './type';
|
||||||
|
|
||||||
|
export const useTableColumns = (
|
||||||
|
props: TableProps,
|
||||||
|
emit: (event: 'select' | 'change' | 'addDiffCount', ...args: any[]) => void,
|
||||||
|
currentPage: Ref<number>,
|
||||||
|
pageSize: Ref<number>,
|
||||||
|
modelName: Ref<string | number>,
|
||||||
|
) => {
|
||||||
|
const mForm = inject<FormState | undefined>('mForm');
|
||||||
|
|
||||||
|
const display = (fuc: any) => displayFunc(mForm, fuc, props);
|
||||||
|
|
||||||
|
const lastData = computed(() =>
|
||||||
|
props.config.pagination
|
||||||
|
? getDataByPage(props.lastValues[modelName.value], currentPage.value, pageSize.value)
|
||||||
|
: props.lastValues[modelName.value] || [],
|
||||||
|
);
|
||||||
|
|
||||||
|
const itemExtra = (fuc: any, index: number) => {
|
||||||
|
if (typeof fuc === 'function') {
|
||||||
|
return fuc(mForm, {
|
||||||
|
values: mForm?.initValues,
|
||||||
|
model: props.model,
|
||||||
|
formValue: mForm ? mForm.values : props.model,
|
||||||
|
prop: props.prop,
|
||||||
|
index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return fuc;
|
||||||
|
};
|
||||||
|
|
||||||
|
const selection = computed(() => {
|
||||||
|
if (typeof props.config.selection === 'function') {
|
||||||
|
return props.config.selection(mForm, { model: props.model[modelName.value] });
|
||||||
|
}
|
||||||
|
return props.config.selection;
|
||||||
|
});
|
||||||
|
|
||||||
|
const getProp = (index: number) => {
|
||||||
|
return `${props.prop}${props.prop ? '.' : ''}${index + 1 + currentPage.value * pageSize.value - 1}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const makeConfig = (config: TableColumnConfig, row: any) => {
|
||||||
|
const newConfig = cloneDeep(config);
|
||||||
|
if (typeof config.itemsFunction === 'function') {
|
||||||
|
newConfig.items = config.itemsFunction(row);
|
||||||
|
}
|
||||||
|
delete newConfig.display;
|
||||||
|
return newConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
const changeHandler = (v: any, eventData: ContainerChangeEventData) => {
|
||||||
|
emit('change', props.model, eventData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onAddDiffCount = () => emit('addDiffCount');
|
||||||
|
|
||||||
|
const columns = computed<TableColumnOptions[]>(() => {
|
||||||
|
const columns: TableColumnOptions[] = [];
|
||||||
|
|
||||||
|
if (props.config.itemExtra && !props.config.dropSort) {
|
||||||
|
columns.push({
|
||||||
|
props: {
|
||||||
|
fixed: 'left',
|
||||||
|
width: 30,
|
||||||
|
type: 'expand',
|
||||||
|
},
|
||||||
|
cell: ({ $index }: any) =>
|
||||||
|
h('span', {
|
||||||
|
innerHTML: itemExtra(props.config.itemExtra, $index),
|
||||||
|
class: 'm-form-tip',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.push({
|
||||||
|
props: {
|
||||||
|
label: '操作',
|
||||||
|
fixed: props.config.fixed === false ? undefined : 'left',
|
||||||
|
width: props.config.operateColWidth || 100,
|
||||||
|
align: 'center',
|
||||||
|
},
|
||||||
|
cell: ({ row, $index }: any) =>
|
||||||
|
h(ActionsColumn, {
|
||||||
|
row,
|
||||||
|
index: $index,
|
||||||
|
model: props.model,
|
||||||
|
config: props.config,
|
||||||
|
prop: props.prop,
|
||||||
|
disabled: props.disabled,
|
||||||
|
name: modelName.value,
|
||||||
|
currentPage: currentPage.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
onChange: (v: any) => {
|
||||||
|
emit('change', v);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (props.sort && props.model[modelName.value] && props.model[modelName.value].length > 1) {
|
||||||
|
columns.push({
|
||||||
|
props: {
|
||||||
|
label: '排序',
|
||||||
|
width: 80,
|
||||||
|
},
|
||||||
|
cell: ({ $index }: any) =>
|
||||||
|
h(SortColumn, {
|
||||||
|
index: $index,
|
||||||
|
model: props.model,
|
||||||
|
disabled: props.disabled,
|
||||||
|
name: modelName.value,
|
||||||
|
currentPage: currentPage.value,
|
||||||
|
pageSize: pageSize.value,
|
||||||
|
onSwap: (index1: number, index2: number) => {
|
||||||
|
const newData = sortArray(props.model[modelName.value], index1, index2, props.sortKey);
|
||||||
|
emit('change', newData);
|
||||||
|
mForm?.$emit('field-change', newData);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selection.value) {
|
||||||
|
columns.push({
|
||||||
|
props: {
|
||||||
|
align: 'center',
|
||||||
|
headerAlign: 'center',
|
||||||
|
type: 'selection',
|
||||||
|
width: 45,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.showIndex && props.config.showIndex) {
|
||||||
|
columns.push({
|
||||||
|
props: {
|
||||||
|
label: '序号',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
cell: ({ $index }: any) => h('span', $index + 1 + currentPage.value * pageSize.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const column of props.config.items) {
|
||||||
|
if (column.type !== 'hidden' && display(column.display)) {
|
||||||
|
columns.push({
|
||||||
|
props: {
|
||||||
|
prop: column.name,
|
||||||
|
label: column.label,
|
||||||
|
width: column.width,
|
||||||
|
sortable: column.sortable,
|
||||||
|
sortOrders: ['ascending', 'descending'],
|
||||||
|
class: props.config.dropSort === true ? 'el-table__column--dropable' : '',
|
||||||
|
},
|
||||||
|
cell: ({ row, $index }: any) =>
|
||||||
|
h(Container, {
|
||||||
|
labelWidth: '0',
|
||||||
|
disabled: props.disabled,
|
||||||
|
prop: getProp($index),
|
||||||
|
rules: column.rules,
|
||||||
|
config: makeConfig(column, row),
|
||||||
|
model: row,
|
||||||
|
lastValues: lastData.value[$index],
|
||||||
|
isCompare: props.isCompare,
|
||||||
|
size: props.size,
|
||||||
|
onChange: changeHandler,
|
||||||
|
onAddDiffCount,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
columns,
|
||||||
|
};
|
||||||
|
};
|
||||||
@ -19,17 +19,21 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table {
|
.tmagic-design-table {
|
||||||
.cell > div.m-form-container {
|
.cell > div.m-form-container {
|
||||||
display: block;
|
display: block;
|
||||||
|
|
||||||
|
&.has-tip {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-tabs {
|
.tmagic-design-tabs {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-form-item.tmagic-form-hidden {
|
.tmagic-design-form-item.tmagic-form-hidden {
|
||||||
> .el-form-item__label {
|
> .el-form-item__label {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
@ -39,5 +43,9 @@
|
|||||||
> .t-form__label {
|
> .t-form__label {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> .t-form__controls {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import {
|
|||||||
FormValue,
|
FormValue,
|
||||||
HtmlField,
|
HtmlField,
|
||||||
Rule,
|
Rule,
|
||||||
|
SortProp,
|
||||||
TabPaneConfig,
|
TabPaneConfig,
|
||||||
TypeFunction,
|
TypeFunction,
|
||||||
} from '../schema';
|
} from '../schema';
|
||||||
@ -136,6 +137,18 @@ const initValueItem = function (
|
|||||||
|
|
||||||
setValue(mForm, value, initValue, item);
|
setValue(mForm, value, initValue, item);
|
||||||
|
|
||||||
|
if (type === 'table') {
|
||||||
|
if (item.defautSort) {
|
||||||
|
sortChange(value[name], item.defautSort);
|
||||||
|
} else if (item.defaultSort) {
|
||||||
|
sortChange(value[name], item.defaultSort);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.sort && item.sortKey) {
|
||||||
|
value[name].sort((a: any, b: any) => b[item.sortKey] - a[item.sortKey]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -297,3 +310,36 @@ export const datetimeFormatter = (
|
|||||||
}
|
}
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getDataByPage = (data: any[] = [], pagecontext: number, pagesize: number) =>
|
||||||
|
data.filter(
|
||||||
|
(item: any, index: number) => index >= pagecontext * pagesize && index + 1 <= (pagecontext + 1) * pagesize,
|
||||||
|
);
|
||||||
|
|
||||||
|
export const sortArray = (data: any[], newIndex: number, oldIndex: number, sortKey?: string) => {
|
||||||
|
if (newIndex === oldIndex) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newIndex < 0 || newIndex >= data.length || oldIndex < 0 || oldIndex >= data.length) {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newData = data.toSpliced(newIndex, 0, ...data.splice(oldIndex, 1));
|
||||||
|
|
||||||
|
if (sortKey) {
|
||||||
|
for (let i = newData.length - 1, v = 0; i >= 0; i--, v++) {
|
||||||
|
newData[v][sortKey] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return newData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const sortChange = (data: any[], { prop, order }: SortProp) => {
|
||||||
|
if (order === 'ascending') {
|
||||||
|
data = data.sort((a: any, b: any) => a[prop] - b[prop]);
|
||||||
|
} else if (order === 'descending') {
|
||||||
|
data = data.sort((a: any, b: any) => b[prop] - a[prop]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -18,7 +18,7 @@
|
|||||||
|
|
||||||
import { describe, expect, test } from 'vitest';
|
import { describe, expect, test } from 'vitest';
|
||||||
import type { FormState } from '@form/index';
|
import type { FormState } from '@form/index';
|
||||||
import { datetimeFormatter, display, filterFunction, getRules, initValue } from '@form/utils/form';
|
import { datetimeFormatter, display, filterFunction, getRules, initValue, sortArray } from '@form/utils/form';
|
||||||
|
|
||||||
// form state mock 数据
|
// form state mock 数据
|
||||||
const mForm: FormState = {
|
const mForm: FormState = {
|
||||||
@ -32,6 +32,19 @@ const mForm: FormState = {
|
|||||||
setField: (prop: string, field: any) => field,
|
setField: (prop: string, field: any) => field,
|
||||||
getField: (prop: string) => prop,
|
getField: (prop: string) => prop,
|
||||||
deleteField: (prop: string) => prop,
|
deleteField: (prop: string) => prop,
|
||||||
|
$messageBox: {
|
||||||
|
alert: () => Promise.resolve(),
|
||||||
|
confirm: () => Promise.resolve(),
|
||||||
|
prompt: () => Promise.resolve(),
|
||||||
|
close: () => undefined,
|
||||||
|
},
|
||||||
|
$message: {
|
||||||
|
success: () => undefined,
|
||||||
|
warning: () => undefined,
|
||||||
|
info: () => undefined,
|
||||||
|
error: () => undefined,
|
||||||
|
closeAll: () => undefined,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('filterFunction', () => {
|
describe('filterFunction', () => {
|
||||||
@ -339,3 +352,71 @@ describe('datetimeFormatter', () => {
|
|||||||
expect(datetimeFormatter(date.toISOString(), defaultValue, 'timestamp')).toBe(date.getTime());
|
expect(datetimeFormatter(date.toISOString(), defaultValue, 'timestamp')).toBe(date.getTime());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('sortArray', () => {
|
||||||
|
test('索引相同时不执行任何操作', () => {
|
||||||
|
const data = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
expect(sortArray(data, 2, 2)).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('正常交换两个元素的位置', () => {
|
||||||
|
const data = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
expect(sortArray(data, 0, 3)).toEqual([4, 1, 2, 3, 5]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('从后往前移动元素', () => {
|
||||||
|
const data = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
expect(sortArray(data, 3, 1)).toEqual([1, 3, 4, 2, 5]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('使用sortKey参数重新排序', () => {
|
||||||
|
const data = [
|
||||||
|
{ id: 1, order: 0 },
|
||||||
|
{ id: 2, order: 1 },
|
||||||
|
{ id: 3, order: 2 },
|
||||||
|
{ id: 4, order: 3 },
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(sortArray(data, 0, 2, 'order')).toEqual([
|
||||||
|
{ id: 3, order: 3 },
|
||||||
|
{ id: 1, order: 2 },
|
||||||
|
{ id: 2, order: 1 },
|
||||||
|
{ id: 4, order: 0 },
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('移动第一个元素到最后', () => {
|
||||||
|
const data = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
expect(sortArray(data, 4, 0)).toEqual([2, 3, 4, 5, 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('移动最后一个元素到第一个', () => {
|
||||||
|
const data = [1, 2, 3, 4, 5];
|
||||||
|
|
||||||
|
expect(sortArray(data, 0, 4)).toEqual([5, 1, 2, 3, 4]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('空数组不执行任何操作', () => {
|
||||||
|
const data: any[] = [];
|
||||||
|
|
||||||
|
expect(sortArray(data, 0, 1)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('只有一个元素的数组不执行任何操作', () => {
|
||||||
|
const data = [1];
|
||||||
|
|
||||||
|
expect(sortArray(data, 0, 0)).toEqual([1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('索引超出范围时正常处理', () => {
|
||||||
|
const data = [1, 2, 3];
|
||||||
|
|
||||||
|
// 索引超出范围应该由调用方处理,这里测试函数的行为
|
||||||
|
expect(sortArray(data, 5, 1)).toEqual(data);
|
||||||
|
expect(sortArray(data, 1, 5)).toEqual(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@ -1,49 +1,46 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicTableColumn :label="config.label" :width="config.width" :fixed="config.fixed">
|
<TMagicTooltip
|
||||||
<template v-slot="scope">
|
v-for="(action, actionIndex) in config.actions"
|
||||||
<TMagicTooltip
|
:placement="action.tooltipPlacement || 'top'"
|
||||||
v-for="(action, actionIndex) in config.actions"
|
:key="actionIndex"
|
||||||
:placement="action.tooltipPlacement || 'top'"
|
:disabled="!Boolean(action.tooltip)"
|
||||||
:key="actionIndex"
|
:content="action.tooltip"
|
||||||
:disabled="!Boolean(action.tooltip)"
|
>
|
||||||
:content="action.tooltip"
|
<TMagicButton
|
||||||
>
|
v-show="display(action.display, row) && !editState[index]"
|
||||||
<TMagicButton
|
class="action-btn"
|
||||||
v-show="display(action.display, scope.row) && !editState[scope.$index]"
|
link
|
||||||
class="action-btn"
|
size="small"
|
||||||
link
|
:type="action.buttonType || 'primary'"
|
||||||
size="small"
|
:icon="action.icon"
|
||||||
:type="action.buttonType || 'primary'"
|
:disabled="disabled(action.disabled, row)"
|
||||||
:icon="action.icon"
|
@click="actionHandler(action, row, index)"
|
||||||
:disabled="disabled(action.disabled, scope.row)"
|
><span v-html="formatter(action.text, row)"></span
|
||||||
@click="actionHandler(action, scope.row, scope.$index)"
|
></TMagicButton>
|
||||||
><span v-html="formatter(action.text, scope.row)"></span
|
</TMagicTooltip>
|
||||||
></TMagicButton>
|
|
||||||
</TMagicTooltip>
|
<TMagicButton
|
||||||
<TMagicButton
|
class="action-btn"
|
||||||
class="action-btn"
|
v-show="editState[index]"
|
||||||
v-show="editState[scope.$index]"
|
link
|
||||||
link
|
type="primary"
|
||||||
type="primary"
|
size="small"
|
||||||
size="small"
|
@click="save(index, config)"
|
||||||
@click="save(scope.$index, config)"
|
>保存</TMagicButton
|
||||||
>保存</TMagicButton
|
>
|
||||||
>
|
<TMagicButton
|
||||||
<TMagicButton
|
class="action-btn"
|
||||||
class="action-btn"
|
v-show="editState[index]"
|
||||||
v-show="editState[scope.$index]"
|
link
|
||||||
link
|
type="primary"
|
||||||
type="primary"
|
size="small"
|
||||||
size="small"
|
@click="editState[index] = undefined"
|
||||||
@click="editState[scope.$index] = undefined"
|
>取消</TMagicButton
|
||||||
>取消</TMagicButton
|
>
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TMagicButton, tMagicMessage, TMagicTableColumn, TMagicTooltip } from '@tmagic/design';
|
import { TMagicButton, tMagicMessage, TMagicTooltip } from '@tmagic/design';
|
||||||
|
|
||||||
import { ColumnActionConfig, ColumnConfig } from './schema';
|
import { ColumnActionConfig, ColumnConfig } from './schema';
|
||||||
|
|
||||||
@ -53,10 +50,12 @@ defineOptions({
|
|||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
columns: any[];
|
columns: ColumnConfig[];
|
||||||
config: ColumnConfig;
|
config: ColumnConfig;
|
||||||
rowkeyName?: string;
|
rowkeyName?: string;
|
||||||
editState?: any;
|
editState?: any;
|
||||||
|
row: any;
|
||||||
|
index: number;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
columns: () => [],
|
columns: () => [],
|
||||||
@ -114,7 +113,9 @@ const save = async (index: number, config: ColumnConfig) => {
|
|||||||
props.columns
|
props.columns
|
||||||
.filter((item) => item.type)
|
.filter((item) => item.type)
|
||||||
.forEach((column) => {
|
.forEach((column) => {
|
||||||
data[column.prop] = row[column.prop];
|
if (column.prop) {
|
||||||
|
data[column.prop] = row[column.prop];
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const res: any = await action({
|
const res: any = await action({
|
||||||
|
|||||||
@ -1,25 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicTableColumn
|
<component
|
||||||
show-overflow-tooltip
|
:is="config.component"
|
||||||
:label="config.label"
|
v-bind="componentProps(row, index)"
|
||||||
:width="config.width"
|
v-on="componentListeners(row, index)"
|
||||||
:fixed="config.fixed"
|
></component>
|
||||||
:sortable="config.sortable"
|
|
||||||
:prop="config.prop"
|
|
||||||
>
|
|
||||||
<template v-slot="scope">
|
|
||||||
<component
|
|
||||||
:is="config.component"
|
|
||||||
v-bind="componentProps(scope.row, scope.$index)"
|
|
||||||
v-on="componentListeners(scope.row, scope.$index)"
|
|
||||||
></component>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TMagicTableColumn } from '@tmagic/design';
|
|
||||||
|
|
||||||
import { ColumnConfig } from './schema';
|
import { ColumnConfig } from './schema';
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
@ -29,6 +16,8 @@ defineOptions({
|
|||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
config: ColumnConfig;
|
config: ColumnConfig;
|
||||||
|
row: any;
|
||||||
|
index: number;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
config: () => ({}),
|
config: () => ({}),
|
||||||
|
|||||||
@ -1,26 +1,20 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- @ts-nocheck -->
|
<MTable
|
||||||
<TMagicTableColumn type="expand" :width="config.width" :fixed="config.fixed">
|
v-if="config.table"
|
||||||
<template #default="scope">
|
:show-header="false"
|
||||||
<MTable
|
:columns="config.table"
|
||||||
v-if="config.table"
|
:data="(config.prop && row[config.prop]) || []"
|
||||||
:show-header="false"
|
></MTable>
|
||||||
:columns="config.table"
|
<MForm
|
||||||
:data="(config.prop && scope.row[config.prop]) || []"
|
v-if="config.form"
|
||||||
></MTable>
|
:config="config.form"
|
||||||
<MForm
|
:init-values="config.values || (config.prop && row[config.prop]) || {}"
|
||||||
v-if="config.form"
|
></MForm>
|
||||||
:config="config.form"
|
<div v-if="config.expandContent" v-html="config.expandContent(row, config.prop)"></div>
|
||||||
:init-values="config.values || (config.prop && scope.row[config.prop]) || {}"
|
<component v-if="config.component" :is="config.component" v-bind="componentProps(row)"></component>
|
||||||
></MForm>
|
|
||||||
<div v-if="config.expandContent" v-html="config.expandContent(scope.row, config.prop)"></div>
|
|
||||||
<component v-if="config.component" :is="config.component" v-bind="componentProps(scope.row)"></component>
|
|
||||||
</template>
|
|
||||||
</TMagicTableColumn>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TMagicTableColumn } from '@tmagic/design';
|
|
||||||
import { MForm } from '@tmagic/form';
|
import { MForm } from '@tmagic/form';
|
||||||
|
|
||||||
import { ColumnConfig } from './schema';
|
import { ColumnConfig } from './schema';
|
||||||
@ -33,6 +27,7 @@ defineOptions({
|
|||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
config: ColumnConfig;
|
config: ColumnConfig;
|
||||||
|
row: any;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
config: () => ({}),
|
config: () => ({}),
|
||||||
|
|||||||
@ -1,32 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- @ts-nocheck -->
|
<TMagicPopover
|
||||||
<TMagicTableColumn :label="config.label" :width="config.width" :fixed="config.fixed">
|
v-if="config.popover"
|
||||||
<template v-slot="scope">
|
:placement="config.popover.placement"
|
||||||
<TMagicPopover
|
:width="config.popover.width"
|
||||||
v-if="config.popover"
|
:trigger="config.popover.trigger"
|
||||||
:placement="config.popover.placement"
|
:destroy-on-close="config.popover.destroyOnClose ?? true"
|
||||||
:width="config.popover.width"
|
>
|
||||||
:trigger="config.popover.trigger"
|
<MTable
|
||||||
:destroy-on-close="config.popover.destroyOnClose ?? true"
|
v-if="config.popover.tableEmbed"
|
||||||
>
|
:show-header="config.showHeader"
|
||||||
<MTable
|
:columns="config.table"
|
||||||
v-if="config.popover.tableEmbed"
|
:data="(config.prop && row[config.prop]) || []"
|
||||||
:show-header="config.showHeader"
|
></MTable>
|
||||||
:columns="config.table"
|
<template #reference>
|
||||||
:data="(config.prop && scope.row[config.prop]) || []"
|
<TMagicButton link type="primary">{{ config.text || formatter(config, row, { index: index }) }}</TMagicButton>
|
||||||
></MTable>
|
|
||||||
<template #reference>
|
|
||||||
<TMagicButton link type="primary">
|
|
||||||
{{ config.text || formatter(config, scope.row, { index: scope.$index }) }}</TMagicButton
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
</TMagicPopover>
|
|
||||||
</template>
|
</template>
|
||||||
</TMagicTableColumn>
|
</TMagicPopover>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TMagicButton, TMagicPopover, TMagicTableColumn } from '@tmagic/design';
|
import { TMagicButton, TMagicPopover } from '@tmagic/design';
|
||||||
|
|
||||||
import { ColumnConfig } from './schema';
|
import { ColumnConfig } from './schema';
|
||||||
import MTable from './Table.vue';
|
import MTable from './Table.vue';
|
||||||
@ -39,6 +32,8 @@ defineOptions({
|
|||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
config: ColumnConfig;
|
config: ColumnConfig;
|
||||||
|
row: any;
|
||||||
|
index: number;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
config: () => ({}),
|
config: () => ({}),
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicTable
|
<TMagicTable
|
||||||
tooltip-effect="dark"
|
v-loading="loading"
|
||||||
:tooltip-options="{ popperOptions: { strategy: 'absolute' } }"
|
|
||||||
class="m-table"
|
class="m-table"
|
||||||
ref="tMagicTable"
|
ref="tMagicTable"
|
||||||
v-loading="loading"
|
:show-overflow-tooltip="true"
|
||||||
|
tooltip-effect="dark"
|
||||||
|
:tooltip-options="{ popperOptions: { strategy: 'absolute' } }"
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
:show-header="showHeader"
|
:show-header="showHeader"
|
||||||
:max-height="bodyHeight"
|
:max-height="bodyHeight"
|
||||||
@ -14,59 +15,21 @@
|
|||||||
:tree-props="{ children: 'children' }"
|
:tree-props="{ children: 'children' }"
|
||||||
:empty-text="emptyText || '暂无数据'"
|
:empty-text="emptyText || '暂无数据'"
|
||||||
:span-method="objectSpanMethod"
|
:span-method="objectSpanMethod"
|
||||||
|
:columns="tableColumns"
|
||||||
@sort-change="sortChange"
|
@sort-change="sortChange"
|
||||||
@select="selectHandler"
|
@select="selectHandler"
|
||||||
@select-all="selectAllHandler"
|
@select-all="selectAllHandler"
|
||||||
@selection-change="selectionChangeHandler"
|
@selection-change="selectionChangeHandler"
|
||||||
@cell-click="cellClickHandler"
|
@cell-click="cellClickHandler"
|
||||||
@expand-change="expandChange"
|
@expand-change="expandChange"
|
||||||
>
|
></TMagicTable>
|
||||||
<template v-for="(item, columnIndex) in columns">
|
|
||||||
<template v-if="item.type === 'expand'">
|
|
||||||
<ExpandColumn :config="item" :key="columnIndex"></ExpandColumn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else-if="item.type === 'component'">
|
|
||||||
<ComponentColumn :config="item" :key="columnIndex"></ComponentColumn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else-if="item.selection">
|
|
||||||
<component
|
|
||||||
width="40"
|
|
||||||
type="selection"
|
|
||||||
:is="tableColumnComponent?.component || 'el-table-column'"
|
|
||||||
:key="columnIndex"
|
|
||||||
:selectable="item.selectable"
|
|
||||||
></component>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else-if="item.actions">
|
|
||||||
<ActionsColumn
|
|
||||||
:columns="columns"
|
|
||||||
:config="item"
|
|
||||||
:rowkey-name="rowkeyName"
|
|
||||||
:edit-state="editState"
|
|
||||||
:key="columnIndex"
|
|
||||||
@after-action="$emit('after-action')"
|
|
||||||
></ActionsColumn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else-if="item.type === 'popover'">
|
|
||||||
<PopoverColumn :key="columnIndex" :config="item"></PopoverColumn>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<TextColumn :key="columnIndex" :config="item" :edit-state="editState"></TextColumn>
|
|
||||||
</template>
|
|
||||||
</template>
|
|
||||||
</TMagicTable>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed, ref } from 'vue';
|
import { computed, h, ref, useTemplateRef } from 'vue';
|
||||||
import { cloneDeep } from 'lodash-es';
|
import { cloneDeep } from 'lodash-es';
|
||||||
|
|
||||||
import { getDesignConfig, TMagicTable } from '@tmagic/design';
|
import { TMagicTable } from '@tmagic/design';
|
||||||
|
|
||||||
import ActionsColumn from './ActionsColumn.vue';
|
import ActionsColumn from './ActionsColumn.vue';
|
||||||
import ComponentColumn from './ComponentColumn.vue';
|
import ComponentColumn from './ComponentColumn.vue';
|
||||||
@ -117,11 +80,72 @@ const emit = defineEmits([
|
|||||||
'cell-click',
|
'cell-click',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const tMagicTable = ref<InstanceType<typeof TMagicTable>>();
|
const cellRender = (config: ColumnConfig, { row = {}, $index }: any) => {
|
||||||
|
if (config.type === 'expand') {
|
||||||
|
return h(ExpandColumn, {
|
||||||
|
config,
|
||||||
|
row,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (config.type === 'component') {
|
||||||
|
return h(ComponentColumn, {
|
||||||
|
config,
|
||||||
|
row,
|
||||||
|
index: $index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (config.actions) {
|
||||||
|
return h(ActionsColumn, {
|
||||||
|
config,
|
||||||
|
row,
|
||||||
|
index: $index,
|
||||||
|
rowkeyName: props.rowkeyName,
|
||||||
|
editState: editState.value,
|
||||||
|
columns: props.columns,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (config.type === 'popover') {
|
||||||
|
return h(PopoverColumn, {
|
||||||
|
config,
|
||||||
|
row,
|
||||||
|
index: $index,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return h(TextColumn, {
|
||||||
|
config,
|
||||||
|
row,
|
||||||
|
index: $index,
|
||||||
|
editState: editState.value,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const tableColumns = computed(() =>
|
||||||
|
props.columns.map((item) => {
|
||||||
|
let type: 'default' | 'selection' | 'index' | 'expand' = 'default';
|
||||||
|
if (item.type === 'expand') {
|
||||||
|
type = 'expand';
|
||||||
|
} else if (item.selection) {
|
||||||
|
type = 'selection';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
props: {
|
||||||
|
label: item.label,
|
||||||
|
fixed: item.fixed,
|
||||||
|
width: item.width ?? (item.selection ? 40 : undefined),
|
||||||
|
prop: item.prop,
|
||||||
|
type,
|
||||||
|
selectable: item.selectable,
|
||||||
|
},
|
||||||
|
cell: type === 'selection' ? undefined : ({ row, $index }: any) => cellRender(item, { row, $index }),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const tMagicTableRef = useTemplateRef('tMagicTable');
|
||||||
|
|
||||||
const editState = ref([]);
|
const editState = ref([]);
|
||||||
|
|
||||||
const tableColumnComponent = getDesignConfig('components')?.tableColumn;
|
|
||||||
const selectionColumn = computed(() => {
|
const selectionColumn = computed(() => {
|
||||||
const column = props.columns.filter((item) => item.selection);
|
const column = props.columns.filter((item) => item.selection);
|
||||||
return column.length ? column[0] : null;
|
return column.length ? column[0] : null;
|
||||||
@ -171,15 +195,15 @@ const expandChange = (...args: any[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const toggleRowSelection = (row: any, selected: boolean) => {
|
const toggleRowSelection = (row: any, selected: boolean) => {
|
||||||
tMagicTable.value?.toggleRowSelection(row, selected);
|
tMagicTableRef.value?.toggleRowSelection(row, selected);
|
||||||
};
|
};
|
||||||
|
|
||||||
const toggleRowExpansion = (row: any, expanded: boolean) => {
|
const toggleRowExpansion = (row: any, expanded: boolean) => {
|
||||||
tMagicTable.value?.toggleRowExpansion(row, expanded);
|
tMagicTableRef.value?.toggleRowExpansion(row, expanded);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clearSelection = () => {
|
const clearSelection = () => {
|
||||||
tMagicTable.value?.clearSelection();
|
tMagicTableRef.value?.clearSelection();
|
||||||
};
|
};
|
||||||
|
|
||||||
const objectSpanMethod = (data: any) => {
|
const objectSpanMethod = (data: any) => {
|
||||||
|
|||||||
@ -1,69 +1,52 @@
|
|||||||
<template>
|
<template>
|
||||||
<TMagicTableColumn
|
<div v-if="config.type === 'index'">
|
||||||
show-overflow-tooltip
|
{{ config.pageIndex && config.pageSize ? config.pageIndex * config.pageSize + index + 1 : index + 1 }}
|
||||||
:label="config.label"
|
</div>
|
||||||
:width="config.width"
|
<TMagicForm v-else-if="config.type && editState[index]" label-width="0" :model="editState[index]">
|
||||||
:fixed="config.fixed"
|
<m-form-container
|
||||||
:sortable="config.sortable"
|
:prop="config.prop"
|
||||||
:prop="config.prop"
|
:rules="config.rules"
|
||||||
|
:config="config"
|
||||||
|
:name="config.prop"
|
||||||
|
:model="editState[index]"
|
||||||
|
></m-form-container>
|
||||||
|
</TMagicForm>
|
||||||
|
|
||||||
|
<TMagicButton
|
||||||
|
v-else-if="config.action === 'actionLink' && config.prop"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="config.handler?.(row)"
|
||||||
>
|
>
|
||||||
<template v-slot="scope">
|
<span v-html="formatter(config, row, { index: index })"></span>
|
||||||
<div v-if="config.type === 'index'">
|
</TMagicButton>
|
||||||
{{
|
|
||||||
config.pageIndex && config.pageSize ? config.pageIndex * config.pageSize + scope.$index + 1 : scope.$index + 1
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
<TMagicForm v-else-if="config.type && editState[scope.$index]" label-width="0" :model="editState[scope.$index]">
|
|
||||||
<m-form-container
|
|
||||||
:prop="config.prop"
|
|
||||||
:rules="config.rules"
|
|
||||||
:config="config"
|
|
||||||
:name="config.prop"
|
|
||||||
:model="editState[scope.$index]"
|
|
||||||
></m-form-container>
|
|
||||||
</TMagicForm>
|
|
||||||
|
|
||||||
<TMagicButton
|
<a v-else-if="config.action === 'img' && config.prop" target="_blank" :href="row[config.prop]"
|
||||||
v-else-if="config.action === 'actionLink' && config.prop"
|
><img :src="row[config.prop]" height="50"
|
||||||
link
|
/></a>
|
||||||
type="primary"
|
|
||||||
@click="config.handler?.(scope.row)"
|
|
||||||
>
|
|
||||||
<span v-html="formatter(config, scope.row, { index: scope.$index })"></span>
|
|
||||||
</TMagicButton>
|
|
||||||
|
|
||||||
<a v-else-if="config.action === 'img' && config.prop" target="_blank" :href="scope.row[config.prop]"
|
<a v-else-if="config.action === 'link' && config.prop" target="_blank" :href="row[config.prop]" class="keep-all">{{
|
||||||
><img :src="scope.row[config.prop]" height="50"
|
row[config.prop]
|
||||||
/></a>
|
}}</a>
|
||||||
|
|
||||||
<a
|
<el-tooltip v-else-if="config.action === 'tip'" placement="left">
|
||||||
v-else-if="config.action === 'link' && config.prop"
|
<template #content>
|
||||||
target="_blank"
|
<div>{{ formatter(config, row, { index: index }) }}</div>
|
||||||
:href="scope.row[config.prop]"
|
|
||||||
class="keep-all"
|
|
||||||
>{{ scope.row[config.prop] }}</a
|
|
||||||
>
|
|
||||||
|
|
||||||
<el-tooltip v-else-if="config.action === 'tip'" placement="left">
|
|
||||||
<template #content>
|
|
||||||
<div>{{ formatter(config, scope.row, { index: scope.$index }) }}</div>
|
|
||||||
</template>
|
|
||||||
<TMagicButton link type="primary">{{ config.buttonText || '扩展配置' }}</TMagicButton>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<TMagicTag
|
|
||||||
v-else-if="config.action === 'tag' && config.prop"
|
|
||||||
:type="typeof config.type === 'function' ? config.type(scope.row[config.prop], scope.row) : config.type"
|
|
||||||
close-transition
|
|
||||||
>{{ formatter(config, scope.row, { index: scope.$index }) }}</TMagicTag
|
|
||||||
>
|
|
||||||
<div v-else v-html="formatter(config, scope.row, { index: scope.$index })"></div>
|
|
||||||
</template>
|
</template>
|
||||||
</TMagicTableColumn>
|
<TMagicButton link type="primary">{{ config.buttonText || '扩展配置' }}</TMagicButton>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<TMagicTag
|
||||||
|
v-else-if="config.action === 'tag' && config.prop"
|
||||||
|
:type="typeof config.type === 'function' ? config.type(row[config.prop], row) : config.type"
|
||||||
|
close-transition
|
||||||
|
>{{ formatter(config, row, { index: index }) }}</TMagicTag
|
||||||
|
>
|
||||||
|
<div v-else v-html="formatter(config, row, { index: index })"></div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { TMagicButton, TMagicForm, TMagicTableColumn, TMagicTag } from '@tmagic/design';
|
import { TMagicButton, TMagicForm, TMagicTag } from '@tmagic/design';
|
||||||
|
|
||||||
import { ColumnConfig } from './schema';
|
import { ColumnConfig } from './schema';
|
||||||
import { formatter } from './utils';
|
import { formatter } from './utils';
|
||||||
@ -76,6 +59,8 @@ withDefaults(
|
|||||||
defineProps<{
|
defineProps<{
|
||||||
config: ColumnConfig;
|
config: ColumnConfig;
|
||||||
editState?: any;
|
editState?: any;
|
||||||
|
row: any;
|
||||||
|
index: number;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
config: () => ({}),
|
config: () => ({}),
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
],
|
],
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@tmagic/design": "workspace:*",
|
"@tmagic/design": "workspace:*",
|
||||||
"tdesign-vue-next": "^1.9.8",
|
"tdesign-vue-next": "^1.17.1",
|
||||||
"vue": "catalog:",
|
"vue": "catalog:",
|
||||||
"typescript": "catalog:"
|
"typescript": "catalog:"
|
||||||
},
|
},
|
||||||
|
|||||||
47
packages/tdesign-vue-next-adapter/src/Checkbox.vue
Normal file
47
packages/tdesign-vue-next-adapter/src/Checkbox.vue
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
<template>
|
||||||
|
<TCheckbox v-model="checked" :disabled="disabled" :value="value" @change="changeHandler">
|
||||||
|
<template #default v-if="$slots.default"> <slot></slot> </template>
|
||||||
|
</TCheckbox>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { Checkbox as TCheckbox } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
import type { CheckboxProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TTDesignAdapterCheckbox',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<CheckboxProps>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['change', 'update:modelValue']);
|
||||||
|
|
||||||
|
const checked = ref(false);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(v) => {
|
||||||
|
if (typeof props.trueValue !== 'undefined') {
|
||||||
|
checked.value = v === props.trueValue;
|
||||||
|
} else if (typeof props.falseValue !== 'undefined') {
|
||||||
|
checked.value = v !== props.falseValue;
|
||||||
|
} else {
|
||||||
|
checked.value = Boolean(v);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const changeHandler = (v: boolean) => {
|
||||||
|
updateModelValue(v);
|
||||||
|
emit('change', v ? (props.trueValue ?? true) : (props.falseValue ?? false));
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateModelValue = (v: boolean) => {
|
||||||
|
emit('update:modelValue', v ? (props.trueValue ?? true) : (props.falseValue ?? false));
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -8,7 +8,7 @@
|
|||||||
:size="size === 'default' ? 'medium' : size"
|
:size="size === 'default' ? 'medium' : size"
|
||||||
:separator="rangeSeparator"
|
:separator="rangeSeparator"
|
||||||
:format="format"
|
:format="format"
|
||||||
:valueType="valueFormat === 's' ? 'time-stamp' : valueFormat"
|
:valueType="valueType"
|
||||||
@change="changeHandler"
|
@change="changeHandler"
|
||||||
@update:modelValue="updateModelValue"
|
@update:modelValue="updateModelValue"
|
||||||
/>
|
/>
|
||||||
@ -21,7 +21,7 @@
|
|||||||
:size="size === 'default' ? 'medium' : size"
|
:size="size === 'default' ? 'medium' : size"
|
||||||
:format="format"
|
:format="format"
|
||||||
:enableTimePicker="type.includes('time')"
|
:enableTimePicker="type.includes('time')"
|
||||||
:valueType="valueFormat === 's' ? 'time-stamp' : valueFormat"
|
:valueType="valueType"
|
||||||
@change="changeHandler"
|
@change="changeHandler"
|
||||||
@update:modelValue="updateModelValue"
|
@update:modelValue="updateModelValue"
|
||||||
/>
|
/>
|
||||||
@ -54,6 +54,8 @@ const mode = computed(() => {
|
|||||||
return map[props.type] || props.type;
|
return map[props.type] || props.type;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const valueType = computed(() => (props.valueFormat === 's' ? 'time-stamp' : props.valueFormat.replace(/\//g, '-')));
|
||||||
|
|
||||||
const emit = defineEmits(['change', 'update:modelValue']);
|
const emit = defineEmits(['change', 'update:modelValue']);
|
||||||
|
|
||||||
const changeHandler = (v: any) => {
|
const changeHandler = (v: any) => {
|
||||||
|
|||||||
43
packages/tdesign-vue-next-adapter/src/Dialog.vue
Normal file
43
packages/tdesign-vue-next-adapter/src/Dialog.vue
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<TDialog
|
||||||
|
:visible="modelValue"
|
||||||
|
:attach="appendToBody ? 'body' : undefined"
|
||||||
|
:header="title"
|
||||||
|
:width="width"
|
||||||
|
:mode="fullscreen ? 'full-screen' : 'modal'"
|
||||||
|
:close-on-overlay-click="closeOnClickModal"
|
||||||
|
:close-on-esc-keydown="closeOnPressEscape"
|
||||||
|
:destroy-on-close="destroyOnClose"
|
||||||
|
@before-open="beforeClose"
|
||||||
|
@close="closeHandler"
|
||||||
|
@update:visible="updateModelValue"
|
||||||
|
>
|
||||||
|
<slot></slot>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<slot name="footer"></slot>
|
||||||
|
</template>
|
||||||
|
</TDialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { Dialog as TDialog } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
import type { DialogProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TTDesignAdapterDialog',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<DialogProps>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['close', 'update:modelValue']);
|
||||||
|
|
||||||
|
const closeHandler = (...args: any[]) => {
|
||||||
|
emit('close', ...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateModelValue = (v: any) => {
|
||||||
|
emit('update:modelValue', v);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@ -1,7 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<i>
|
<i class="t-t-design-adapter-icon">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</i>
|
</i>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
defineOptions({
|
||||||
|
name: 'TTDesignAdapterIcon',
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|||||||
23
packages/tdesign-vue-next-adapter/src/Radio.vue
Normal file
23
packages/tdesign-vue-next-adapter/src/Radio.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<TRadio :value="value" @click="clickHandler">
|
||||||
|
<template #default v-if="$slots.default"> <slot></slot> </template>
|
||||||
|
</TRadio>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { Radio as TRadio } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
import type { RadioProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TTDesignAdapterRadio',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<RadioProps>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['click']);
|
||||||
|
|
||||||
|
const clickHandler = () => {
|
||||||
|
emit('click');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
23
packages/tdesign-vue-next-adapter/src/RadioButton.vue
Normal file
23
packages/tdesign-vue-next-adapter/src/RadioButton.vue
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<template>
|
||||||
|
<TRadioButton :value="value" :disabled="disabled" @click="clickHandler">
|
||||||
|
<template #default v-if="$slots.default"> <slot></slot> </template>
|
||||||
|
</TRadioButton>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { RadioButton as TRadioButton } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
import type { RadioButtonProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TTDesignAdapterRadioButton',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<RadioButtonProps>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['click']);
|
||||||
|
|
||||||
|
const clickHandler = () => {
|
||||||
|
emit('click');
|
||||||
|
};
|
||||||
|
</script>
|
||||||
124
packages/tdesign-vue-next-adapter/src/Table.vue
Normal file
124
packages/tdesign-vue-next-adapter/src/Table.vue
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
<template>
|
||||||
|
<TTable
|
||||||
|
ref="table"
|
||||||
|
:data="data"
|
||||||
|
:bordered="border"
|
||||||
|
:max-height="maxHeight"
|
||||||
|
:default-expand-all="defaultExpandAll"
|
||||||
|
:show-header="showHeader"
|
||||||
|
:row-key="rowKey"
|
||||||
|
:tree="treeProps"
|
||||||
|
:empty="emptyText"
|
||||||
|
:columns="tableColumns"
|
||||||
|
@sort-change="sortChange"
|
||||||
|
@select-change="selectHandler"
|
||||||
|
@cell-click="cellClickHandler"
|
||||||
|
@expand-change="expandChange"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, useTemplateRef } from 'vue';
|
||||||
|
import { Table as TTable } from 'tdesign-vue-next';
|
||||||
|
|
||||||
|
import type { TableProps } from '@tmagic/design';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'TTDesignAdapterTable',
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['sort-change', 'select', 'select-all', 'selection-change', 'expand-change', 'cell-click']);
|
||||||
|
|
||||||
|
const props = defineProps<TableProps>();
|
||||||
|
|
||||||
|
const tableRef = useTemplateRef('table');
|
||||||
|
|
||||||
|
// 将列配置转换为 TDesign 格式
|
||||||
|
const tableColumns = computed(() => {
|
||||||
|
if (!props.columns) return [];
|
||||||
|
|
||||||
|
const columns = [];
|
||||||
|
for (const item of props.columns) {
|
||||||
|
if (item.props.type === 'expand') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let colKey = item.props?.prop || item.props?.type;
|
||||||
|
|
||||||
|
if (!colKey) {
|
||||||
|
colKey = 'tmagic_table_operation';
|
||||||
|
}
|
||||||
|
|
||||||
|
const column: any = {
|
||||||
|
thClassName: item.props?.class,
|
||||||
|
colKey,
|
||||||
|
title: item.props?.label,
|
||||||
|
width: item.props?.width,
|
||||||
|
fixed: item.props?.fixed === true ? 'left' : item.props?.fixed || undefined,
|
||||||
|
ellipsis: props.showOverflowTooltip,
|
||||||
|
sorter: item.props?.sortable,
|
||||||
|
align: item.props?.align,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理自定义单元格渲染
|
||||||
|
if (item.cell) {
|
||||||
|
column.cell = (h: any, { row, rowIndex }: any) => {
|
||||||
|
return item.cell?.({ row, $index: rowIndex });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.push(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
return columns;
|
||||||
|
});
|
||||||
|
|
||||||
|
const sortChange = (data: any) => {
|
||||||
|
emit('sort-change', data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectHandler = (selectedRowKeys: any[], options: any) => {
|
||||||
|
const { selectedRowData, type } = options;
|
||||||
|
|
||||||
|
if (type === 'check') {
|
||||||
|
emit('select', selectedRowData);
|
||||||
|
} else if (type === 'uncheck') {
|
||||||
|
emit('select', selectedRowData);
|
||||||
|
}
|
||||||
|
|
||||||
|
emit('selection-change', selectedRowData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const cellClickHandler = (context: any) => {
|
||||||
|
const { row, col, e } = context;
|
||||||
|
emit('cell-click', row, col, undefined, e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const expandChange = (expandedRowKeys: any[], options: any) => {
|
||||||
|
emit('expand-change', options.expandedRowData, options.currentRowData);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleRowSelection = (_row: any, _selected: boolean) => {
|
||||||
|
// TDesign 的选择方法需要通过 selectedRowKeys 来控制
|
||||||
|
// 这里需要根据实际情况调整
|
||||||
|
console.warn('toggleRowSelection needs to be implemented based on TDesign API');
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleRowExpansion = (_row: any, _expanded: boolean) => {
|
||||||
|
// TDesign 的展开方法需要通过 expandedRowKeys 来控制
|
||||||
|
console.warn('toggleRowExpansion needs to be implemented based on TDesign API');
|
||||||
|
};
|
||||||
|
|
||||||
|
const clearSelection = () => {
|
||||||
|
// TDesign 需要通过更新 selectedRowKeys 来清空选择
|
||||||
|
console.warn('clearSelection needs to be implemented based on TDesign API');
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
getEl: () => tableRef.value?.$el,
|
||||||
|
getTableRef: () => tableRef.value,
|
||||||
|
clearSelection,
|
||||||
|
toggleRowSelection,
|
||||||
|
toggleRowExpansion,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@ -1,5 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div></div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
|
||||||
@ -4,13 +4,11 @@ import {
|
|||||||
Button as TButton,
|
Button as TButton,
|
||||||
Card as TCard,
|
Card as TCard,
|
||||||
Cascader as TCascader,
|
Cascader as TCascader,
|
||||||
Checkbox as TCheckbox,
|
|
||||||
CheckboxGroup as TCheckboxGroup,
|
CheckboxGroup as TCheckboxGroup,
|
||||||
Col as TCol,
|
Col as TCol,
|
||||||
Collapse as TCollapse,
|
Collapse as TCollapse,
|
||||||
CollapsePanel as TCollapsePanel,
|
CollapsePanel as TCollapsePanel,
|
||||||
ColorPicker as TColorPicker,
|
ColorPicker as TColorPicker,
|
||||||
Dialog as TDialog,
|
|
||||||
DialogPlugin,
|
DialogPlugin,
|
||||||
Divider as TDivider,
|
Divider as TDivider,
|
||||||
Drawer as TDrawer,
|
Drawer as TDrawer,
|
||||||
@ -23,15 +21,12 @@ import {
|
|||||||
Option as TOption,
|
Option as TOption,
|
||||||
OptionGroup as TOptionGroup,
|
OptionGroup as TOptionGroup,
|
||||||
Pagination as TPagination,
|
Pagination as TPagination,
|
||||||
Radio as TRadio,
|
|
||||||
RadioButton as TRadioButton,
|
|
||||||
RadioGroup as TRadioGroup,
|
RadioGroup as TRadioGroup,
|
||||||
Row as TRow,
|
Row as TRow,
|
||||||
Select as TSelect,
|
Select as TSelect,
|
||||||
StepItem as TStepItem,
|
StepItem as TStepItem,
|
||||||
Steps as TSteps,
|
Steps as TSteps,
|
||||||
Switch as TSwitch,
|
Switch as TSwitch,
|
||||||
Table as TTable,
|
|
||||||
TabPanel as TTabPanel,
|
TabPanel as TTabPanel,
|
||||||
Tabs as TTabs,
|
Tabs as TTabs,
|
||||||
Tag as TTag,
|
Tag as TTag,
|
||||||
@ -71,7 +66,6 @@ import type {
|
|||||||
StepProps,
|
StepProps,
|
||||||
StepsProps,
|
StepsProps,
|
||||||
SwitchProps,
|
SwitchProps,
|
||||||
TableColumnProps,
|
|
||||||
TableProps,
|
TableProps,
|
||||||
TabPaneProps,
|
TabPaneProps,
|
||||||
TabsProps,
|
TabsProps,
|
||||||
@ -81,11 +75,15 @@ import type {
|
|||||||
UploadProps,
|
UploadProps,
|
||||||
} from '@tmagic/design';
|
} from '@tmagic/design';
|
||||||
|
|
||||||
|
import Checkbox from './Checkbox.vue';
|
||||||
import DatePicker from './DatePicker.vue';
|
import DatePicker from './DatePicker.vue';
|
||||||
|
import Dialog from './Dialog.vue';
|
||||||
import Icon from './Icon.vue';
|
import Icon from './Icon.vue';
|
||||||
import Input from './Input.vue';
|
import Input from './Input.vue';
|
||||||
|
import Radio from './Radio.vue';
|
||||||
|
import RadioButton from './RadioButton.vue';
|
||||||
import Scrollbar from './Scrollbar.vue';
|
import Scrollbar from './Scrollbar.vue';
|
||||||
import TableColumn from './TableColumn.vue';
|
import Table from './Table.vue';
|
||||||
|
|
||||||
const adapter: any = {
|
const adapter: any = {
|
||||||
message: MessagePlugin,
|
message: MessagePlugin,
|
||||||
@ -119,7 +117,7 @@ const adapter: any = {
|
|||||||
props: (props: ButtonProps) => ({
|
props: (props: ButtonProps) => ({
|
||||||
theme: props.type,
|
theme: props.type,
|
||||||
size: props.size === 'default' ? 'medium' : props.size,
|
size: props.size === 'default' ? 'medium' : props.size,
|
||||||
icon: () => (props.icon ? h(props.icon) : null),
|
icon: () => (props.icon ? h(Icon, null, { default: () => h(props.icon) }) : null),
|
||||||
variant: props.link || props.text ? 'text' : 'base',
|
variant: props.link || props.text ? 'text' : 'base',
|
||||||
shape: props.circle ? 'circle' : 'rectangle',
|
shape: props.circle ? 'circle' : 'rectangle',
|
||||||
}),
|
}),
|
||||||
@ -153,13 +151,8 @@ const adapter: any = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
checkbox: {
|
checkbox: {
|
||||||
component: TCheckbox,
|
component: Checkbox,
|
||||||
props: (props: CheckboxProps) => ({
|
props: (props: CheckboxProps) => props,
|
||||||
modelValue: props.modelValue,
|
|
||||||
label: props.label,
|
|
||||||
value: props.value,
|
|
||||||
disabled: props.disabled,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
checkboxGroup: {
|
checkboxGroup: {
|
||||||
@ -174,14 +167,14 @@ const adapter: any = {
|
|||||||
col: {
|
col: {
|
||||||
component: TCol,
|
component: TCol,
|
||||||
props: (props: ColProps) => ({
|
props: (props: ColProps) => ({
|
||||||
span: props.span,
|
span: props.span ? props.span / 2 : 12,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
collapse: {
|
collapse: {
|
||||||
component: TCollapse,
|
component: TCollapse,
|
||||||
props: (props: CollapseProps) => ({
|
props: (props: CollapseProps) => ({
|
||||||
value: props.modelValue,
|
modelValue: props.modelValue,
|
||||||
expandIconPlacement: 'right',
|
expandIconPlacement: 'right',
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -212,15 +205,8 @@ const adapter: any = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
dialog: {
|
dialog: {
|
||||||
component: TDialog,
|
component: Dialog,
|
||||||
props: (props: DialogProps) => ({
|
props: (props: DialogProps) => props,
|
||||||
visible: props.modelValue,
|
|
||||||
attach: props.appendToBody ? 'body' : '',
|
|
||||||
header: props.title,
|
|
||||||
width: props.width,
|
|
||||||
mode: props.fullscreen ? 'full-screen' : 'modal',
|
|
||||||
closeOnOverlayClick: props.closeOnClickModal,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
divider: {
|
divider: {
|
||||||
@ -295,6 +281,7 @@ const adapter: any = {
|
|||||||
labelWidth: props.labelWidth,
|
labelWidth: props.labelWidth,
|
||||||
name: props.prop,
|
name: props.prop,
|
||||||
rules: props.rules,
|
rules: props.rules,
|
||||||
|
help: props.extra,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -347,18 +334,13 @@ const adapter: any = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
radio: {
|
radio: {
|
||||||
component: TRadio,
|
component: Radio,
|
||||||
props: (props: RadioProps) => ({
|
props: (props: RadioProps) => props,
|
||||||
label: props.label,
|
|
||||||
value: props.value,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
radioButton: {
|
radioButton: {
|
||||||
component: TRadioButton,
|
component: RadioButton,
|
||||||
props: (props: RadioButtonProps) => ({
|
props: (props: RadioButtonProps) => props,
|
||||||
label: props.label,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
radioGroup: {
|
radioGroup: {
|
||||||
@ -424,15 +406,10 @@ const adapter: any = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
table: {
|
table: {
|
||||||
component: TTable,
|
component: Table,
|
||||||
props: (props: TableProps) => props,
|
props: (props: TableProps) => props,
|
||||||
},
|
},
|
||||||
|
|
||||||
tableColumn: {
|
|
||||||
component: TableColumn,
|
|
||||||
props: (props: TableColumnProps) => props,
|
|
||||||
},
|
|
||||||
|
|
||||||
tabPane: {
|
tabPane: {
|
||||||
component: TTabPanel,
|
component: TTabPanel,
|
||||||
props: (props: TabPaneProps) => ({
|
props: (props: TabPaneProps) => ({
|
||||||
|
|||||||
@ -1,15 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="m-editor-nav-menu">
|
<div class="m-editor-nav-menu">
|
||||||
<TMagicButton
|
<div v-for="(item, index) in data" :key="index" class="menu-item button">
|
||||||
v-for="(item, index) in data"
|
<TMagicButton size="small" link @click="item.handler">
|
||||||
class="menu-item button"
|
<TMagicIcon><component :is="item.icon"></component></TMagicIcon><span>{{ item.text }}</span>
|
||||||
:key="index"
|
</TMagicButton>
|
||||||
size="small"
|
</div>
|
||||||
link
|
|
||||||
@click="item.handler"
|
|
||||||
>
|
|
||||||
<TMagicIcon><component :is="item.icon"></component></TMagicIcon><span>{{ item.text }}</span>
|
|
||||||
</TMagicButton>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@ -85,6 +85,10 @@ export default defineConfig({
|
|||||||
find: /^@tmagic\/element-plus-adapter/,
|
find: /^@tmagic\/element-plus-adapter/,
|
||||||
replacement: path.join(__dirname, '../packages/element-plus-adapter/src/index.ts'),
|
replacement: path.join(__dirname, '../packages/element-plus-adapter/src/index.ts'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
find: /^@tmagic\/tdesign-vue-next-adapter/,
|
||||||
|
replacement: path.join(__dirname, '../packages/tdesign-vue-next-adapter/src/index.ts'),
|
||||||
|
},
|
||||||
] : [],
|
] : [],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user