feat(design, form, tdesign-vue-next-adapter): 完善tdesign适配

This commit is contained in:
roymondchen 2025-10-23 19:56:57 +08:00
parent 68c69ac405
commit ca0f8fc988
9 changed files with 189 additions and 37 deletions

View File

@ -39,5 +39,9 @@ const clickHandler = (...args: any[]) => {
.t-button__text { .t-button__text {
align-items: center; align-items: center;
} }
+ .tmagic-design-button {
margin-left: 12px;
}
} }
</style> </style>

View File

@ -7,6 +7,7 @@
@change="changeHandler" @change="changeHandler"
@input="inputHandler" @input="inputHandler"
@update:modelValue="updateModelValue" @update:modelValue="updateModelValue"
@blur="blurHandler"
> >
<template #prepend v-if="$slots.prepend"> <template #prepend v-if="$slots.prepend">
<slot name="prepend"></slot> <slot name="prepend"></slot>
@ -41,7 +42,7 @@ const uiComponent = ui?.component || 'el-input';
const uiProps = computed<InputProps>(() => ui?.props(props) || props); const uiProps = computed<InputProps>(() => ui?.props(props) || props);
const emit = defineEmits(['change', 'input', 'update:modelValue']); const emit = defineEmits(['change', 'input', 'blur', 'update:modelValue']);
const instance = ref<any>(); const instance = ref<any>();
@ -57,13 +58,52 @@ const updateModelValue = (...args: any[]) => {
emit('update:modelValue', ...args); emit('update:modelValue', ...args);
}; };
const blurHandler = (...args: any[]) => {
emit('blur', ...args);
};
defineExpose({ defineExpose({
instance, instance,
getInput() { getInput() {
return instance.value.input; if (instance.value.input) {
return instance.value.input;
}
return instance.value?.$el?.querySelector('input');
}, },
getTextarea() { getTextarea() {
return instance.value.textarea; if (instance.value.textarea) {
return instance.value.textarea;
}
return instance.value?.$el?.querySelector('textarea');
}, },
}); });
</script> </script>
<style lang="scss">
.tmagic-design-input {
&.t-input-adornment {
.t-input-adornment__prepend {
> span {
border-radius: var(--td-radius-default) 0 0 var(--td-radius-default);
}
}
.t-input-adornment__append {
> span {
border-radius: 0 var(--td-radius-default) var(--td-radius-default) 0;
}
}
.t-input-adornment__prepend,
.t-input-adornment__append {
> span {
display: inline-flex;
height: 100%;
align-items: center;
box-sizing: border-box;
white-space: nowrap;
padding: 0 var(--td-comp-paddingLR-s);
border: 1px solid var(--td-border-level-2-color);
}
}
}
}
</style>

View File

@ -8,6 +8,9 @@
@tab-remove="onTabRemove" @tab-remove="onTabRemove"
@update:model-value="updateModelName" @update:model-value="updateModelName"
> >
<template #add-icon v-if="$slots['add-icon']">
<slot name="add-icon"></slot>
</template>
<template #default> <template #default>
<slot></slot> <slot></slot>
</template> </template>

View File

@ -32,6 +32,7 @@ export interface ButtonProps {
text?: boolean; text?: boolean;
circle?: boolean; circle?: boolean;
icon?: any; icon?: any;
variant?: string;
} }
export interface CardProps { export interface CardProps {
@ -193,6 +194,7 @@ export interface InputProps {
rows?: number; rows?: number;
type?: string; type?: string;
size?: FieldSize; size?: FieldSize;
row?: number;
} }
export interface InputNumberProps { export interface InputNumberProps {
@ -354,7 +356,7 @@ export interface TabPaneProps {
export interface TabsProps { export interface TabsProps {
type?: string; type?: string;
editable?: boolean; editable?: boolean;
tabPosition?: string; tabPosition?: 'left' | 'right' | 'top' | 'bottom';
modelValue?: string | number; modelValue?: string | number;
} }

View File

@ -167,7 +167,17 @@ watchEffect(() => {
const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items); const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items);
const tabClickHandler = (tab: any) => tabClick(mForm, tab, props); const tabClickHandler = (tab: any) => {
if (typeof tab === 'object') {
tabClick(mForm, tab, props);
} else {
let item = tabs.value.find((tab: any) => tab.status === tab);
if (!item) {
item = tabs.value[tab];
}
tabClick(mForm, item, props);
}
};
const onTabAdd = async () => { const onTabAdd = async () => {
if (!props.name) throw new Error('dynamic tab 必须配置name'); if (!props.name) throw new Error('dynamic tab 必须配置name');

View File

@ -48,4 +48,8 @@
margin-left: 0 !important; margin-left: 0 !important;
} }
} }
&.t-form:not(.t-form-inline) .t-form__item:last-of-type {
margin-bottom: var(--td-comp-margin-xxl);
}
} }

View File

@ -5,34 +5,46 @@
:size="size === 'default' ? 'medium' : size" :size="size === 'default' ? 'medium' : size"
:disabled="disabled" :disabled="disabled"
:placeholder="placeholder" :placeholder="placeholder"
:row="row"
@keypress="inputHandler" @keypress="inputHandler"
@change="changeHandler" @change="changeHandler"
></TTextarea> ></TTextarea>
<TInput <TInputAdornment v-else>
v-else <template #prepend v-if="$slots.prepend">
:modelValue="modelValue" <slot name="prepend"></slot>
:size="size === 'default' ? 'medium' : size"
:clearable="clearable"
:disabled="disabled"
:placeholder="placeholder"
@keypress="inputHandler"
@change="changeHandler"
@update:modelValue="updateModelValue"
>
<template #prefix-icon v-if="$slots.prefix">
<slot name="prefix"></slot>
</template> </template>
<template #suffix v-if="$slots.suffix"> <template #append v-if="$slots.append">
<slot name="suffix"></slot> <slot name="append"></slot>
</template> </template>
</TInput> <TInput
:modelValue="modelValue"
:size="size === 'default' ? 'medium' : size"
:clearable="clearable"
:disabled="disabled"
:placeholder="placeholder"
@keypress="inputHandler"
@change="changeHandler"
@update:modelValue="updateModelValue"
>
<template #prefix-icon v-if="$slots.prefix">
<slot name="prefix"></slot>
</template>
<template #suffix v-if="$slots.suffix">
<slot name="suffix"></slot>
</template>
</TInput>
</TInputAdornment>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { Input as TInput, Textarea as TTextarea } from 'tdesign-vue-next'; import { Input as TInput, InputAdornment as TInputAdornment, Textarea as TTextarea } from 'tdesign-vue-next';
import type { InputProps } from '@tmagic/design'; import type { InputProps } from '@tmagic/design';
defineOptions({
name: 'TTDesignAdapterInput',
});
defineProps< defineProps<
InputProps & { InputProps & {
modelValue: string; modelValue: string;

View File

@ -0,0 +1,49 @@
<template>
<TTabs
:model-value="modelValue"
:addable="editable"
:theme="type === 'card' ? 'card' : 'normal'"
:placement="tabPosition"
@add="onTabAdd"
@change="tabClickHandler"
@remove="onTabRemove"
@update:model-value="updateModelName"
>
<template #action v-if="$slots['add-icon']">
<slot name="add-icon"></slot>
</template>
<template #default>
<slot></slot>
</template>
</TTabs>
</template>
<script setup lang="ts">
import { Tabs as TTabs } from 'tdesign-vue-next';
import type { TabsProps } from '@tmagic/design';
defineOptions({
name: 'TTDesignAdapterTabs',
});
defineProps<TabsProps>();
const emit = defineEmits(['tab-click', 'tab-add', 'tab-remove', 'update:model-value']);
const tabClickHandler = (...args: any[]) => {
emit('tab-click', ...args);
};
const onTabAdd = (...args: any[]) => {
emit('tab-add', ...args);
};
const onTabRemove = (...args: any[]) => {
emit('tab-remove', ...args);
};
const updateModelName = (...args: any[]) => {
emit('update:model-value', ...args);
};
</script>

View File

@ -17,6 +17,7 @@ import {
Form as TForm, Form as TForm,
FormItem as TFormItem, FormItem as TFormItem,
InputNumber as TInputNumber, InputNumber as TInputNumber,
LoadingDirective,
MessagePlugin, MessagePlugin,
Option as TOption, Option as TOption,
OptionGroup as TOptionGroup, OptionGroup as TOptionGroup,
@ -28,7 +29,6 @@ import {
Steps as TSteps, Steps as TSteps,
Switch as TSwitch, Switch as TSwitch,
TabPanel as TTabPanel, TabPanel as TTabPanel,
Tabs as TTabs,
Tag as TTag, Tag as TTag,
TimePicker as TTimePicker, TimePicker as TTimePicker,
Tooltip as TTooltip, Tooltip as TTooltip,
@ -59,6 +59,7 @@ import type {
OptionGroupProps, OptionGroupProps,
OptionProps, OptionProps,
PaginationProps, PaginationProps,
PopconfirmProps,
RadioButtonProps, RadioButtonProps,
RadioGroupProps, RadioGroupProps,
RadioProps, RadioProps,
@ -80,22 +81,46 @@ import DatePicker from './DatePicker.vue';
import Dialog from './Dialog.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 Popconfirm from './Popconfirm.vue';
import Radio from './Radio.vue'; import Radio from './Radio.vue';
import RadioButton from './RadioButton.vue'; import RadioButton from './RadioButton.vue';
import Scrollbar from './Scrollbar.vue'; import Scrollbar from './Scrollbar.vue';
import Table from './Table.vue'; import Table from './Table.vue';
import Tabs from './Tabs.vue';
const adapter: any = { const adapter: any = {
message: MessagePlugin, message: MessagePlugin,
messageBox: { messageBox: {
alert: (msg: string) => { alert: (msg: string, title?: string) => {
DialogPlugin.alert({ return new Promise((resolve, reject) => {
body: msg, const dia = DialogPlugin.alert({
header: title,
body: msg,
onConfirm: (e) => {
dia.hide();
resolve(e);
},
onClose: (e) => {
dia.hide();
reject(e);
},
});
}); });
}, },
confirm: (msg: string) => { confirm: (msg: string, title?: string) => {
DialogPlugin.confirm({ return new Promise((resolve, reject) => {
body: msg, const dia = DialogPlugin.confirm({
header: title,
body: msg,
onConfirm: (e) => {
dia.hide();
resolve(e);
},
onClose: (e) => {
dia.hide();
reject(e);
},
});
}); });
}, },
close: (msg: string) => { close: (msg: string) => {
@ -118,7 +143,7 @@ const adapter: any = {
theme: props.type, theme: props.type,
size: props.size === 'default' ? 'medium' : props.size, size: props.size === 'default' ? 'medium' : props.size,
icon: props.icon ? () => h(Icon, null, { default: () => h(props.icon) }) : undefined, icon: props.icon ? () => h(Icon, null, { default: () => h(props.icon) }) : undefined,
variant: props.link || props.text ? 'text' : 'base', variant: props.link || props.text ? 'text' : props.variant || 'base',
shape: props.circle ? 'circle' : 'rectangle', shape: props.circle ? 'circle' : 'rectangle',
}), }),
}, },
@ -129,6 +154,8 @@ const adapter: any = {
shadow: props.shadow !== 'never', shadow: props.shadow !== 'never',
hoverShadow: props.shadow === 'hover', hoverShadow: props.shadow === 'hover',
header: props.header, header: props.header,
bodyStyle: props.bodyStyle,
headerBordered: true,
}), }),
}, },
@ -419,13 +446,8 @@ const adapter: any = {
}, },
tabs: { tabs: {
component: TTabs, component: Tabs,
props: (props: TabsProps) => ({ props: (props: TabsProps) => props,
addable: props.editable,
theme: props.type === 'card' ? 'card' : 'normal',
placement: props.tabPosition,
value: props.modelValue,
}),
}, },
tag: { tag: {
@ -462,7 +484,13 @@ const adapter: any = {
autoUpload: props.autoUpload, autoUpload: props.autoUpload,
}), }),
}, },
popconfirm: {
component: Popconfirm,
props: (props: PopconfirmProps) => props,
},
}, },
loading: LoadingDirective,
}; };
export default adapter; export default adapter;