feat(form): 剥离element-plus依赖,使用tamgic-design

#401
This commit is contained in:
roymondchen 2022-10-11 15:25:54 +08:00 committed by jia000
parent c613b12f11
commit 3a1a9795f6
35 changed files with 2067 additions and 2528 deletions

View File

@ -35,14 +35,13 @@
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.9",
"@tmagic/design": "1.2.0-beta.2",
"@tmagic/utils": "1.2.0-beta.2",
"element-plus": "^2.2.17",
"lodash-es": "^4.17.21",
"sortablejs": "^1.14.0",
"vue": "^3.2.37"
},
"peerDependencies": {
"element-plus": "^2.2.17",
"vue": "^3.2.37"
},
"devDependencies": {

View File

@ -1,7 +1,7 @@
<template>
<el-form
<TMagicForm
class="m-form"
ref="elForm"
ref="tMagicForm"
:model="values"
:label-width="labelWidth"
:disabled="disabled"
@ -10,7 +10,7 @@
:label-position="labelPosition"
>
<template v-if="initialized && Array.isArray(config)">
<m-form-container
<Container
v-for="(item, index) in config"
:key="item[keyProp] ?? index"
:config="item"
@ -19,96 +19,55 @@
:step-active="stepActive"
:size="size"
@change="changeHandler"
></m-form-container>
></Container>
</template>
</el-form>
</TMagicForm>
</template>
<script lang="ts">
import { defineComponent, PropType, provide, reactive, ref, toRaw, watch } from 'vue';
import { cloneDeep, isEqual } from 'lodash-es';
<script setup lang="ts">
import { provide, reactive, ref, toRaw, watch } from 'vue';
import cloneDeep from 'lodash-es/cloneDeep';
import isEqual from 'lodash-es/isEqual';
import { TMagicForm } from '@tmagic/design';
import Container from './containers/Container.vue';
import { getConfig } from './utils/config';
import { initValue } from './utils/form';
import { FormConfig, FormState, FormValue } from './schema';
import type { FormConfig, FormState, FormValue, ValidateError } from './schema';
interface ValidateError {
message: string;
field: string;
}
export default defineComponent({
name: 'm-form',
props: {
//
initValues: {
type: Object,
required: true,
default: () => ({}),
const props = withDefaults(
defineProps<{
config: FormConfig;
initValues: Object;
parentValues?: Object;
labelWidth?: string;
disabled?: boolean;
height?: string;
stepActive?: string | number;
size?: 'small' | 'default' | 'large';
inline?: boolean;
labelPosition?: string;
keyProp?: string;
popperClass?: string;
}>(),
{
config: () => [],
initValues: () => ({}),
parentValues: () => ({}),
labelWidth: '200px',
disabled: false,
height: 'auto',
stepActive: 1,
inline: false,
labelPosition: 'right',
keyProp: '__key',
},
);
parentValues: {
type: Object,
default: () => ({}),
},
const emit = defineEmits(['change', 'field-input', 'field-change']);
//
config: {
type: Array as PropType<FormConfig>,
required: true,
default: () => [],
},
labelWidth: {
type: String,
default: () => '200px',
},
disabled: {
type: Boolean,
default: () => false,
},
height: {
type: String,
default: () => 'auto',
},
stepActive: {
type: [String, Number],
default: () => 1,
},
size: {
type: String as PropType<'small' | 'default' | 'large'>,
},
inline: {
type: Boolean,
default: false,
},
labelPosition: {
type: String,
default: 'right',
},
keyProp: {
type: String,
default: '__key',
},
popperClass: {
type: String,
},
},
emits: ['change', 'field-input', 'field-change'],
setup(props, { emit }) {
// InstanceType<typeof ElForm>types
const elForm = ref<any>();
const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
const initialized = ref(false);
const values = ref<FormValue>({});
const fields = new Map<string, any>();
@ -157,22 +116,22 @@ export default defineComponent({
{ immediate: true },
);
return {
initialized,
values,
elForm,
formState,
changeHandler: () => {
const changeHandler = () => {
emit('change', values.value);
},
};
resetForm: () => elForm.value?.resetFields(),
defineExpose({
values,
formState,
initialized,
changeHandler,
resetForm: () => tMagicForm.value?.resetFields(),
submitForm: async (native?: boolean): Promise<any> => {
try {
await elForm.value?.validate();
await tMagicForm.value?.validate();
return native ? values.value : cloneDeep(toRaw(values.value));
} catch (invalidFields: any) {
const error: string[] = [];
@ -187,7 +146,5 @@ export default defineComponent({
throw new Error(error.join('<br>'));
}
},
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-dialog
<TMagicDialog
v-model="dialogVisible"
class="m-form-dialog"
top="20px"
@ -15,7 +15,7 @@
class="m-dialog-body"
:style="`max-height: ${bodyHeight}; overflow-y: auto; overflow-x: hidden;`"
>
<m-form
<Form
v-model="stepActive"
ref="form"
:size="size"
@ -24,78 +24,65 @@
:parent-values="parentValues"
:label-width="labelWidth"
@change="changeHandler"
></m-form>
></Form>
<slot></slot>
</div>
<template #footer>
<el-row class="dialog-footer">
<el-col :span="12" style="text-align: left">
<TMagicRow class="dialog-footer">
<TMagicCol :span="12" style="text-align: left">
<div style="min-height: 1px">
<slot name="left"></slot>
</div>
</el-col>
<el-col :span="12">
</TMagicCol>
<TMagicCol :span="12">
<slot name="footer">
<el-button @click="cancel" size="small"> </el-button>
<el-button v-if="hasStep && stepActive > 1" type="info" size="small" @click="preStep">上一步</el-button>
<el-button v-if="hasStep && stepCount > stepActive" type="info" size="small" @click="nextStep"
>下一步</el-button
<TMagicButton @click="cancel" size="small"> </TMagicButton>
<TMagicButton v-if="hasStep && stepActive > 1" type="info" size="small" @click="preStep"
>上一步</TMagicButton
>
<el-button v-else type="primary" size="small" :loading="saveFetch" @click="save">{{
<TMagicButton v-if="hasStep && stepCount > stepActive" type="info" size="small" @click="nextStep"
>下一步</TMagicButton
>
<TMagicButton v-else type="primary" size="small" :loading="saveFetch" @click="save">{{
confirmText
}}</el-button>
}}</TMagicButton>
</slot>
</el-col>
</el-row>
</TMagicCol>
</TMagicRow>
</template>
</el-dialog>
</TMagicDialog>
</template>
<script lang="ts">
import { computed, defineComponent, PropType, ref } from 'vue';
<script setup lang="ts">
import { computed, ref } from 'vue';
import { TMagicButton, TMagicCol, TMagicDialog, TMagicRow } from '@tmagic/design';
import Form from './Form.vue';
import { FormConfig, StepConfig } from './schema';
export default defineComponent({
name: 'm-form-dialog',
props: {
values: {
type: Object,
default: () => ({}),
const props = withDefaults(
defineProps<{
config?: FormConfig;
values?: Object;
parentValues?: Object;
width?: string | number;
labelWidth?: string;
fullscreen?: boolean;
title?: string;
size?: 'small' | 'default' | 'large';
confirmText?: string;
}>(),
{
config: () => [],
values: () => ({}),
confirmText: '确定',
},
);
parentValues: {
type: Object,
},
const emit = defineEmits(['close', 'submit', 'error', 'change']);
width: [Number, String],
fullscreen: Boolean,
title: String,
config: {
type: Array as PropType<FormConfig>,
required: true,
default: () => [],
},
labelWidth: [Number, String],
size: String as PropType<'small' | 'default' | 'large'>,
confirmText: {
type: String,
default: '确定',
},
},
emits: ['close', 'submit', 'error', 'change'],
setup(props, { emit }) {
const form = ref<InstanceType<typeof Form>>();
const dialogVisible = ref(false);
const saveFetch = ref(false);
@ -153,21 +140,12 @@ export default defineComponent({
emit('change', value);
};
return {
defineExpose({
form,
saveFetch,
stepActive,
dialogVisible,
bodyHeight,
stepCount,
hasStep,
cancel,
closeHandler,
save,
preStep,
nextStep,
changeHandler,
};
},
});
</script>

View File

@ -1,6 +1,6 @@
<template>
<el-col v-show="display && config.type !== 'hidden'" :span="span">
<m-form-container
<TMagicCol v-show="display && config.type !== 'hidden'" :span="span">
<Container
:model="model"
:config="config"
:prop="prop"
@ -8,49 +8,33 @@
:expand-more="expandMore"
:size="size"
@change="changeHandler"
></m-form-container>
</el-col>
></Container>
</TMagicCol>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType } from 'vue';
<script setup lang="ts">
import { computed, inject } from 'vue';
import { TMagicCol } from '@tmagic/design';
import { ChildConfig, FormState } from '../schema';
import { display as displayFunction } from '../utils/form';
export default defineComponent({
props: {
labelWidth: String,
expandMore: Boolean,
span: Number,
import Container from './Container.vue';
model: {
type: Object,
default: () => ({}),
},
const props = defineProps<{
model: any;
config: ChildConfig;
labelWidth?: string;
expandMore?: boolean;
span?: number;
size?: string;
prop?: string;
}>();
config: {
type: Object as PropType<ChildConfig>,
default: () => ({}),
},
const emit = defineEmits(['change']);
prop: String,
size: String,
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const display = computed(() => displayFunction(mForm, props.config.display, props));
const changeHandler = () => emit('change', props.model);
return {
mForm,
display: computed(() => displayFunction(mForm, props.config.display, props)),
changeHandler,
};
},
});
</script>

View File

@ -30,7 +30,7 @@
></component>
<template v-else-if="type && display">
<el-form-item
<TMagicFormItem
:style="config.tip ? 'flex: 1' : ''"
:class="{ hidden: `${itemLabelWidth}` === '0' || !config.text }"
:prop="itemProp"
@ -38,7 +38,7 @@
:rules="rule"
>
<template #label><span v-html="type === 'checkbox' ? '' : config.text"></span></template>
<el-tooltip v-if="tooltip">
<TMagicTooltip v-if="tooltip">
<component
:key="key(config)"
:size="size"
@ -53,7 +53,7 @@
<template #content>
<div v-html="tooltip"></div>
</template>
</el-tooltip>
</TMagicTooltip>
<component
v-else
@ -69,19 +69,19 @@
></component>
<div v-if="extra" v-html="extra" class="m-form-tip"></div>
</el-form-item>
</TMagicFormItem>
<el-tooltip v-if="config.tip" placement="left">
<el-icon style="line-height: 40px; margin-left: 5px"><warning-filled /></el-icon>
<TMagicTooltip v-if="config.tip" placement="left">
<TMagicIcon style="line-height: 40px; margin-left: 5px"><warning-filled /></TMagicIcon>
<template #content>
<div v-html="config.tip"></div>
</template>
</el-tooltip>
</TMagicTooltip>
</template>
<template v-else-if="items && display">
<template v-if="name || name === 0 ? model[name] : model">
<m-form-container
<Container
v-for="item in items"
:key="key(item)"
:model="name || name === 0 ? model[name] : model"
@ -92,60 +92,44 @@
:label-width="itemLabelWidth"
:prop="itemProp"
@change="onChangeHandler"
></m-form-container>
></Container>
</template>
</template>
<div style="text-align: center" v-if="config.expand && type !== 'fieldset'">
<el-button text @click="expandHandler">{{ expand ? '收起配置' : '展开更多配置' }}</el-button>
<TMagicButton text @click="expandHandler">{{ expand ? '收起配置' : '展开更多配置' }}</TMagicButton>
</div>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType, ref, resolveComponent, watchEffect } from 'vue';
<script setup lang="ts">
import { computed, inject, ref, resolveComponent, watchEffect } from 'vue';
import { WarningFilled } from '@element-plus/icons-vue';
import { TMagicButton, TMagicFormItem, TMagicIcon, TMagicTooltip } from '@tmagic/design';
import { ChildConfig, ContainerCommonConfig, FormState, FormValue } from '../schema';
import { display as displayFunction, filterFunction, getRules } from '../utils/form';
export default defineComponent({
name: 'm-form-container',
components: { WarningFilled },
props: {
labelWidth: String,
expandMore: Boolean,
model: {
type: [Object, Array] as PropType<FormValue>,
required: true,
const props = withDefaults(
defineProps<{
model: FormValue;
config: ChildConfig;
prop?: string;
labelWidth?: string;
expandMore?: boolean;
stepActive?: string | number;
size?: string;
}>(),
{
prop: '',
size: 'small',
expandMore: false,
},
);
config: {
type: Object as PropType<ChildConfig>,
required: true,
},
const emit = defineEmits(['change']);
prop: {
type: String,
default: () => '',
},
stepActive: {
type: [String, Number],
},
size: {
type: String,
default: 'small',
},
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(false);
@ -270,24 +254,4 @@ export default defineComponent({
}
emit('change', props.model);
};
return {
expand,
name,
type,
disabled,
itemProp,
items,
display,
itemLabelWidth,
tagName,
rule,
tooltip,
extra,
key,
onChangeHandler,
expandHandler,
};
},
});
</script>

View File

@ -5,13 +5,13 @@
:style="show ? 'padding: 15px 15px 0 5px;' : 'border: 0'"
>
<component v-if="name && config.checkbox" :is="!show ? 'div' : 'legend'">
<el-checkbox
<TMagicCheckbox
v-model="model[name].value"
:prop="`${prop}${prop ? '.' : ''}${config.name}.value`"
:true-label="1"
:false-label="0"
><span v-html="config.legend"></span><span v-if="config.extra" v-html="config.extra" class="m-form-tip"></span
></el-checkbox>
></TMagicCheckbox>
</component>
<legend v-else>
<span v-html="config.legend"></span>
@ -20,7 +20,7 @@
<div v-if="config.schematic && show" style="display: flex">
<div style="flex: 1">
<m-form-container
<Container
v-for="(item, index) in config.items"
:key="key(item, index)"
:model="name ? model[name] : model"
@ -30,14 +30,14 @@
:labelWidth="lWidth"
:size="size"
@change="change"
></m-form-container>
></Container>
</div>
<img class="m-form-schematic" :src="config.schematic" />
</div>
<template v-else-if="show">
<m-form-container
<Container
v-for="(item, index) in config.items"
:key="key(item, index)"
:model="name ? model[name] : model"
@ -47,7 +47,7 @@
:labelWidth="lWidth"
:size="size"
@change="change"
></m-form-container>
></Container>
</template>
</fieldset>
</template>
@ -55,8 +55,12 @@
<script lang="ts" setup>
import { computed, inject, watch } from 'vue';
import { TMagicCheckbox } from '@tmagic/design';
import { FieldsetConfig, FormState } from '../schema';
import Container from './Container.vue';
const props = withDefaults(
defineProps<{
labelWidth?: string;
@ -89,7 +93,7 @@ const lWidth = computed(() => {
if (props.config.items) {
return props.config.labelWidth || props.labelWidth;
}
return props.config.labelWidth || props.labelWidth || (props.config.text ? null : '0');
return props.config.labelWidth || props.labelWidth || (props.config.text ? undefined : '0');
});
const change = () => {

View File

@ -5,7 +5,7 @@
<span class="el-table__empty-text">暂无数据</span>
</div>
<m-fields-group-list-item
<MFieldsGroupListItem
v-else
v-for="(item, index) in model[name]"
:key="index"
@ -19,57 +19,36 @@
@remove-item="removeHandler"
@swap-item="swapHandler"
@change="changeHandler"
></m-fields-group-list-item>
></MFieldsGroupListItem>
<el-button @click="addHandler" size="small" v-if="addable">添加组</el-button>
<TMagicButton @click="addHandler" size="small" v-if="addable">添加组</TMagicButton>
<el-button :icon="Grid" size="small" @click="toggleMode" v-if="config.enableToggleMode">切换为表格</el-button>
<TMagicButton :icon="Grid" size="small" @click="toggleMode" v-if="config.enableToggleMode">切换为表格</TMagicButton>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType } from 'vue';
<script setup lang="ts">
import { computed, inject } from 'vue';
import { Grid } from '@element-plus/icons-vue';
import { TMagicButton } from '@tmagic/design';
import { FormState, GroupListConfig } from '../schema';
import { initValue } from '../utils/form';
import MFieldsGroupListItem from './GroupListItem.vue';
export default defineComponent({
name: 'm-form-group-list',
const props = defineProps<{
model: any;
config: GroupListConfig;
name: string;
labelWidth?: string;
prop?: string;
size?: string;
}>();
components: { MFieldsGroupListItem },
const emit = defineEmits(['change']);
props: {
labelWidth: String,
model: {
type: Object,
default: () => ({}),
},
config: {
type: Object as PropType<GroupListConfig>,
default: () => ({}),
},
prop: {
type: String,
default: '',
},
size: String,
name: {
type: String,
default: '',
},
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const addable = computed(() => {
@ -142,15 +121,4 @@ export default defineComponent({
text: null,
}))) as any;
};
return {
Grid,
addable,
toggleMode,
removeHandler,
swapHandler,
changeHandler,
addHandler,
};
},
});
</script>

View File

@ -1,33 +1,33 @@
<template>
<div class="m-fields-group-list-item">
<div>
<el-icon style="margin-right: 7px" @click="expandHandler"
<TMagicIcon style="margin-right: 7px" @click="expandHandler"
><CaretBottom v-if="expand" /><CaretRight v-else
/></el-icon>
/></TMagicIcon>
<el-button text @click="expandHandler">{{ title }}</el-button>
<TMagicButton text @click="expandHandler">{{ title }}</TMagicButton>
<el-button
<TMagicButton
v-show="showDelete(parseInt(String(index)))"
text
:icon="Delete"
style="color: #f56c6c"
@click="removeHandler"
></el-button>
></TMagicButton>
<template v-if="movable()">
<el-button v-show="index !== 0" text size="small" @click="changeOrder(-1)"
>上移<el-icon><CaretTop /></el-icon
></el-button>
<el-button v-show="index !== length - 1" text size="small" @click="changeOrder(1)"
>下移<el-icon><CaretBottom /></el-icon
></el-button>
<TMagicButton v-show="index !== 0" text size="small" @click="changeOrder(-1)"
>上移<TMagicIcon><CaretTop /></TMagicIcon
></TMagicButton>
<TMagicButton v-show="index !== length - 1" text size="small" @click="changeOrder(1)"
>下移<TMagicIcon><CaretBottom /></TMagicIcon
></TMagicButton>
</template>
<span v-if="itemExtra" v-html="itemExtra" class="m-form-tip"></span>
</div>
<m-form-container
<Container
v-if="expand"
:config="rowConfig"
:model="model"
@ -35,53 +35,33 @@
:prop="`${prop}${prop ? '.' : ''}${String(index)}`"
:size="size"
@change="changeHandler"
></m-form-container>
></Container>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType, ref, watchEffect } from 'vue';
<script setup lang="ts">
import { computed, inject, ref, watchEffect } from 'vue';
import { CaretBottom, CaretRight, CaretTop, Delete } from '@element-plus/icons-vue';
import { TMagicButton, TMagicIcon } from '@tmagic/design';
import { FormState, GroupListConfig } from '../schema';
import { filterFunction } from '../utils/form';
export default defineComponent({
name: 'm-form-group-list-item',
import Container from './Container.vue';
components: { CaretBottom, CaretRight, CaretTop },
const props = defineProps<{
model: any;
groupModel: any[];
config: GroupListConfig;
labelWidth?: string;
prop?: string;
size?: string;
index: number;
}>();
props: {
labelWidth: String,
const emit = defineEmits(['swap-item', 'remove-item', 'change']);
model: {
type: Object,
default: () => ({}),
},
config: {
type: Object as PropType<GroupListConfig>,
default: () => ({}),
},
prop: String,
size: String,
index: {
type: [Number, String, Symbol],
default: 0,
},
groupModel: {
type: Array,
default: () => [],
},
},
emits: ['swap-item', 'remove-item', 'change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(false);
@ -139,21 +119,4 @@ export default defineComponent({
}
return movable;
};
return {
expand,
expandHandler,
title,
showDelete,
removeHandler,
movable,
changeOrder,
itemExtra,
rowConfig,
changeHandler,
length,
Delete,
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-card
<TMagicCard
v-if="items && items.length"
class="box-card m-form-panel"
:body-style="{ display: expand ? 'block' : 'none' }"
@ -7,7 +7,7 @@
<template #header>
<div class="clearfix">
<a href="javascript:" style="width: 100%; display: block" @click="expand = !expand">
<el-icon><caret-bottom v-if="expand" /><caret-right v-else /></el-icon> {{ filter(config.title) }}
<TMagicIcon><CaretBottom v-if="expand" /><CaretRight v-else /></TMagicIcon> {{ filter(config.title) }}
<span v-if="config && config.extra" v-html="config.extra" class="m-form-tip"></span>
</a>
</div>
@ -18,7 +18,7 @@
<div v-if="config.schematic" style="display: flex">
<div style="flex: 1">
<m-form-container
<Container
v-for="(item, index) in items"
:key="item[mForm?.keyProp || '__key'] ?? index"
:config="item"
@ -27,14 +27,14 @@
:size="size"
:label-width="config.labelWidth || labelWidth"
@change="changeHandler"
></m-form-container>
></Container>
</div>
<img class="m-form-schematic" :src="config.schematic" />
</div>
<template v-else>
<m-form-container
<Container
v-for="(item, index) in items"
:key="item[mForm?.keyProp || '__key'] ?? index"
:config="item"
@ -43,47 +43,34 @@
:size="size"
:label-width="config.labelWidth || labelWidth"
@change="changeHandler"
></m-form-container>
></Container>
</template>
</div>
</el-card>
</TMagicCard>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType, ref } from 'vue';
<script setup lang="ts">
import { computed, inject, ref } from 'vue';
import { CaretBottom, CaretRight } from '@element-plus/icons-vue';
import { TMagicCard, TMagicIcon } from '@tmagic/design';
import { FormState, PanelConfig } from '../schema';
import { filterFunction } from '../utils/form';
export default defineComponent({
name: 'm-form-panel',
import Container from './Container.vue';
components: { CaretBottom, CaretRight },
const props = defineProps<{
model: any;
config: PanelConfig;
name: string;
labelWidth?: string;
prop?: string;
size?: string;
}>();
props: {
labelWidth: String,
const emit = defineEmits(['change']);
model: {
type: Object,
default: () => ({}),
},
config: {
type: Object as PropType<PanelConfig>,
default: () => ({}),
},
prop: String,
size: String,
name: String,
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(props.config.expand !== false);
@ -93,14 +80,4 @@ export default defineComponent({
const filter = (config: any) => filterFunction(mForm, config, props);
const changeHandler = () => emit('change', props.model);
return {
mForm,
expand,
items,
filter,
changeHandler,
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-row :gutter="10">
<TMagicRow :gutter="10">
<Col
v-for="(col, index) in config.items"
:key="col[mForm?.keyProp || '__key'] ?? index"
@ -12,53 +12,31 @@
:size="size"
@change="changeHandler"
/>
</el-row>
</TMagicRow>
</template>
<script lang="ts">
import { defineComponent, inject, PropType } from 'vue';
<script setup lang="ts">
import { inject } from 'vue';
import { TMagicRow } from '@tmagic/design';
import { FormState, RowConfig } from '../schema';
import Col from './Col.vue';
export default defineComponent({
name: 'm-form-row',
const props = defineProps<{
model: any;
config: RowConfig;
name: string;
labelWidth?: string;
prop?: string;
size?: string;
expandMore?: boolean;
}>();
components: { Col },
const emit = defineEmits(['change']);
props: {
labelWidth: String,
expandMore: Boolean,
model: {
type: Object,
default: () => ({}),
},
config: {
type: Object as PropType<RowConfig>,
default: () => ({}),
},
prop: String,
name: String,
size: String,
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const changeHandler = () => emit('change', props.name ? props.model[props.name] : props.model);
return {
mForm,
changeHandler,
};
},
});
</script>

View File

@ -1,65 +1,57 @@
<template>
<div>
<el-steps :active="active" align-center :space="config.space">
<el-step
<TMagicSteps :active="active" align-center :space="config.space">
<TMagicStep
v-for="(item, index) in config.items"
:key="item.__key"
:title="item.title"
:active="active"
@click="stepClick(index)"
></el-step>
</el-steps>
></TMagicStep>
</TMagicSteps>
<template v-for="(step, index) in config.items">
<template v-for="item in step.items">
<m-form-container
<Container
v-if="item"
v-show="active - 1 === index"
:key="item[mForm?.keyProp || '__key']"
:config="item"
:model="step.name ? model[step.name] : model"
:prop="step.name"
:prop="`${step.name}`"
:size="size"
:label-width="config.labelWidth || labelWidth"
@change="changeHandler"
></m-form-container>
></Container>
</template>
</template>
</div>
</template>
<script lang="ts">
import { defineComponent, inject, PropType, ref, watchEffect } from 'vue';
<script setup lang="ts">
import { inject, ref, watchEffect } from 'vue';
import { TMagicStep, TMagicSteps } from '@tmagic/design';
import { FormState, StepConfig } from '../schema';
export default defineComponent({
name: 'm-form-step',
import Container from './Container.vue';
props: {
model: {
type: Object,
default: () => ({}),
const props = withDefaults(
defineProps<{
model: any;
config: StepConfig;
stepActive?: number;
labelWidth?: string;
size?: string;
}>(),
{
stepActive: 1,
},
);
config: {
type: Object as PropType<StepConfig>,
default: () => ({}),
},
const emit = defineEmits(['change']);
stepActive: {
type: Number,
default: () => 1,
},
size: String,
labelWidth: String,
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const active = ref(1);
@ -75,8 +67,4 @@ export default defineComponent({
const changeHandler = () => {
emit('change', props.model);
};
return { mForm, active, stepClick, changeHandler };
},
});
</script>

View File

@ -2,10 +2,10 @@
<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>
<div class="el-form-item__content">
<el-tooltip content="拖拽可排序" placement="left-start" :disabled="config.dropSort !== true">
<el-table
<TMagicTooltip content="拖拽可排序" placement="left-start" :disabled="config.dropSort !== true">
<TMagicTable
v-if="model[modelName]"
ref="elTable"
ref="tMagicTable"
style="width: 100%"
:data="model[modelName]"
:border="config.border"
@ -15,31 +15,36 @@
@select="selectHandle"
@sort-change="sortChange"
>
<el-table-column v-if="config.itemExtra" :fixed="'left'" width="30" type="expand">
<TMagicTableColumn v-if="config.itemExtra" :fixed="'left'" width="30" type="expand">
<template v-slot="scope">
<span v-html="itemExtra(config.itemExtra, scope.$index)" class="m-form-tip"></span>
</template>
</el-table-column>
</TMagicTableColumn>
<el-table-column label="操作" width="55" align="center" :fixed="config.fixed === false ? undefined : 'left'">
<TMagicTableColumn
label="操作"
width="55"
align="center"
:fixed="config.fixed === false ? undefined : 'left'"
>
<template v-slot="scope">
<el-icon
<TMagicIcon
v-show="showDelete(scope.$index + 1 + pagecontext * pagesize - 1)"
class="m-table-delete-icon"
@click="removeHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
><Delete
/></el-icon>
/></TMagicIcon>
</template>
</el-table-column>
</TMagicTableColumn>
<el-table-column v-if="sort && model[modelName] && model[modelName].length > 1" label="排序" width="60">
<TMagicTableColumn v-if="sort && model[modelName] && model[modelName].length > 1" label="排序" width="60">
<template v-slot="scope">
<el-tooltip
<TMagicTooltip
v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== 0"
content="点击上移,双击置顶"
placement="top"
v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== 0"
>
<el-button
<TMagicButton
plain
size="small"
type="primary"
@ -47,14 +52,14 @@
text
@click="upHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
@dblclick="topHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
></el-button>
</el-tooltip>
<el-tooltip
></TMagicButton>
</TMagicTooltip>
<TMagicTooltip
v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== model[modelName].length - 1"
content="点击下移,双击置底"
placement="top"
v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== model[modelName].length - 1"
>
<el-button
<TMagicButton
plain
size="small"
type="primary"
@ -62,25 +67,25 @@
text
@click="downHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
@dblclick="bottomHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
></el-button>
</el-tooltip>
></TMagicButton>
</TMagicTooltip>
</template>
</el-table-column>
</TMagicTableColumn>
<el-table-column
<TMagicTableColumn
v-if="selection"
align="center"
header-align="center"
type="selection"
width="45"
></el-table-column>
></TMagicTableColumn>
<el-table-column width="60" label="序号" v-if="showIndex && config.showIndex">
<TMagicTableColumn width="60" label="序号" v-if="showIndex && config.showIndex">
<template v-slot="scope">{{ scope.$index + 1 + pagecontext * pagesize }}</template>
</el-table-column>
</TMagicTableColumn>
<template v-for="(column, index) in config.items">
<el-table-column
<TMagicTableColumn
v-if="column.type !== 'hidden' && display(column.display)"
:prop="column.name"
:width="column.width"
@ -91,7 +96,7 @@
:class-name="config.dropSort === true ? 'el-table__column--dropable' : ''"
>
<template #default="scope">
<m-form-container
<Container
v-if="scope.$index > -1"
labelWidth="0"
:prop="getProp(scope.$index)"
@ -100,18 +105,23 @@
:model="scope.row"
:size="size"
@change="$emit('change', model[modelName])"
></m-form-container>
></Container>
</template>
</el-table-column>
</TMagicTableColumn>
</template>
</el-table>
</el-tooltip>
</TMagicTable>
</TMagicTooltip>
<slot></slot>
<el-button v-if="addable" size="small" type="primary" plain @click="newHandler()">添加</el-button> &nbsp;
<el-button :icon="Grid" size="small" type="primary" @click="toggleMode" v-if="enableToggleMode && !isFullscreen"
>展开配置</el-button
<TMagicButton v-if="addable" size="small" type="primary" plain @click="newHandler()">添加</TMagicButton> &nbsp;
<TMagicButton
:icon="Grid"
size="small"
type="primary"
@click="toggleMode"
v-if="enableToggleMode && !isFullscreen"
>展开配置</TMagicButton
>
<el-button
<TMagicButton
:icon="FullScreen"
size="small"
type="primary"
@ -119,8 +129,8 @@
v-if="config.enableFullscreen !== false"
>
{{ isFullscreen ? '退出全屏' : '全屏编辑' }}
</el-button>
<el-upload
</TMagicButton>
<TMagicUpload
v-if="importable"
ref="excelBtn"
action="/noop"
@ -128,13 +138,13 @@
:auto-upload="false"
style="display: inline-block"
>
<el-button size="small" type="success" plain>导入EXCEL</el-button> </el-upload
<TMagicButton size="small" type="success" plain>导入EXCEL</TMagicButton> </TMagicUpload
>&nbsp;
<el-button v-if="importable" size="small" type="warning" plain @click="clearHandler()">清空</el-button>
<TMagicButton v-if="importable" size="small" type="warning" plain @click="clearHandler()">清空</TMagicButton>
</div>
<div class="bottom" style="text-align: right">
<el-pagination
<TMagicPagination
layout="total, sizes, prev, pager, next, jumper"
:hide-on-single-page="model[modelName].length < pagesize"
:current-page="pagecontext + 1"
@ -144,88 +154,63 @@
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</TMagicPagination>
</div>
</div>
</template>
<script lang="ts">
/* eslint-disable no-param-reassign */
import { computed, defineComponent, inject, onMounted, PropType, ref, toRefs } from 'vue';
<script setup lang="ts">
import { computed, inject, onMounted, ref, toRefs } from 'vue';
import { ArrowDown, ArrowUp, Delete, FullScreen, Grid } from '@element-plus/icons-vue';
import { ElMessage, UploadFile } from 'element-plus';
import { cloneDeep } from 'lodash-es';
import Sortable, { SortableEvent } from 'sortablejs';
import {
TMagicButton,
TMagicIcon,
tMagicMessage,
TMagicPagination,
TMagicTable,
TMagicTableColumn,
TMagicTooltip,
TMagicUpload,
} from '@tmagic/design';
import { asyncLoadJs, sleep } from '@tmagic/utils';
import { ColumnConfig, FormState, SortProp, TableConfig } from '../schema';
import { display, initValue } from '../utils/form';
import { display as displayFunc, initValue } from '../utils/form';
export default defineComponent({
props: {
name: {
type: String,
required: true,
import Container from './Container.vue';
const props = withDefaults(
defineProps<{
model: any;
config: TableConfig;
name: string;
prop?: string;
labelWidth?: string;
sort?: boolean;
sortKey?: string;
text?: string;
size?: string;
enableToggleMode?: boolean;
showIndex?: boolean;
}>(),
{
prop: '',
sortKey: '',
enableToggleMode: true,
showIndex: true,
},
);
model: {
type: Object,
required: true,
default: () => ({}),
},
const emit = defineEmits(['change', 'select']);
config: {
type: Object as PropType<TableConfig>,
required: true,
default: () => ({}),
},
prop: {
type: String,
},
labelWidth: {
type: String,
},
sort: {
type: Boolean,
default: false,
},
sortKey: {
type: String,
default: '',
},
text: {
type: String,
},
size: String,
enableToggleMode: {
type: Boolean,
default: true,
},
showIndex: {
type: Boolean,
default: true,
},
},
emits: ['change', 'select'],
components: { Delete },
setup(props, { emit }) {
let timer: any | null = null;
const mForm = inject<FormState | undefined>('mForm');
const elTable = ref<any>();
const excelBtn = ref<any>();
const tMagicTable = ref<InstanceType<typeof TMagicTable>>();
const excelBtn = ref<InstanceType<typeof TMagicUpload>>();
const mTable = ref<HTMLDivElement>();
const pagesize = ref(10);
@ -263,7 +248,7 @@ export default defineComponent({
};
const rowDrop = () => {
const tableEl = elTable.value?.$el;
const tableEl = tMagicTable.value?.$el;
if (tableEl?.querySelectorAll('.el-table__body > tbody')) {
// eslint-disable-next-line prefer-destructuring
const el = tableEl.querySelectorAll('.el-table__body > tbody')[0];
@ -292,7 +277,7 @@ export default defineComponent({
const newHandler = async (row?: any) => {
if (props.config.max && props.model[modelName.value].length >= props.config.max) {
ElMessage.error(`最多新增配置不能超过${props.config.max}`);
tMagicMessage.error(`最多新增配置不能超过${props.config.max}`);
return;
}
@ -369,30 +354,7 @@ export default defineComponent({
}
});
return {
mForm,
pagesize,
pagecontext,
foreUpdate,
isFullscreen,
elTable,
mTable,
excelBtn,
modelName,
updateKey,
ArrowDown,
ArrowUp,
Delete,
FullScreen,
Grid,
data: computed(() =>
props.model[modelName.value].filter(
(item: any, index: number) =>
index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value,
),
),
addable: computed(() => {
const addable = computed(() => {
if (!props.model[modelName.value].length) {
return true;
}
@ -404,14 +366,16 @@ export default defineComponent({
});
}
return typeof props.config.addable === 'undefined' ? true : props.config.addable;
}),
selection: computed(() => {
});
const selection = computed(() => {
if (typeof props.config.selection === 'function') {
return props.config.selection(mForm, { model: props.model[modelName.value] });
}
return props.config.selection;
}),
importable: computed(() => {
});
const importable = computed(() => {
if (typeof props.config.importable === 'function') {
return props.config.importable(mForm, {
formValue: mForm?.values,
@ -419,15 +383,11 @@ export default defineComponent({
});
}
return typeof props.config.importable === 'undefined' ? false : props.config.importable;
}),
});
sortChange,
const display = (fuc: any) => displayFunc(mForm, fuc, props);
display(fuc: any) {
return display(mForm, fuc, props);
},
itemExtra(fuc: any, index: number) {
const itemExtra = (fuc: any, index: number) => {
if (typeof fuc === 'function') {
return fuc(mForm, {
values: mForm?.initValues,
@ -439,37 +399,35 @@ export default defineComponent({
}
return fuc;
},
};
newHandler,
removeHandler(index: number) {
const removeHandler = (index: number) => {
props.model[modelName.value].splice(index, 1);
emit('change', props.model[modelName.value]);
},
};
selectHandle(selection: any, row: any) {
const selectHandle = (selection: any, row: any) => {
if (typeof props.config.selection === 'string' && props.config.selection === 'single') {
elTable.value?.clearSelection();
elTable.value?.toggleRowSelection(row, true);
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 });
}
},
};
toggleRowSelection(row: any, selected: boolean) {
elTable.value?.toggleRowSelection.call(elTable.value, row, selected);
},
const toggleRowSelection = (row: any, selected: boolean) => {
tMagicTable.value?.toggleRowSelection.call(tMagicTable.value, row, selected);
};
makeConfig(config: ColumnConfig) {
const makeConfig = (config: ColumnConfig) => {
const newConfig = cloneDeep(config);
delete newConfig.display;
return newConfig;
},
};
upHandler(index: number) {
const upHandler = (index: number) => {
if (timer) {
clearTimeout(timer);
}
@ -477,9 +435,9 @@ export default defineComponent({
timer = setTimeout(() => {
swapArray(index, index - 1);
}, 300);
},
};
topHandler(index: number) {
const topHandler = (index: number) => {
if (timer) {
clearTimeout(timer);
}
@ -492,9 +450,9 @@ export default defineComponent({
swapArray(index, index - 1);
index -= 1;
}
},
};
downHandler(index: number) {
const downHandler = (index: number) => {
if (timer) {
clearTimeout(timer);
}
@ -502,9 +460,9 @@ export default defineComponent({
timer = setTimeout(() => {
swapArray(index, index + 1);
}, 300);
},
};
bottomHandler(index: number) {
const bottomHandler = (index: number) => {
if (timer) {
clearTimeout(timer);
}
@ -517,24 +475,24 @@ export default defineComponent({
swapArray(index, index + 1);
index += 1;
}
},
};
//
showDelete(index: number) {
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;
},
};
clearHandler() {
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]);
},
};
excelHandler(file: UploadFile) {
const excelHandler = (file: any) => {
if (!file || !file.raw) {
return false;
}
@ -557,17 +515,17 @@ export default defineComponent({
reader.readAsArrayBuffer(file.raw);
return false;
},
};
handleSizeChange(val: number) {
const handleSizeChange = (val: number) => {
pagesize.value = val;
},
};
handleCurrentChange(val: number) {
const handleCurrentChange = (val: number) => {
pagecontext.value = val - 1;
},
};
toggleMode() {
const toggleMode = () => {
const calcLabelWidth = (label: string) => {
if (!label) return '0px';
const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
@ -591,9 +549,9 @@ export default defineComponent({
span: item.span || 12,
};
});
},
};
toggleFullscreen() {
const toggleFullscreen = () => {
if (isFullscreen.value) {
mTable.value?.classList.remove('fixed');
isFullscreen.value = false;
@ -601,13 +559,14 @@ export default defineComponent({
mTable.value?.classList.add('fixed');
isFullscreen.value = true;
}
},
};
getProp(index: number) {
const getProp = (index: number) => {
const { prop } = toRefs(props);
return `${prop.value}${prop.value ? '.' : ''}${index + 1 + pagecontext.value * pagesize.value - 1}`;
},
};
},
defineExpose({
toggleRowSelection,
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-tabs
<TMagicTabs
v-model="activeTabName"
:class="config.dynamic ? 'magic-form-dynamic-tab' : 'magic-form-tab'"
:type="config.tabType"
@ -10,14 +10,14 @@
@tab-remove="onTabRemove"
>
<template v-for="(tab, tabIndex) in tabs">
<el-tab-pane
<TMagicTabPane
v-if="display(tab.display) && tabItems(tab).length"
:key="tab[mForm?.keyProp || '__key'] ?? tabIndex"
:name="filter(tab.status) || tabIndex.toString()"
:label="filter(tab.title)"
:lazy="tab.lazy || false"
>
<m-form-container
<Container
v-for="item in tabItems(tab)"
:key="item[mForm?.keyProp || '__key']"
:config="item"
@ -35,18 +35,22 @@
:label-width="tab.labelWidth || labelWidth"
:expand-more="expandMore"
@change="changeHandler"
></m-form-container>
</el-tab-pane>
></Container>
</TMagicTabPane>
</template>
</el-tabs>
</TMagicTabs>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType, ref, watchEffect } from 'vue';
<script setup lang="ts">
import { computed, inject, ref, watchEffect } from 'vue';
import { cloneDeep } from 'lodash-es';
import { FormState, FormValue, TabConfig, TabPaneConfig } from '../schema';
import { display, filterFunction } from '../utils/form';
import { TMagicTabPane, TMagicTabs } from '@tmagic/design';
import { FormState, TabConfig, TabPaneConfig } from '../schema';
import { display as displayFunc, filterFunction } from '../utils/form';
import Container from './Container.vue';
const getActive = (mForm: FormState | undefined, props: any, activeTabName: string) => {
const { config, model, prop } = props;
@ -59,7 +63,7 @@ const getActive = (mForm: FormState | undefined, props: any, activeTabName: stri
return '0';
};
const tabClickHandler = (mForm: FormState | undefined, tab: any, props: any) => {
const tabClick = (mForm: FormState | undefined, tab: any, props: any) => {
const { config, model, prop } = props;
// vue2element-ui
@ -75,37 +79,18 @@ const tabClickHandler = (mForm: FormState | undefined, tab: any, props: any) =>
}
};
const Tab = defineComponent({
name: 'm-form-tab',
const props = defineProps<{
model: any;
config: TabConfig;
name: string;
size?: string;
labelWidth?: string;
prop?: string;
expandMore?: boolean;
}>();
props: {
labelWidth: String,
expandMore: Boolean,
const emit = defineEmits(['change']);
model: {
type: Object,
default: () => ({}),
},
config: {
type: Object as PropType<TabConfig>,
default: () => ({}),
},
prop: String,
name: String,
size: String,
},
emits: {
change(values: FormValue) {
return values;
},
},
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const activeTabName = ref(getActive(mForm, props, ''));
@ -128,20 +113,11 @@ const Tab = defineComponent({
}
});
return {
mForm,
const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items);
activeTabName,
const tabClickHandler = (tab: any) => tabClick(mForm, tab, props);
tabs,
filter,
tabItems: (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items),
tabClickHandler: (tab: any) => tabClickHandler(mForm, tab, props),
onTabAdd: () => {
const onTabAdd = () => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabAdd === 'function') {
@ -157,9 +133,9 @@ const Tab = defineComponent({
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
},
};
onTabRemove: (tabName: string) => {
const onTabRemove = (tabName: string) => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabRemove === 'function') {
@ -174,24 +150,19 @@ const Tab = defineComponent({
//
if (tabName < activeTabName.value || activeTabName.value >= props.model[props.config.name].length) {
activeTabName.value = (+activeTabName.value - 1).toString();
tabClickHandler(mForm, { name: activeTabName.value }, props);
tabClick(mForm, { name: activeTabName.value }, props);
}
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
},
};
display: (displayConfig: any) => display(mForm, displayConfig, props),
const display = (displayConfig: any) => displayFunc(mForm, displayConfig, props);
changeHandler: () => {
const changeHandler = () => {
emit('change', props.model);
if (typeof props.config.onChange === 'function') {
props.config.onChange(mForm, { model: props.model, prop: props.prop, config: props.config });
}
},
};
},
});
export default Tab;
</script>

View File

@ -1,8 +1,7 @@
<template>
<div class="m-cascader" style="width: 100%">
<el-cascader
<TMagicCascader
v-model="model[name]"
ref="cascader"
ref="tMagicCascader"
style="width: 100%"
clearable
filterable
@ -12,50 +11,38 @@
:options="options"
:props="{ multiple: config.multiple }"
@change="changeHandler"
></el-cascader>
</div>
></TMagicCascader>
</template>
<script lang="ts">
import {
ComponentInternalInstance,
computed,
defineComponent,
getCurrentInstance,
inject,
PropType,
ref,
watchEffect,
} from 'vue';
<script setup lang="ts">
import { inject, ref, watchEffect } from 'vue';
import { TMagicCascader } from '@tmagic/design';
import { CascaderConfig, FormState } from '../schema';
import { getConfig } from '../utils/config';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-cascader',
const props = defineProps<{
config: CascaderConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<CascaderConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | null>('mForm');
const vm = getCurrentInstance() as ComponentInternalInstance;
useAddField(props.prop);
const requestFunc = getConfig('request') as Function;
const cascader = ref<any>();
const dialog = ref<any>();
const tMagicCascader = ref<InstanceType<typeof TMagicCascader>>();
const options = Array.isArray(props.config.options) ? ref(props.config.options) : ref([]);
const remoteData = ref<any>(null);
@ -96,51 +83,16 @@ export default defineComponent({
//
if (typeof props.config.options === 'function' && props.model && mForm) {
watchEffect(
() =>
(options.value = (props.config.options as Function)(vm, { model: props.model, formValues: mForm.values })),
() => (options.value = (props.config.options as Function)(mForm, { model: props.model, formValues: mForm.values })),
);
} else if (!props.config.options || !props.config.options.length || props.config.remote) {
Promise.resolve(setRemoteOptions());
}
const action = computed(() => {
if (props.config.add?.action.method === 'post') {
return (options: any) =>
requestFunc({
...props.config?.add?.action.body,
...options,
});
}
return null;
});
return {
options,
remoteData,
addButtonStyle: {
top: 0,
left: 0,
width: 'auto',
},
dialogVisible: false,
cascader,
dialog,
action,
setRemoteOptions,
changeHandler: (value: any) => {
if (!cascader.value) return;
cascader.value.query = '';
cascader.value.previousQuery = null;
const changeHandler = (value: any) => {
if (!tMagicCascader.value) return;
tMagicCascader.value.setQuery('');
tMagicCascader.value.setPreviousQuery(null);
emit('change', value);
},
addHandler: () => {
if (!dialog.value) return;
dialog.value.dialogVisible = true;
},
editAfterAction: () => {
setRemoteOptions();
},
};
},
});
</script>

View File

@ -1,40 +1,39 @@
<template>
<el-checkbox
<TMagicCheckbox
v-model="model[name]"
:size="size"
:trueLabel="activeValue"
:falseLabel="inactiveValue"
:disabled="disabled"
:label="config.text"
@change="changeHandler"
></el-checkbox>
>{{ config.text }}</TMagicCheckbox
>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
<script setup lang="ts">
import { computed } from 'vue';
import { TMagicCheckbox } from '@tmagic/design';
import { CheckboxConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-checkbox',
const props = defineProps<{
config: CheckboxConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<CheckboxConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change', 'input'],
setup(props, { emit }) {
useAddField(props.prop);
return {
activeValue: computed(() => {
const activeValue = computed(() => {
if (typeof props.config.activeValue === 'undefined') {
if (props.config.filter === 'number') {
return 1;
@ -44,9 +43,9 @@ export default defineComponent({
}
return undefined;
}),
});
inactiveValue: computed(() => {
const inactiveValue = computed(() => {
if (typeof props.config.inactiveValue === 'undefined') {
if (props.config.filter === 'number') {
return 0;
@ -56,12 +55,9 @@ export default defineComponent({
}
return undefined;
}),
changeHandler(value: number | boolean) {
emit('change', value);
},
};
},
});
const changeHandler = (value: number | boolean) => {
emit('change', value);
};
</script>

View File

@ -1,44 +1,42 @@
<template>
<el-checkbox-group v-model="model[name]" :size="size" :disabled="disabled" @change="changeHandler">
<el-checkbox v-for="option in config.options" :label="option.value" :key="option.value"
<TMagicCheckboxGroup v-model="model[name]" :size="size" :disabled="disabled" @change="changeHandler">
<TMagicCheckbox
v-for="option in config.options"
:label="option.value"
:key="option.value"
:disabled="option.disabled"
>{{ option.text }}
</el-checkbox>
</el-checkbox-group>
</TMagicCheckbox>
</TMagicCheckboxGroup>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
<script lang="ts" setup>
import { TMagicCheckbox, TMagicCheckboxGroup } from '@tmagic/design';
import { CheckboxGroupConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-checkbox-group',
const props = defineProps<{
config: CheckboxGroupConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<CheckboxGroupConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change'],
setup(props, { emit }) {
useAddField(props.prop);
//
if (props.model && !props.model[props.name]) {
// eslint-disable-next-line no-param-reassign
props.model[props.name] = [];
}
return {
changeHandler: (v: Array<string | number | boolean>) => {
const changeHandler = (v: Array<string | number | boolean>) => {
emit('change', v);
},
};
},
});
</script>

View File

@ -1,14 +1,16 @@
<template>
<el-color-picker
<TMagicColorPicker
v-model="model[name]"
:size="size"
:disabled="disabled"
:showAlpha="true"
@change="changeHandler"
></el-color-picker>
></TMagicColorPicker>
</template>
<script lang="ts" setup>
import { TMagicColorPicker } from '@tmagic/design';
import { ColorPickConfig } from '../schema';
import { useAddField } from '../utils/useAddField';

View File

@ -1,5 +1,5 @@
<template>
<el-date-picker
<TMagicDatePicker
v-model="model[name]"
type="date"
:size="size"
@ -8,40 +8,34 @@
:format="config.format"
:value-format="config.format || 'YYYY-MM-DD HH:mm:ss'"
@change="changeHandler"
></el-date-picker>
></TMagicDatePicker>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
<script lang="ts" setup>
import { TMagicDatePicker } from '@tmagic/design';
import { datetimeFormatter } from '@tmagic/utils';
import { DateConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-date',
const props = defineProps<{
config: DateConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<DateConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change', 'input'],
setup(props, { emit }) {
useAddField(props.prop);
props.model[props.name] = datetimeFormatter(props.model[props.name], '');
return {
changeHandler(v: string) {
const changeHandler = (v: string) => {
emit('change', v);
},
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-date-picker
<TMagicDatePicker
v-model="model[name]"
popper-class="magic-datetime-picker-popper"
type="datetime"
@ -10,32 +10,29 @@
:value-format="config.valueFormat || 'YYYY-MM-DD HH:mm:ss'"
:default-time="config.defaultTime"
@change="changeHandler"
></el-date-picker>
></TMagicDatePicker>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
<script lang="ts" setup>
import { TMagicDatePicker } from '@tmagic/design';
import { datetimeFormatter } from '@tmagic/utils';
import { DateTimeConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-datetime',
const props = defineProps<{
config: DateTimeConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<DateTimeConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change'],
setup(props, { emit }) {
useAddField(props.prop);
const value = props.model?.[props.name].toString();
@ -47,12 +44,7 @@ export default defineComponent({
}
}
return {
value,
changeHandler: (v: Date) => {
const changeHandler = (v: string) => {
emit('change', v);
},
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-date-picker
<TMagicDatePicker
v-model="value"
type="datetimerange"
range-separator="-"
@ -10,12 +10,13 @@
:disabled="disabled"
:default-time="config.defaultTime"
@change="changeHandler"
></el-date-picker>
></TMagicDatePicker>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { TMagicDatePicker } from '@tmagic/design';
import { datetimeFormatter } from '@tmagic/utils';
import { DaterangeConfig } from '../schema';

View File

@ -1,18 +1,18 @@
<template>
<div class="m-fields-dynamic-field">
<el-form size="small">
<el-form-item v-for="key in Object.keys(fieldMap.value)" :key="key" :label="fieldLabelMap.value[key]">
<el-input
<TMagicForm size="small">
<TMagicFormItem v-for="key in Object.keys(fieldMap.value)" :key="key" :label="fieldLabelMap.value[key]">
<TMagicInput
v-model="fieldMap.value[key]"
:placeholder="fieldLabelMap.value[key]"
@change="inputChangeHandler(key)"
></el-input>
</el-form-item>
</el-form>
></TMagicInput>
</TMagicFormItem>
</TMagicForm>
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
/**
* 动态表单目前只支持input类型字段
* inputType: 'dynamic-field',
@ -23,27 +23,27 @@
* 特别注意field的上一级必须extensible: true才能保存未声明的字段
*/
import { defineComponent, onBeforeUnmount, PropType, reactive, watch } from 'vue';
import { onBeforeUnmount, reactive, watch } from 'vue';
import { TMagicForm, TMagicFormItem, TMagicInput } from '@tmagic/design';
import { DynamicFieldConfig } from '../schema';
import { getConfig } from '../utils/config';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-dynamic-field',
const props = defineProps<{
config: DynamicFieldConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<DynamicFieldConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change'],
setup(props, { emit }) {
useAddField(props.prop);
const request = getConfig('request') as Function;
@ -89,21 +89,7 @@ export default defineComponent({
}
});
return {
request,
fieldMap,
fieldLabelMap,
unwatch,
changeFieldMap,
inputChangeHandler: (key: string) => {
const inputChangeHandler = (key: string) => {
emit('change', fieldMap.value[key], key);
},
};
},
methods: {
init() {},
},
});
</script>

View File

@ -2,8 +2,8 @@
<a v-if="config.href && !disabled" target="_blank" :href="href" :style="config.css || {}">{{ displayText }}</a>
<span v-else-if="config.href && disabled" :style="config.disabledCss || {}">{{ displayText }}</span>
<div v-else class="m-fields-link">
<el-button text type="primary" @click="editHandler">点击编辑</el-button>
<m-form-dialog
<TMagicButton :text="true" type="primary" @click="editHandler">点击编辑</TMagicButton>
<FormDialog
ref="editor"
:title="config.formTitle || '编辑扩展配置'"
:width="config.formWidth"
@ -12,32 +12,32 @@
:parentValues="values"
:fullscreen="config.fullscreen"
@submit="action"
></m-form-dialog>
></FormDialog>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType, ref } from 'vue';
<script lang="ts" setup>
import { computed, inject, ref } from 'vue';
import { TMagicButton } from '@tmagic/design';
import FormDialog from '../FormDialog.vue';
import { FormState, LinkConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-link',
const props = defineProps<{
config: LinkConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<LinkConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change'],
setup(props, { emit }) {
useAddField(props.prop);
const formValue = ref({});
@ -55,15 +55,7 @@ export default defineComponent({
formValue.value = props.model?.[props.name] || {};
};
return {
// ref
formValue,
editor,
// computed methods
href,
formConfig: computed(() => {
const formConfig = computed(() => {
if (typeof props.config.form === 'function') {
return props.config.form(mForm, {
model: props.model || {},
@ -71,16 +63,9 @@ export default defineComponent({
});
}
return props.config.form;
}),
});
disable: computed(() => {
if (typeof props.config.disabled !== 'undefined') {
return props.config.disabled;
}
return !href.value;
}),
displayText: computed(() => {
const displayText = computed(() => {
if (typeof props.config.displayText === 'function') {
return props.config.displayText(mForm, { model: props.model || {} });
}
@ -88,25 +73,19 @@ export default defineComponent({
return props.config.displayText;
}
return '跳转';
}),
});
// methods
init,
editHandler: () => {
const editHandler = () => {
init();
editor.value && (editor.value.dialogVisible = true);
},
};
action: (data: any) => {
const action = (data: any) => {
if (props.model) {
props.model[props.name] = data;
formValue.value = data;
emit('change', props.model[props.name]);
}
editor.value && (editor.value.dialogVisible = false);
},
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-input-number
<TMagicInputNumber
v-if="model"
v-model="model[name]"
clearable
@ -12,42 +12,40 @@
:disabled="disabled"
@change="changeHandler"
@input="inputHandler"
></el-input-number>
></TMagicInputNumber>
</template>
<script lang="ts">
import { defineComponent, inject, PropType } from 'vue';
<script lang="ts" setup>
import { inject } from 'vue';
import { TMagicInputNumber } from '@tmagic/design';
import { FormState, NumberConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-number',
props: {
...fieldProps,
config: {
type: Object as PropType<NumberConfig>,
required: true,
},
},
const props = defineProps<{
config: NumberConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
emits: ['change', 'input'],
const emit = defineEmits(['change', 'input']);
setup(props, { emit }) {
useAddField(props.prop);
const mForm = inject<FormState | null>('mForm');
return {
mForm,
changeHandler: (value: number) => {
const changeHandler = (value: number) => {
emit('change', value);
},
inputHandler: (v: string) => {
};
const inputHandler = (v: string) => {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
},
};
},
});
</script>

View File

@ -1,33 +1,33 @@
<template>
<el-radio-group v-if="model" v-model="model[name]" :size="size" :disabled="disabled" @change="changeHandler">
<el-radio v-for="option in config.options" :label="option.value" :key="option.value">{{ option.text }}</el-radio>
</el-radio-group>
<TMagicRadioGroup v-if="model" v-model="model[name]" :size="size" :disabled="disabled" @change="changeHandler">
<TMagicRadio v-for="option in config.options" :label="option.value" :key="`${option.value}`">{{
option.text
}}</TMagicRadio>
</TMagicRadioGroup>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
<script lang="ts" setup>
import { TMagicRadio, TMagicRadioGroup } from '@tmagic/design';
import fieldProps from '../utils/fieldProps';
import { RadioGroupConfig } from '../schema';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-radio-group',
const props = defineProps<{
config: RadioGroupConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object,
required: true,
},
},
const emit = defineEmits(['change']);
emits: ['change'],
setup(props, { emit }) {
useAddField(props.prop);
return {
changeHandler: (v: string | number | boolean) => emit('change', v),
const changeHandler = (value: number) => {
emit('change', value);
};
},
});
useAddField(props.prop);
</script>

View File

@ -1,10 +1,10 @@
<template>
<el-select
<TMagicSelect
v-if="model"
v-model="model[name]"
v-loading="loading"
class="m-select"
ref="select"
ref="tMagicSelect"
clearable
filterable
:popper-class="`m-select-popper ${popperClass}`"
@ -19,43 +19,41 @@
@change="changeHandler"
@visible-change="visibleHandler"
>
<template v-if="config.group"><select-option-groups :options="groupOptions"></select-option-groups></template>
<template v-else><select-options :options="options"></select-options></template>
<template v-if="config.group"><SelectOptionGroups :options="groupOptions"></SelectOptionGroups></template>
<template v-else><SelectOptions :options="itemOptions"></SelectOptions></template>
<div v-loading="true" v-if="moreLoadingVisible"></div>
</el-select>
</TMagicSelect>
</template>
<script lang="ts">
import { defineComponent, inject, onBeforeMount, onMounted, PropType, Ref, ref, watchEffect } from 'vue';
<script lang="ts" setup>
import { inject, nextTick, onBeforeMount, onMounted, Ref, ref, watchEffect } from 'vue';
import { TMagicSelect } from '@tmagic/design';
import { FormState, SelectConfig, SelectGroupOption, SelectOption } from '../schema';
import { getConfig } from '../utils/config';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
import SelectOptionGroups from './SelectOptionGroups.vue';
import SelectOptions from './SelectOptions.vue';
export default defineComponent({
name: 'm-fields-select',
const props = defineProps<{
config: SelectConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
components: { SelectOptions, SelectOptionGroups },
const emit = defineEmits(['change']);
props: {
...fieldProps,
config: {
type: Object as PropType<SelectConfig>,
required: true,
},
},
emits: ['change'],
setup(props, { emit }) {
if (!props.model) throw new Error('不能没有model');
useAddField(props.prop);
const select = ref<any>();
const tMagicSelect = ref<InstanceType<typeof TMagicSelect>>();
const mForm = inject<FormState | undefined>('mForm');
const options = ref<SelectOption[] | SelectGroupOption[]>([]);
const localOptions = ref<SelectOption[] | SelectGroupOption[]>([]);
@ -206,9 +204,7 @@ export default defineComponent({
}
if (config.multiple && value.findIndex) {
return (localOptions.value as any[]).filter(
(item) => value.findIndex((v: any) => equalValue(item.value, v)) > -1,
);
return (localOptions.value as any[]).filter((item) => value.findIndex((v: any) => equalValue(item.value, v)) > -1);
}
return (localOptions.value as any[]).filter((item) => equalValue(item.value, value));
};
@ -295,8 +291,9 @@ export default defineComponent({
}
props.config.remote &&
onMounted(() => {
select.value?.scrollbar.wrap$.addEventListener('scroll', async (e: Event) => {
onMounted(async () => {
await nextTick();
tMagicSelect.value?.scrollbarWrap?.addEventListener('scroll', async (e: Event) => {
const el = e.currentTarget as HTMLDivElement;
if (moreLoadingVisible.value) {
return;
@ -314,39 +311,26 @@ export default defineComponent({
});
});
return {
select,
loading,
remote,
options: options as Ref<SelectOption[]>,
groupOptions: options as Ref<SelectGroupOption[]>,
moreLoadingVisible,
popperClass: mForm?.popperClass,
const popperClass = mForm?.popperClass;
getOptions,
getRequestFuc() {
return getConfig('request');
},
changeHandler(value: any) {
const changeHandler = (value: any) => {
emit('change', value);
},
};
async visibleHandler(visible: boolean) {
const visibleHandler = async (visible: boolean) => {
if (!visible) return;
if (!props.config.remote) return;
if (query.value && select.value) {
select.value.query = query.value;
select.value.previousQuery = query.value;
select.value.selectedLabel = query.value;
if (query.value && tMagicSelect.value) {
tMagicSelect.value.setQuery(query.value);
tMagicSelect.value.setPreviousQuery(query.value);
tMagicSelect.value.setSelectedLabel(query.value);
} else if (options.value.length <= (props.config.multiple ? props.model?.[props.name].length : 1)) {
options.value = await getOptions();
}
},
};
async remoteMethod(q: string) {
const remoteMethod = async (q: string) => {
if (!localOptions.value.length) {
query.value = q;
pgIndex.value = 0;
@ -354,11 +338,11 @@ export default defineComponent({
// el-select
if (props.config.multiple)
setTimeout(() => {
select.value?.setSelected();
tMagicSelect.value?.setSelected();
}, 0);
}
},
};
},
});
const itemOptions = options as Ref<SelectOption[]>;
const groupOptions = options as Ref<SelectGroupOption[]>;
</script>

View File

@ -1,17 +1,19 @@
<template>
<el-option-group v-for="(group, index) in options" :key="index" :label="group.label" :disabled="group.disabled">
<el-option
<TMagicOptionGroup v-for="(group, index) in options" :key="index" :label="group.label" :disabled="group.disabled">
<TMagicOption
v-for="(item, index) in group.options"
:key="index"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
>
</el-option>
</el-option-group>
</TMagicOption>
</TMagicOptionGroup>
</template>
<script lang="ts" setup>
import { TMagicOption, TMagicOptionGroup } from '@tmagic/design';
import { SelectGroupOption } from '../schema';
defineProps<{

View File

@ -1,14 +1,16 @@
<template>
<el-option
<TMagicOption
v-for="option in options"
:label="option.text"
:value="option.value"
:key="valueKey ? option.value[valueKey] : option.value"
:disabled="option.disabled"
></el-option>
></TMagicOption>
</template>
<script lang="ts" setup>
import { TMagicOption } from '@tmagic/design';
import { SelectOption } from '../schema';
defineProps<{

View File

@ -1,40 +1,42 @@
<template>
<el-switch
v-if="model"
v-model="model[n]"
<TMagicSwitch
v-model="model[name]"
:size="size"
:activeValue="activeValue"
:inactiveValue="inactiveValue"
:disabled="disabled"
@change="changeHandler"
></el-switch>
></TMagicSwitch>
</template>
<script lang="ts">
import { computed, defineComponent, PropType } from 'vue';
<script lang="ts" setup>
import { computed } from 'vue';
import { TMagicSwitch } from '@tmagic/design';
import { SwitchConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-switch',
props: {
...fieldProps,
config: {
type: Object as PropType<SwitchConfig>,
required: true,
},
},
const props = defineProps<{
config: SwitchConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
emits: ['change'],
const emit = defineEmits(['change']);
setup(props, { emit }) {
useAddField(props.prop);
return {
n: computed(() => props.name || props.config.name || ''),
activeValue: computed(() => {
const changeHandler = (value: number) => {
emit('change', value);
};
const activeValue = computed(() => {
if (typeof props.config.activeValue === 'undefined') {
if (props.config.filter === 'number') {
return 1;
@ -44,8 +46,9 @@ export default defineComponent({
}
return true;
}),
inactiveValue: computed(() => {
});
const inactiveValue = computed(() => {
if (typeof props.config.inactiveValue === 'undefined') {
if (props.config.filter === 'number') {
return 0;
@ -55,11 +58,5 @@ export default defineComponent({
}
return false;
}),
changeHandler: (v: boolean | number | string) => {
emit('change', v);
},
};
},
});
</script>

View File

@ -1,6 +1,6 @@
<template>
<el-input
v-model="model[modelName]"
<TMagicInput
v-model="model[name]"
clearable
:size="size"
:placeholder="config.placeholder"
@ -20,50 +20,45 @@
{{ config.append.text }}
</el-button>
</template>
</el-input>
</TMagicInput>
</template>
<script lang="ts">
import { computed, defineComponent, inject, PropType } from 'vue';
<script lang="ts" setup>
import { inject } from 'vue';
import { TMagicInput } from '@tmagic/design';
import { isNumber } from '@tmagic/utils';
import { FormState, TextConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-text',
const props = defineProps<{
config: TextConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<TextConfig>,
required: true,
},
},
emits: ['change', 'input'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const emit = defineEmits(['change', 'input']);
useAddField(props.prop);
const modelName = computed(() => props.name || props.config.name || '');
return {
modelName,
const mForm = inject<FormState | undefined>('mForm');
changeHandler(v: string | number) {
emit('change', v);
},
const changeHandler = (value: number) => {
emit('change', value);
};
inputHandler(v: string | number) {
const inputHandler = (v: string) => {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
},
};
buttonClickHandler() {
const buttonClickHandler = () => {
if (typeof props.config.append === 'string') return;
if (props.config.append?.handler) {
@ -72,11 +67,11 @@ export default defineComponent({
values: mForm?.values,
});
}
},
};
keyUpHandler($event: KeyboardEvent) {
const keyUpHandler = ($event: KeyboardEvent) => {
if (!props.model) return;
if (!modelName.value) return;
if (!props.name) return;
const arrowUp = $event.key === 'ArrowUp';
const arrowDown = $event.key === 'ArrowDown';
@ -85,7 +80,7 @@ export default defineComponent({
return;
}
const value = props.model[modelName.value];
const value = props.model[props.name];
let num;
let unit;
if (isNumber(value)) {
@ -127,10 +122,7 @@ export default defineComponent({
}
}
props.model[modelName.value] = `${num}${unit || ''}`;
emit('change', props.model[modelName.value]);
},
props.model[props.name] = `${num}${unit || ''}`;
emit('change', props.model[props.name]);
};
},
});
</script>

View File

@ -1,5 +1,5 @@
<template>
<el-input
<TMagicInput
v-model="model[name]"
type="textarea"
:size="size"
@ -9,52 +9,40 @@
@change="changeHandler"
@input="inputHandler"
>
</el-input>
</TMagicInput>
</template>
<script lang="ts">
import { defineComponent, inject, PropType } from 'vue';
<script lang="ts" setup>
import { inject } from 'vue';
import { TMagicInput } from '@tmagic/design';
import { FormState, TextareaConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-textarea',
props: {
...fieldProps,
config: {
type: Object as PropType<TextareaConfig>,
required: true,
},
},
const props = defineProps<{
config: TextareaConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
emits: {
change(values: string | number) {
return values;
},
const emit = defineEmits(['change', 'input']);
input(values: string | number) {
return values;
},
},
setup(props, { emit }) {
useAddField(props.prop);
const mForm = inject<FormState | null>('mForm');
return {
mForm,
changeHandler: (v: string) => {
emit('change', v);
},
const changeHandler = (value: number) => {
emit('change', value);
};
inputHandler: (v: string) => {
const inputHandler = (v: string) => {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
},
};
},
});
</script>

View File

@ -1,46 +1,35 @@
<template>
<el-time-picker
<TMagicTimePicker
v-model="model[name]"
:size="size"
value-format="HH:mm:ss"
:placeholder="config.placeholder"
:disabled="disabled"
@change="changeHandler"
></el-time-picker>
></TMagicTimePicker>
</template>
<script lang="ts">
import { defineComponent, PropType } from 'vue';
<script lang="ts" setup>
import { TMagicTimePicker } from '@tmagic/design';
import { TimeConfig } from '../schema';
import fieldProps from '../utils/fieldProps';
import { useAddField } from '../utils/useAddField';
export default defineComponent({
name: 'm-fields-time',
const props = defineProps<{
config: TimeConfig;
model: any;
initValues?: any;
values?: any;
name: string;
prop: string;
disabled?: boolean;
size: 'mini' | 'small' | 'medium';
}>();
props: {
...fieldProps,
config: {
type: Object as PropType<TimeConfig>,
required: true,
},
},
const emit = defineEmits(['change']);
emits: {
change(values: Date) {
return values;
},
},
setup(props, { emit }) {
useAddField(props.prop);
return {
changeHandler: (v: Date) => {
const changeHandler = (v: string) => {
emit('change', v);
},
};
},
});
</script>

View File

@ -18,6 +18,8 @@
import { App } from 'vue';
import TMagicDesign from '@tmagic/design';
import Container from './containers/Container.vue';
import Fieldset from './containers/Fieldset.vue';
import GroupList from './containers/GroupList.vue';
@ -53,7 +55,6 @@ import './theme/index.scss';
export * from './schema';
export * from './utils/form';
export * from './utils/useAddField';
export { default as fieldProps } from './utils/fieldProps';
export { default as MForm } from './Form.vue';
export { default as MFormDialog } from './FormDialog.vue';
@ -86,40 +87,42 @@ export { default as MDynamicField } from './fields/DynamicField.vue';
const defaultInstallOpt = {};
const install = (app: App, opt: any) => {
app.use(TMagicDesign, opt.uiAdapter);
const option = Object.assign(defaultInstallOpt, opt);
// eslint-disable-next-line no-param-reassign
app.config.globalProperties.$MAGIC_FORM = option;
setConfig(option);
app.component(Form.name, Form);
app.component(FormDialog.name, FormDialog);
app.component(Container.name, Container);
app.component('m-form', Form);
app.component('m-form-dialog', FormDialog);
app.component('m-form-container', Container);
app.component('m-form-fieldset', Fieldset);
app.component(GroupList.name, GroupList);
app.component(Panel.name, Panel);
app.component(Row.name, Row);
app.component(MStep.name, MStep);
app.component('m-form-group-list', GroupList);
app.component('m-form-panel', Panel);
app.component('m-form-row', Row);
app.component('m-form-step', MStep);
app.component('m-form-table', Table);
app.component(Tabs.name, Tabs);
app.component(Text.name, Text);
app.component(Number.name, Number);
app.component(Textarea.name, Textarea);
app.component('m-form-tab', Tabs);
app.component('m-fields-text', Text);
app.component('m-fields-number', Number);
app.component('m-fields-textarea', Textarea);
app.component('m-fields-hidden', Hidden);
app.component(Date.name, Date);
app.component(DateTime.name, DateTime);
app.component(Time.name, Time);
app.component(Checkbox.name, Checkbox);
app.component(Switch.name, Switch);
app.component('m-fields-date', Date);
app.component('m-fields-datetime', DateTime);
app.component('m-fields-daterange', Daterange);
app.component('m-fields-time', Time);
app.component('m-fields-checkbox', Checkbox);
app.component('m-fields-switch', Switch);
app.component('m-fields-color-picker', ColorPicker);
app.component(CheckboxGroup.name, CheckboxGroup);
app.component(RadioGroup.name, RadioGroup);
app.component('m-fields-checkbox-group', CheckboxGroup);
app.component('m-fields-radio-group', RadioGroup);
app.component('m-fields-display', Display);
app.component(Link.name, Link);
app.component(Select.name, Select);
app.component(Cascader.name, Cascader);
app.component(DynamicField.name, DynamicField);
app.component('m-fields-link', Link);
app.component('m-fields-select', Select);
app.component('m-fields-cascader', Cascader);
app.component('m-fields-dynamic-field', DynamicField);
};
export default {

View File

@ -16,6 +16,11 @@
* limitations under the License.
*/
export interface ValidateError {
message: string;
field: string;
}
/**
*
*/
@ -167,7 +172,7 @@ type DefaultValueFunction = (mForm: FormState | undefined) => any;
/**
*
*/
export interface SelectOption {
export interface SelectConfigOption {
/** 选项的标签 */
text: string | SelectOptionTextFunction;
/** 选项的值 */
@ -176,10 +181,19 @@ export interface SelectOption {
disabled?: boolean;
}
export interface SelectOption {
/** 选项的标签 */
text: string;
/** 选项的值 */
value: any;
/** 是否禁用该选项 */
disabled?: boolean;
}
/**
*
*/
export interface SelectGroupOption {
export interface SelectConfigGroupOption {
/** 分组的组名 */
label: string;
/** 是否禁用该选项组 */
@ -194,6 +208,21 @@ export interface SelectGroupOption {
}[];
}
export interface SelectGroupOption {
/** 分组的组名 */
label: string;
/** 是否禁用该选项组 */
disabled: boolean;
options: {
/** 选项的标签 */
label: string;
/** 选项的值 */
value: any;
/** 是否禁用该选项 */
disabled?: boolean;
}[];
}
type SelectOptionFunction = (
mForm: FormState | undefined,
data: {
@ -226,11 +255,11 @@ type RemoteSelectOptionRequestFunction = (
},
) => any;
type RemoteSelectOptionItemFunction = (optionsData: Record<string, any>) => SelectOption[];
type RemoteSelectOptionItemFunction = (optionsData: Record<string, any>) => SelectOption[] | SelectGroupOption[];
type SelectOptionValueFunction = (item: Record<string, any>) => any;
type SelectOptionTextFunction = (item: Record<string, any>) => string;
interface CascaderOption {
export interface CascaderOption {
/** 指定选项的值为选项对象的某个属性值 */
value: any;
/** 指定选项标签为选项对象的某个属性值 */
@ -302,7 +331,7 @@ export interface NumberConfig extends FormItem {
min?: number;
max?: number;
step?: number;
placeholder?: number;
placeholder?: string;
}
/**
@ -361,7 +390,7 @@ export interface SwitchConfig extends FormItem {
export interface RadioGroupConfig extends FormItem {
type: 'radio-group';
options: {
values: string | number | boolean;
value: string | number | boolean;
text: string;
}[];
}
@ -381,6 +410,7 @@ export interface CheckboxGroupConfig extends FormItem {
options: {
value: any;
text: string;
disabled?: boolean;
}[];
}
@ -394,7 +424,7 @@ export interface SelectConfig extends FormItem, Input {
valueKey?: string;
allowCreate?: boolean;
group?: boolean;
options: SelectOption[] | SelectGroupOption[] | SelectOptionFunction;
options: SelectConfigOption[] | SelectConfigGroupOption[] | SelectOptionFunction;
remote: true;
option: {
url: string;
@ -520,7 +550,7 @@ export interface TabPaneConfig {
}
export interface TabConfig extends FormItem, ContainerCommonConfig {
type: 'tab' | 'dynamic-tab';
tabType?: 'tab';
tabType?: string;
editable?: boolean;
dynamic?: boolean;
tabPosition?: 'top' | 'right' | 'bottom' | 'left';

View File

@ -1,39 +0,0 @@
/*
* Tencent is pleased to support the open source community by making TMagicEditor available.
*
* Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { PropType } from 'vue';
export default {
model: {
type: Object,
required: true,
default: () => ({}),
},
name: {
type: String,
default: '',
},
disabled: {
type: Boolean,
default: false,
},
size: String as PropType<'mini' | 'small' | 'medium'>,
prop: String,
initValues: Object,
values: Object,
};

View File

@ -30,7 +30,10 @@ export default defineConfig({
alias:
process.env.NODE_ENV === 'production'
? []
: [{ find: /^@tmagic\/utils/, replacement: path.join(__dirname, '../utils/src/index.ts') }],
: [
{ find: /^@tmagic\/utils/, replacement: path.join(__dirname, '../utils/src/index.ts') },
{ find: /^@tmagic\/design/, replacement: path.join(__dirname, '../design/src/index.ts') },
],
},
build: {