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,175 +19,132 @@
: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: () => ({}),
},
parentValues: {
type: Object,
default: () => ({}),
},
//
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,
},
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',
},
);
emits: ['change', 'field-input', 'field-change'],
const emit = defineEmits(['change', 'field-input', 'field-change']);
setup(props, { emit }) {
// InstanceType<typeof ElForm>types
const elForm = ref<any>();
const initialized = ref(false);
const values = ref<FormValue>({});
const fields = new Map<string, any>();
const tMagicForm = ref<InstanceType<typeof TMagicForm>>();
const initialized = ref(false);
const values = ref<FormValue>({});
const fields = new Map<string, any>();
const requestFuc = getConfig('request') as Function;
const requestFuc = getConfig('request') as Function;
const formState: FormState = reactive<FormState>({
keyProp: props.keyProp,
popperClass: props.popperClass,
config: props.config,
const formState: FormState = reactive<FormState>({
keyProp: props.keyProp,
popperClass: props.popperClass,
config: props.config,
initValues: props.initValues,
parentValues: props.parentValues,
values,
$emit: emit as (event: string, ...args: any[]) => void,
fields,
setField: (prop: string, field: any) => fields.set(prop, field),
getField: (prop: string) => fields.get(prop),
deleteField: (prop: string) => fields.delete(prop),
post: (options: any) => {
if (requestFuc) {
return requestFuc({
...options,
method: 'POST',
});
}
},
});
provide('mForm', formState);
watch(
[() => props.config, () => props.initValues],
([config], [preConfig]) => {
if (!isEqual(toRaw(config), toRaw(preConfig))) {
initialized.value = false;
}
initValue(formState, {
initValues: props.initValues,
parentValues: props.parentValues,
values,
$emit: emit as (event: string, ...args: any[]) => void,
fields,
setField: (prop: string, field: any) => fields.set(prop, field),
getField: (prop: string) => fields.get(prop),
deleteField: (prop: string) => fields.delete(prop),
post: (options: any) => {
if (requestFuc) {
return requestFuc({
...options,
method: 'POST',
});
}
},
config: props.config,
}).then((value) => {
values.value = value;
initialized.value = true;
});
},
{ immediate: true },
);
provide('mForm', formState);
const changeHandler = () => {
emit('change', values.value);
};
watch(
[() => props.config, () => props.initValues],
([config], [preConfig]) => {
if (!isEqual(toRaw(config), toRaw(preConfig))) {
initialized.value = false;
}
defineExpose({
values,
formState,
initialized,
initValue(formState, {
initValues: props.initValues,
config: props.config,
}).then((value) => {
values.value = value;
initialized.value = true;
changeHandler,
resetForm: () => tMagicForm.value?.resetFields(),
submitForm: async (native?: boolean): Promise<any> => {
try {
await tMagicForm.value?.validate();
return native ? values.value : cloneDeep(toRaw(values.value));
} catch (invalidFields: any) {
const error: string[] = [];
Object.entries(invalidFields).forEach(([, ValidateError]) => {
(ValidateError as ValidateError[]).forEach(({ field, message }) => {
if (field && message) error.push(`${field} -> ${message}`);
if (field && !message) error.push(`${field} -> 出现错误`);
if (!field && message) error.push(`${message}`);
});
},
{ immediate: true },
);
return {
initialized,
values,
elForm,
formState,
changeHandler: () => {
emit('change', values.value);
},
resetForm: () => elForm.value?.resetFields(),
submitForm: async (native?: boolean): Promise<any> => {
try {
await elForm.value?.validate();
return native ? values.value : cloneDeep(toRaw(values.value));
} catch (invalidFields: any) {
const error: string[] = [];
Object.entries(invalidFields).forEach(([, ValidateError]) => {
(ValidateError as ValidateError[]).forEach(({ field, message }) => {
if (field && message) error.push(`${field} -> ${message}`);
if (field && !message) error.push(`${field} -> 出现错误`);
if (!field && message) error.push(`${message}`);
});
});
throw new Error(error.join('<br>'));
}
},
};
});
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,150 +24,128 @@
: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: () => ({}),
},
parentValues: {
type: Object,
},
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: '确定',
},
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: '确定',
},
);
emits: ['close', 'submit', 'error', 'change'],
const emit = defineEmits(['close', 'submit', 'error', 'change']);
setup(props, { emit }) {
const form = ref<InstanceType<typeof Form>>();
const dialogVisible = ref(false);
const saveFetch = ref(false);
const stepActive = ref(1);
const bodyHeight = ref(`${document.body.clientHeight - 194}px`);
const form = ref<InstanceType<typeof Form>>();
const dialogVisible = ref(false);
const saveFetch = ref(false);
const stepActive = ref(1);
const bodyHeight = ref(`${document.body.clientHeight - 194}px`);
const stepCount = computed(() => {
const { length } = props.config;
for (let index = 0; index < length; index++) {
if (props.config[index].type === 'step') {
return (props.config[index] as StepConfig).items.length;
}
}
return 0;
});
const stepCount = computed(() => {
const { length } = props.config;
for (let index = 0; index < length; index++) {
if (props.config[index].type === 'step') {
return (props.config[index] as StepConfig).items.length;
}
}
return 0;
});
const hasStep = computed(() => {
const { length } = props.config;
for (let index = 0; index < length; index++) {
if (props.config[index].type === 'step') {
return true;
}
}
const hasStep = computed(() => {
const { length } = props.config;
for (let index = 0; index < length; index++) {
if (props.config[index].type === 'step') {
return true;
}
}
return false;
});
return false;
});
const cancel = () => {
dialogVisible.value = false;
};
const cancel = () => {
dialogVisible.value = false;
};
const closeHandler = () => {
stepActive.value = 1;
emit('close');
};
const closeHandler = () => {
stepActive.value = 1;
emit('close');
};
const save = async () => {
try {
const values = await form.value?.submitForm();
emit('submit', values);
} catch (e) {
emit('error', e);
}
};
const save = async () => {
try {
const values = await form.value?.submitForm();
emit('submit', values);
} catch (e) {
emit('error', e);
}
};
const preStep = () => {
stepActive.value -= 1;
};
const preStep = () => {
stepActive.value -= 1;
};
const nextStep = () => {
stepActive.value += 1;
};
const nextStep = () => {
stepActive.value += 1;
};
const changeHandler = (value: any) => {
emit('change', value);
};
const changeHandler = (value: any) => {
emit('change', value);
};
return {
form,
saveFetch,
stepActive,
dialogVisible,
bodyHeight,
stepCount,
hasStep,
cancel,
closeHandler,
save,
preStep,
nextStep,
changeHandler,
};
},
defineExpose({
form,
saveFetch,
dialogVisible,
cancel,
save,
});
</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 changeHandler = () => emit('change', props.model);
return {
mForm,
display: computed(() => displayFunction(mForm, props.config.display, props)),
changeHandler,
};
},
});
const mForm = inject<FormState | undefined>('mForm');
const display = computed(() => displayFunction(mForm, props.config.display, props));
const changeHandler = () => emit('change', props.model);
</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,202 +92,166 @@
: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,
},
config: {
type: Object as PropType<ChildConfig>,
required: true,
},
prop: {
type: String,
default: () => '',
},
stepActive: {
type: [String, Number],
},
size: {
type: String,
default: 'small',
},
const props = withDefaults(
defineProps<{
model: FormValue;
config: ChildConfig;
prop?: string;
labelWidth?: string;
expandMore?: boolean;
stepActive?: string | number;
size?: string;
}>(),
{
prop: '',
size: 'small',
expandMore: false,
},
);
emits: ['change'],
const emit = defineEmits(['change']);
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(false);
const expand = ref(false);
const name = computed(() => props.config.name || '');
const name = computed(() => props.config.name || '');
const items = computed(() => (props.config as ContainerCommonConfig).items);
const items = computed(() => (props.config as ContainerCommonConfig).items);
const itemProp = computed(() => {
let n: string | number = '';
const { names } = props.config as any;
if (names?.[0]) {
[n] = names;
} else if (name.value) {
n = name.value;
} else {
return props.prop;
}
return `${props.prop}${props.prop ? '.' : ''}${n}`;
});
const tagName = computed(() => {
const component = resolveComponent(`m-${items.value ? 'form' : 'fields'}-${type.value}`);
if (typeof component !== 'string') return component;
return 'm-fields-text';
});
const disabled = computed(() => filterFunction(mForm, props.config.disabled, 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 type = computed((): string => {
let { type } = props.config;
if (typeof type === 'function') {
type = type(mForm, {
model: props.model,
});
}
if (type === 'form') return '';
return type?.replace(/([A-Z])/g, '-$1').toLowerCase() || (items.value ? '' : 'text');
});
const display = computed((): boolean => {
if (props.config.display === 'expand') {
return expand.value;
}
return displayFunction(mForm, props.config.display, props);
});
const itemLabelWidth = computed(() => props.config.labelWidth || props.labelWidth);
watchEffect(() => {
expand.value = props.expandMore;
});
const expandHandler = () => (expand.value = !expand.value);
const key = (config: any) => config[mForm?.keyProps];
const filterHandler = (filter: any, value: FormValue | number | string) => {
if (typeof filter === 'function') {
return filter(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
config: props.config,
});
}
if (filter === 'number') {
return +value;
}
return value;
};
const changeHandler = (onChange: any, value: FormValue | number | string) => {
if (typeof onChange === 'function') {
return onChange(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
config: props.config,
});
}
};
const trimHandler = (trim: any, value: FormValue | number | string) => {
if (typeof value === 'string' && trim) {
return value.replace(/^\s*/, '').replace(/\s*$/, '');
}
};
const onChangeHandler = async function (v: FormValue, key?: string) {
const { filter, onChange, trim, name, dynamicKey } = props.config as any;
let value: FormValue | number | string = v;
try {
value = filterHandler(filter, v);
value = (await changeHandler(onChange, value)) ?? value;
value = trimHandler(trim, value) ?? value;
} catch (e) {
console.error(e);
}
// fieldfield-linkmodel===value,
if ((name || name === 0) && props.model !== value && (v !== value || props.model[name] !== value)) {
// eslint-disable-next-line vue/no-mutating-props
props.model[name] = value;
}
// valuekeymodel
if (key !== undefined && dynamicKey) {
// eslint-disable-next-line vue/no-mutating-props
props.model[key] = value;
}
emit('change', props.model);
};
return {
expand,
name,
type,
disabled,
itemProp,
items,
display,
itemLabelWidth,
tagName,
rule,
tooltip,
extra,
key,
onChangeHandler,
expandHandler,
};
},
const itemProp = computed(() => {
let n: string | number = '';
const { names } = props.config as any;
if (names?.[0]) {
[n] = names;
} else if (name.value) {
n = name.value;
} else {
return props.prop;
}
return `${props.prop}${props.prop ? '.' : ''}${n}`;
});
const tagName = computed(() => {
const component = resolveComponent(`m-${items.value ? 'form' : 'fields'}-${type.value}`);
if (typeof component !== 'string') return component;
return 'm-fields-text';
});
const disabled = computed(() => filterFunction(mForm, props.config.disabled, 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 type = computed((): string => {
let { type } = props.config;
if (typeof type === 'function') {
type = type(mForm, {
model: props.model,
});
}
if (type === 'form') return '';
return type?.replace(/([A-Z])/g, '-$1').toLowerCase() || (items.value ? '' : 'text');
});
const display = computed((): boolean => {
if (props.config.display === 'expand') {
return expand.value;
}
return displayFunction(mForm, props.config.display, props);
});
const itemLabelWidth = computed(() => props.config.labelWidth || props.labelWidth);
watchEffect(() => {
expand.value = props.expandMore;
});
const expandHandler = () => (expand.value = !expand.value);
const key = (config: any) => config[mForm?.keyProps];
const filterHandler = (filter: any, value: FormValue | number | string) => {
if (typeof filter === 'function') {
return filter(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
config: props.config,
});
}
if (filter === 'number') {
return +value;
}
return value;
};
const changeHandler = (onChange: any, value: FormValue | number | string) => {
if (typeof onChange === 'function') {
return onChange(mForm, value, {
model: props.model,
values: mForm?.initValues,
formValue: mForm?.values,
prop: itemProp.value,
config: props.config,
});
}
};
const trimHandler = (trim: any, value: FormValue | number | string) => {
if (typeof value === 'string' && trim) {
return value.replace(/^\s*/, '').replace(/\s*$/, '');
}
};
const onChangeHandler = async function (v: FormValue, key?: string) {
const { filter, onChange, trim, name, dynamicKey } = props.config as any;
let value: FormValue | number | string = v;
try {
value = filterHandler(filter, v);
value = (await changeHandler(onChange, value)) ?? value;
value = trimHandler(trim, value) ?? value;
} catch (e) {
console.error(e);
}
// fieldfield-linkmodel===value,
if ((name || name === 0) && props.model !== value && (v !== value || props.model[name] !== value)) {
// eslint-disable-next-line vue/no-mutating-props
props.model[name] = value;
}
// valuekeymodel
if (key !== undefined && dynamicKey) {
// eslint-disable-next-line vue/no-mutating-props
props.model[key] = value;
}
emit('change', props.model);
};
</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,138 +19,106 @@
@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,
const mForm = inject<FormState | undefined>('mForm');
model: {
type: Object,
default: () => ({}),
},
const addable = computed(() => {
if (!props.name) return false;
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(() => {
if (!props.name) return false;
if (typeof props.config.addable === 'function') {
return props.config.addable(mForm, {
model: props.model[props.name],
formValue: mForm?.values,
prop: props.prop,
config: props.config,
});
}
return typeof props.config.addable === 'undefined' ? true : props.config.addable;
if (typeof props.config.addable === 'function') {
return props.config.addable(mForm, {
model: props.model[props.name],
formValue: mForm?.values,
prop: props.prop,
config: props.config,
});
}
const changeHandler = () => {
if (!props.name) return false;
emit('change', props.model[props.name]);
};
const addHandler = async () => {
if (!props.name) return false;
let initValues = {};
if (typeof props.config.defaultAdd === 'function') {
initValues = await props.config.defaultAdd(mForm, {
model: props.model[props.name],
formValue: mForm?.values,
prop: props.prop,
config: props.config,
});
} else if (props.config.defaultAdd) {
initValues = props.config.defaultAdd;
}
const groupValue = await initValue(mForm, {
config: props.config.items,
initValues,
});
props.model[props.name].push(groupValue);
};
const removeHandler = (index: number) => {
if (!props.name) return false;
props.model[props.name].splice(index, 1);
changeHandler();
};
const swapHandler = (idx1: number, idx2: number) => {
if (!props.name) return false;
const [currRow] = props.model[props.name].splice(idx1, 1);
props.model[props.name].splice(idx2, 0, currRow);
changeHandler();
};
const toggleMode = () => {
props.config.type = 'table';
props.config.groupItems = props.config.items;
props.config.items = (props.config.tableItems ||
props.config.items.map((item: any) => ({
...item,
label: item.label || item.text,
text: null,
}))) as any;
};
return {
Grid,
addable,
toggleMode,
removeHandler,
swapHandler,
changeHandler,
addHandler,
};
},
return typeof props.config.addable === 'undefined' ? true : props.config.addable;
});
const changeHandler = () => {
if (!props.name) return false;
emit('change', props.model[props.name]);
};
const addHandler = async () => {
if (!props.name) return false;
let initValues = {};
if (typeof props.config.defaultAdd === 'function') {
initValues = await props.config.defaultAdd(mForm, {
model: props.model[props.name],
formValue: mForm?.values,
prop: props.prop,
config: props.config,
});
} else if (props.config.defaultAdd) {
initValues = props.config.defaultAdd;
}
const groupValue = await initValue(mForm, {
config: props.config.items,
initValues,
});
props.model[props.name].push(groupValue);
};
const removeHandler = (index: number) => {
if (!props.name) return false;
props.model[props.name].splice(index, 1);
changeHandler();
};
const swapHandler = (idx1: number, idx2: number) => {
if (!props.name) return false;
const [currRow] = props.model[props.name].splice(idx1, 1);
props.model[props.name].splice(idx2, 0, currRow);
changeHandler();
};
const toggleMode = () => {
props.config.type = 'table';
props.config.groupItems = props.config.items;
props.config.items = (props.config.tableItems ||
props.config.items.map((item: any) => ({
...item,
label: item.label || item.text,
text: null,
}))) as any;
};
</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,125 +35,88 @@
: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: () => ({}),
},
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(false);
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);
watchEffect(() => {
expand.value = !props.index;
});
const rowConfig = computed(() => ({
type: 'row',
span: props.config.span || 24,
items: props.config.items,
labelWidth: props.config.labelWidth,
[mForm?.keyProp || '__key']: `${props.config[mForm?.keyProp || '__key']}${String(props.index)}`,
}));
const title = computed(() => {
if (props.config.titleKey && props.model[props.config.titleKey]) {
return props.model[props.config.titleKey];
}
return `${String(props.index)}`;
});
const length = computed(() => props.groupModel?.length || 0);
const itemExtra = computed(() => filterFunction(mForm, props.config.itemExtra, props));
const removeHandler = () => emit('remove-item', props.index);
const changeHandler = () => emit('change');
const expandHandler = () => {
expand.value = !expand.value;
};
//
const showDelete = (index: number) => {
const deleteFunc = props.config.delete;
if (deleteFunc && typeof deleteFunc === 'function') {
return deleteFunc(props.model, index, mForm?.values);
}
return true;
};
//
const changeOrder = (offset = 0) => emit('swap-item', props.index, `${String(props.index)}${offset}`);
const movable = () => {
const { movable } = props.config;
//
if (movable === undefined) return true;
if (typeof movable === 'function') {
return movable(mForm, props.index || 0, props.model, props.groupModel);
}
return movable;
};
return {
expand,
expandHandler,
title,
showDelete,
removeHandler,
movable,
changeOrder,
itemExtra,
rowConfig,
changeHandler,
length,
Delete,
};
},
watchEffect(() => {
expand.value = !props.index;
});
const rowConfig = computed(() => ({
type: 'row',
span: props.config.span || 24,
items: props.config.items,
labelWidth: props.config.labelWidth,
[mForm?.keyProp || '__key']: `${props.config[mForm?.keyProp || '__key']}${String(props.index)}`,
}));
const title = computed(() => {
if (props.config.titleKey && props.model[props.config.titleKey]) {
return props.model[props.config.titleKey];
}
return `${String(props.index)}`;
});
const length = computed(() => props.groupModel?.length || 0);
const itemExtra = computed(() => filterFunction(mForm, props.config.itemExtra, props));
const removeHandler = () => emit('remove-item', props.index);
const changeHandler = () => emit('change');
const expandHandler = () => {
expand.value = !expand.value;
};
//
const showDelete = (index: number) => {
const deleteFunc = props.config.delete;
if (deleteFunc && typeof deleteFunc === 'function') {
return deleteFunc(props.model, index, mForm?.values);
}
return true;
};
//
const changeOrder = (offset = 0) => emit('swap-item', props.index, `${String(props.index)}${offset}`);
const movable = () => {
const { movable } = props.config;
//
if (movable === undefined) return true;
if (typeof movable === 'function') {
return movable(mForm, props.index || 0, props.model, props.groupModel);
}
return movable;
};
</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,64 +43,41 @@
: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: () => ({}),
},
const mForm = inject<FormState | undefined>('mForm');
config: {
type: Object as PropType<PanelConfig>,
default: () => ({}),
},
const expand = ref(props.config.expand !== false);
prop: String,
const items = computed(() => props.config.items);
size: String,
const filter = (config: any) => filterFunction(mForm, config, props);
name: String,
},
emits: ['change'],
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const expand = ref(props.config.expand !== false);
const items = computed(() => props.config.items);
const filter = (config: any) => filterFunction(mForm, config, props);
const changeHandler = () => emit('change', props.model);
return {
mForm,
expand,
items,
filter,
changeHandler,
};
},
});
const changeHandler = () => emit('change', props.model);
</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,
const mForm = inject<FormState | undefined>('mForm');
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,
};
},
});
const changeHandler = () => emit('change', props.name ? props.model[props.name] : props.model);
</script>

View File

@ -1,82 +1,70 @@
<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: () => ({}),
},
config: {
type: Object as PropType<StepConfig>,
default: () => ({}),
},
stepActive: {
type: Number,
default: () => 1,
},
size: String,
labelWidth: String,
const props = withDefaults(
defineProps<{
model: any;
config: StepConfig;
stepActive?: number;
labelWidth?: string;
size?: string;
}>(),
{
stepActive: 1,
},
);
emits: ['change'],
const emit = defineEmits(['change']);
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const active = ref(1);
const mForm = inject<FormState | undefined>('mForm');
const active = ref(1);
watchEffect(() => {
active.value = props.stepActive;
});
const stepClick = (index: number) => {
active.value = index + 1;
mForm?.$emit('update:stepActive', active.value);
};
const changeHandler = () => {
emit('change', props.model);
};
return { mForm, active, stepClick, changeHandler };
},
watchEffect(() => {
active.value = props.stepActive;
});
const stepClick = (index: number) => {
active.value = index + 1;
mForm?.$emit('update:stepActive', active.value);
};
const changeHandler = () => {
emit('change', props.model);
};
</script>

File diff suppressed because it is too large Load Diff

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,123 +79,90 @@ 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: () => ({}),
},
const mForm = inject<FormState | undefined>('mForm');
const activeTabName = ref(getActive(mForm, props, ''));
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, ''));
const tabs = computed(() => {
if (props.config.dynamic) {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
return props.model[props.config.name] || [];
}
return props.config.items;
});
const filter = (config: any) => filterFunction(mForm, config, props);
watchEffect(() => {
if (typeof props.config.activeChange === 'function') {
props.config.activeChange(mForm, activeTabName.value, {
model: props.model,
prop: props.prop,
});
}
});
return {
mForm,
activeTabName,
tabs,
filter,
tabItems: (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items),
tabClickHandler: (tab: any) => tabClickHandler(mForm, tab, props),
onTabAdd: () => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabAdd === 'function') {
props.config.onTabAdd(mForm, {
model: props.model,
prop: props.prop,
config: props.config,
});
} else if (tabs.value.length > 0) {
const newObj = cloneDeep(tabs.value[0]);
newObj.title = `标签${tabs.value.length + 1}`;
props.model[props.config.name].push(newObj);
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
},
onTabRemove: (tabName: string) => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabRemove === 'function') {
props.config.onTabRemove(mForm, tabName, {
model: props.model,
prop: props.prop,
config: props.config,
});
} else {
props.model[props.config.name].splice(+tabName, 1);
//
if (tabName < activeTabName.value || activeTabName.value >= props.model[props.config.name].length) {
activeTabName.value = (+activeTabName.value - 1).toString();
tabClickHandler(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),
changeHandler: () => {
emit('change', props.model);
if (typeof props.config.onChange === 'function') {
props.config.onChange(mForm, { model: props.model, prop: props.prop, config: props.config });
}
},
};
},
const tabs = computed(() => {
if (props.config.dynamic) {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
return props.model[props.config.name] || [];
}
return props.config.items;
});
export default Tab;
const filter = (config: any) => filterFunction(mForm, config, props);
watchEffect(() => {
if (typeof props.config.activeChange === 'function') {
props.config.activeChange(mForm, activeTabName.value, {
model: props.model,
prop: props.prop,
});
}
});
const tabItems = (tab: TabPaneConfig) => (props.config.dynamic ? props.config.items : tab.items);
const tabClickHandler = (tab: any) => tabClick(mForm, tab, props);
const onTabAdd = () => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabAdd === 'function') {
props.config.onTabAdd(mForm, {
model: props.model,
prop: props.prop,
config: props.config,
});
} else if (tabs.value.length > 0) {
const newObj = cloneDeep(tabs.value[0]);
newObj.title = `标签${tabs.value.length + 1}`;
props.model[props.config.name].push(newObj);
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
};
const onTabRemove = (tabName: string) => {
if (!props.config.name) throw new Error('dynamic tab 必须配置name');
if (typeof props.config.onTabRemove === 'function') {
props.config.onTabRemove(mForm, tabName, {
model: props.model,
prop: props.prop,
config: props.config,
});
} else {
props.model[props.config.name].splice(+tabName, 1);
//
if (tabName < activeTabName.value || activeTabName.value >= props.model[props.config.name].length) {
activeTabName.value = (+activeTabName.value - 1).toString();
tabClick(mForm, { name: activeTabName.value }, props);
}
}
emit('change', props.model);
mForm?.$emit('field-change', props.prop, props.model[props.config.name]);
};
const display = (displayConfig: any) => displayFunc(mForm, displayConfig, props);
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 });
}
};
</script>

View File

@ -1,146 +1,98 @@
<template>
<div class="m-cascader" style="width: 100%">
<el-cascader
v-model="model[name]"
ref="cascader"
style="width: 100%"
clearable
filterable
:size="size"
:placeholder="config.placeholder"
:disabled="disabled"
:options="options"
:props="{ multiple: config.multiple }"
@change="changeHandler"
></el-cascader>
</div>
<TMagicCascader
v-model="model[name]"
ref="tMagicCascader"
style="width: 100%"
clearable
filterable
:size="size"
:placeholder="config.placeholder"
:disabled="disabled"
:options="options"
:props="{ multiple: config.multiple }"
@change="changeHandler"
></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'],
const mForm = inject<FormState | null>('mForm');
setup(props, { emit }) {
const mForm = inject<FormState | null>('mForm');
const vm = getCurrentInstance() as ComponentInternalInstance;
useAddField(props.prop);
useAddField(props.prop);
const requestFunc = getConfig('request') as Function;
const requestFunc = getConfig('request') as Function;
const tMagicCascader = ref<InstanceType<typeof TMagicCascader>>();
const cascader = ref<any>();
const dialog = ref<any>();
const options = Array.isArray(props.config.options) ? ref(props.config.options) : ref([]);
const remoteData = ref<any>(null);
const options = Array.isArray(props.config.options) ? ref(props.config.options) : ref([]);
const remoteData = ref<any>(null);
const setRemoteOptions = async function () {
const { config } = props;
const { option } = config;
if (!option) return;
let { body } = option;
const setRemoteOptions = async function () {
const { config } = props;
const { option } = config;
if (!option) return;
let { body } = option;
const postOptions: Record<string, any> = {
url: option.url,
cache: option.cache,
timeout: option.timeout,
data: {},
};
const postOptions: Record<string, any> = {
url: option.url,
cache: option.cache,
timeout: option.timeout,
data: {},
};
if (body && mForm) {
if (typeof body === 'function' && props.model && mForm) {
body = body(mForm, {
model: props.model,
formValue: mForm.values,
formValues: mForm.values,
config: props.config,
});
}
postOptions.data = body;
}
const res = await requestFunc(postOptions);
remoteData.value = res[option.root];
if (remoteData.value && typeof option?.item === 'function') {
options.value = option.item(res[option.root]);
}
};
//
if (typeof props.config.options === 'function' && props.model && mForm) {
watchEffect(
() =>
(options.value = (props.config.options as Function)(vm, { model: props.model, formValues: mForm.values })),
);
} else if (!props.config.options || !props.config.options.length || props.config.remote) {
Promise.resolve(setRemoteOptions());
if (body && mForm) {
if (typeof body === 'function' && props.model && mForm) {
body = body(mForm, {
model: props.model,
formValue: mForm.values,
formValues: mForm.values,
config: props.config,
});
}
postOptions.data = body;
}
const action = computed(() => {
if (props.config.add?.action.method === 'post') {
return (options: any) =>
requestFunc({
...props.config?.add?.action.body,
...options,
});
}
return null;
});
const res = await requestFunc(postOptions);
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;
emit('change', value);
},
addHandler: () => {
if (!dialog.value) return;
dialog.value.dialogVisible = true;
},
editAfterAction: () => {
setRemoteOptions();
},
};
},
});
remoteData.value = res[option.root];
if (remoteData.value && typeof option?.item === 'function') {
options.value = option.item(res[option.root]);
}
};
//
if (typeof props.config.options === 'function' && props.model && mForm) {
watchEffect(
() => (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 changeHandler = (value: any) => {
if (!tMagicCascader.value) return;
tMagicCascader.value.setQuery('');
tMagicCascader.value.setPreviousQuery(null);
emit('change', value);
};
</script>

View File

@ -1,67 +1,63 @@
<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'],
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
const activeValue = computed(() => {
if (typeof props.config.activeValue === 'undefined') {
if (props.config.filter === 'number') {
return 1;
}
} else {
return props.config.activeValue;
}
return {
activeValue: computed(() => {
if (typeof props.config.activeValue === 'undefined') {
if (props.config.filter === 'number') {
return 1;
}
} else {
return props.config.activeValue;
}
return undefined;
}),
inactiveValue: computed(() => {
if (typeof props.config.inactiveValue === 'undefined') {
if (props.config.filter === 'number') {
return 0;
}
} else {
return props.config.inactiveValue;
}
return undefined;
}),
changeHandler(value: number | boolean) {
emit('change', value);
},
};
},
return undefined;
});
const inactiveValue = computed(() => {
if (typeof props.config.inactiveValue === 'undefined') {
if (props.config.filter === 'number') {
return 0;
}
} else {
return props.config.inactiveValue;
}
return undefined;
});
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'],
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
//
if (props.model && !props.model[props.name]) {
props.model[props.name] = [];
}
//
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>) => {
emit('change', v);
},
};
},
});
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'],
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
props.model[props.name] = datetimeFormatter(props.model[props.name], '');
props.model[props.name] = datetimeFormatter(props.model[props.name], '');
return {
changeHandler(v: string) {
emit('change', v);
},
};
},
});
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,49 +10,41 @@
: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'],
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
const value = props.model?.[props.name].toString();
if (props.model) {
if (value === 'Invalid Date') {
props.model[props.name] = '';
} else {
props.model[props.name] = datetimeFormatter(props.model[props.name], '', props.config.valueFormat);
}
}
const value = props.model?.[props.name].toString();
if (props.model) {
if (value === 'Invalid Date') {
props.model[props.name] = '';
} else {
props.model[props.name] = datetimeFormatter(props.model[props.name], '', props.config.valueFormat);
}
}
return {
value,
changeHandler: (v: Date) => {
emit('change', v);
},
};
},
});
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,87 +23,73 @@
* 特别注意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'],
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
const request = getConfig('request') as Function;
const fieldMap = reactive<{ [key: string]: any }>({
value: {},
});
const fieldLabelMap = reactive<{ [key: string]: any }>({
value: {},
});
const changeFieldMap = async () => {
if (typeof props.config.returnFields !== 'function' || !props.model) return;
const fields = await props.config.returnFields(props.config, props.model, request);
fieldMap.value = {};
fieldLabelMap.value = {};
fields.forEach((v) => {
if (typeof v !== 'object' || v.name === undefined) return;
let oldVal = props.model?.[v.name] || '';
if (!oldVal && v.defaultValue !== undefined) {
oldVal = v.defaultValue;
emit('change', oldVal, v.name);
}
fieldMap.value[v.name] = oldVal;
fieldLabelMap.value[v.name] = v.label || '';
});
};
const unwatch = watch(
() => props.model?.[props.config.dynamicKey],
(val) => {
if (val !== '') {
changeFieldMap();
}
},
{
immediate: true,
},
);
onBeforeUnmount(() => {
if (typeof unwatch === 'function') {
unwatch();
}
});
return {
request,
fieldMap,
fieldLabelMap,
unwatch,
changeFieldMap,
inputChangeHandler: (key: string) => {
emit('change', fieldMap.value[key], key);
},
};
},
methods: {
init() {},
},
const request = getConfig('request') as Function;
const fieldMap = reactive<{ [key: string]: any }>({
value: {},
});
const fieldLabelMap = reactive<{ [key: string]: any }>({
value: {},
});
const changeFieldMap = async () => {
if (typeof props.config.returnFields !== 'function' || !props.model) return;
const fields = await props.config.returnFields(props.config, props.model, request);
fieldMap.value = {};
fieldLabelMap.value = {};
fields.forEach((v) => {
if (typeof v !== 'object' || v.name === undefined) return;
let oldVal = props.model?.[v.name] || '';
if (!oldVal && v.defaultValue !== undefined) {
oldVal = v.defaultValue;
emit('change', oldVal, v.name);
}
fieldMap.value[v.name] = oldVal;
fieldLabelMap.value[v.name] = v.label || '';
});
};
const unwatch = watch(
() => props.model?.[props.config.dynamicKey],
(val) => {
if (val !== '') {
changeFieldMap();
}
},
{
immediate: true,
},
);
onBeforeUnmount(() => {
if (typeof unwatch === 'function') {
unwatch();
}
});
const inputChangeHandler = (key: string) => {
emit('change', fieldMap.value[key], key);
};
</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,101 +12,80 @@
: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'],
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
const formValue = ref({});
const editor = ref<InstanceType<typeof FormDialog>>();
const mForm = inject<FormState | undefined>('mForm');
const formValue = ref({});
const editor = ref<InstanceType<typeof FormDialog>>();
const mForm = inject<FormState | undefined>('mForm');
const href = computed(() => {
if (typeof props.config.href === 'function' && props.model) {
return props.config.href(props.model);
}
return props.config.href || props.model?.[props.name];
});
const init = () => {
formValue.value = props.model?.[props.name] || {};
};
return {
// ref
formValue,
editor,
// computed methods
href,
formConfig: computed(() => {
if (typeof props.config.form === 'function') {
return props.config.form(mForm, {
model: props.model || {},
values: props.values || {},
});
}
return props.config.form;
}),
disable: computed(() => {
if (typeof props.config.disabled !== 'undefined') {
return props.config.disabled;
}
return !href.value;
}),
displayText: computed(() => {
if (typeof props.config.displayText === 'function') {
return props.config.displayText(mForm, { model: props.model || {} });
}
if (props.config.displayText) {
return props.config.displayText;
}
return '跳转';
}),
// methods
init,
editHandler: () => {
init();
editor.value && (editor.value.dialogVisible = true);
},
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);
},
};
},
const href = computed(() => {
if (typeof props.config.href === 'function' && props.model) {
return props.config.href(props.model);
}
return props.config.href || props.model?.[props.name];
});
const init = () => {
formValue.value = props.model?.[props.name] || {};
};
const formConfig = computed(() => {
if (typeof props.config.form === 'function') {
return props.config.form(mForm, {
model: props.model || {},
values: props.values || {},
});
}
return props.config.form;
});
const displayText = computed(() => {
if (typeof props.config.displayText === 'function') {
return props.config.displayText(mForm, { model: props.model || {} });
}
if (props.config.displayText) {
return props.config.displayText;
}
return '跳转';
});
const editHandler = () => {
init();
editor.value && (editor.value.dialogVisible = true);
};
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);
useAddField(props.prop);
const mForm = inject<FormState | null>('mForm');
return {
mForm,
changeHandler: (value: number) => {
emit('change', value);
},
inputHandler: (v: string) => {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
},
};
},
});
const mForm = inject<FormState | null>('mForm');
const changeHandler = (value: number) => {
emit('change', value);
};
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'],
const changeHandler = (value: number) => {
emit('change', value);
};
setup(props, { emit }) {
useAddField(props.prop);
return {
changeHandler: (v: string | number | boolean) => emit('change', v),
};
},
});
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,346 +19,330 @@
@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,
},
},
if (!props.model) throw new Error('不能没有model');
useAddField(props.prop);
emits: ['change'],
const tMagicSelect = ref<InstanceType<typeof TMagicSelect>>();
const mForm = inject<FormState | undefined>('mForm');
const options = ref<SelectOption[] | SelectGroupOption[]>([]);
const localOptions = ref<SelectOption[] | SelectGroupOption[]>([]);
const loading = ref(false);
const moreLoadingVisible = ref(false);
const total = ref(0);
const pgIndex = ref(0);
const pgSize = ref(20);
const query = ref('');
const remoteData = ref<any[]>([]);
const remote = ref(true);
setup(props, { emit }) {
if (!props.model) throw new Error('不能没有model');
useAddField(props.prop);
const equalValue = (value: any, v: any): boolean => {
if (typeof v === 'object') {
const key = props.config.valueKey || 'value';
return v[key] === value[key];
}
const select = ref<any>();
const mForm = inject<FormState | undefined>('mForm');
const options = ref<SelectOption[] | SelectGroupOption[]>([]);
const localOptions = ref<SelectOption[] | SelectGroupOption[]>([]);
const loading = ref(false);
const moreLoadingVisible = ref(false);
const total = ref(0);
const pgIndex = ref(0);
const pgSize = ref(20);
const query = ref('');
const remoteData = ref<any[]>([]);
const remote = ref(true);
return value === v;
};
const equalValue = (value: any, v: any): boolean => {
if (typeof v === 'object') {
const key = props.config.valueKey || 'value';
return v[key] === value[key];
}
const mapOptions = (data: any[]) => {
const { option } = props.config;
const { text } = option;
const { value } = option;
return value === v;
};
return data.map((item) => ({
text: typeof text === 'function' ? text(item) : item[text || 'text'],
value: typeof value === 'function' ? value(item) : item[value || 'value'],
}));
};
const mapOptions = (data: any[]) => {
const { option } = props.config;
const { text } = option;
const { value } = option;
const getOptions = async () => {
if (!props.model) return [];
return data.map((item) => ({
text: typeof text === 'function' ? text(item) : item[text || 'text'],
value: typeof value === 'function' ? value(item) : item[value || 'value'],
}));
};
if (localOptions.value.length) {
return localOptions.value;
}
const getOptions = async () => {
if (!props.model) return [];
loading.value = true;
if (localOptions.value.length) {
return localOptions.value;
}
let items: SelectOption[] | SelectGroupOption[] = [];
loading.value = true;
const { config } = props;
const { option } = config;
let { body } = option;
let items: SelectOption[] | SelectGroupOption[] = [];
let postOptions: Record<string, any> = {
method: option.method || 'POST',
url: option.url,
cache: option.cache,
timeout: option.timeout,
mode: option.mode,
headers: option.headers || {},
json: option.json || false,
};
const { config } = props;
const { option } = config;
let { body } = option;
let postOptions: Record<string, any> = {
method: option.method || 'POST',
url: option.url,
cache: option.cache,
timeout: option.timeout,
mode: option.mode,
headers: option.headers || {},
json: option.json || false,
};
if (body) {
if (typeof body === 'function') {
body = body(mForm, {
model: props.model,
formValue: mForm?.values,
formValues: mForm?.values,
config: props.config,
}) as Record<string, any>;
}
body.query = query.value;
body.pgSize = pgSize.value;
body.pgIndex = pgIndex.value;
postOptions.data = body;
}
const requestFuc = getConfig('request') as Function;
if (typeof option.beforeRequest === 'function') {
postOptions = option.beforeRequest(mForm, postOptions, {
model: props.model,
formValue: mForm?.values,
});
}
if (option.method?.toLocaleLowerCase() === 'jsonp') {
postOptions.jsonpCallback = option.jsonpCallback || 'callback';
}
let res = await requestFuc(postOptions);
if (typeof option.afterRequest === 'function') {
res = option.afterRequest(mForm, res, {
model: props.model,
formValue: mForm?.values,
formValues: mForm?.values,
config: props.config,
});
}
const optionsData = res[option.root];
if (res.total > 0) {
total.value = res.total;
}
remoteData.value = remoteData.value.concat(optionsData);
if (optionsData) {
if (typeof option.item === 'function') {
items = option.item(optionsData);
} else if (optionsData.map) {
items = mapOptions(optionsData);
}
}
loading.value = false;
//
const selectedOptions: SelectOption[] | SelectGroupOption[] = [];
if (props.config.multiple && props.model[props.name]) {
options.value.forEach((o: any) => {
const isInclude = props.model?.[props.name].includes(o.value);
if (isInclude && !(items as any[]).find((op: any) => op.value === o.value)) {
selectedOptions.push(o);
}
});
}
return pgIndex.value === 0 ? (selectedOptions as any[]).concat(items) : (options.value as any).concat(items);
};
const getInitLocalOption = async () => {
if (!props.model) return [];
const value = props.model[props.name];
const { config } = props;
localOptions.value = await getOptions();
remote.value = false;
if (config.group) {
if (config.multiple && value.findIndex) {
return (localOptions.value as SelectGroupOption[]).filter(
(group) => group.options.findIndex((item) => value.find((v: any) => equalValue(item.value, v)) > -1) > -1,
);
}
return (localOptions.value as SelectGroupOption[]).filter(
(group) => group.options.findIndex((item) => equalValue(item.value, value)) > -1,
);
}
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) => equalValue(item.value, value));
};
const getInitOption = async () => {
if (!props.model) return [];
const { config } = props;
const { option } = config;
let options: SelectOption[] | SelectGroupOption[] = [];
let url = option.initUrl;
if (!url) {
return getInitLocalOption();
}
if (typeof url === 'function') {
url = await url(mForm, { model: props.model, formValue: mForm?.values });
}
const postOptions: Record<string, any> = {
method: option.method || 'POST',
url,
data: {
id: props.model[props.name],
},
mode: option.mode,
headers: option.headers || {},
json: option.json || false,
};
if (option.method?.toLocaleLowerCase() === 'jsonp') {
postOptions.jsonpCallback = option.jsonpCallback || 'callback';
}
const requestFuc = getConfig('request') as Function;
const res = await requestFuc(postOptions);
let initData = res[option.root];
if (initData) {
if (!Array.isArray(initData)) {
initData = [initData];
}
if (typeof option.item === 'function') {
options = option.item(initData);
} else if (initData.map) {
options = mapOptions(initData);
}
}
return options;
};
if (typeof props.config.options === 'function') {
watchEffect(() => {
typeof props.config.options === 'function' &&
Promise.resolve(
props.config.options(mForm, {
model: props.model,
prop: props.prop,
formValues: mForm?.values,
formValue: mForm?.values,
config: props.config,
}),
).then((data) => {
options.value = data;
});
});
} else if (Array.isArray(props.config.options)) {
watchEffect(() => {
options.value = props.config.options as SelectOption[] | SelectGroupOption[];
});
} else if (props.config.option) {
onBeforeMount(() => {
if (!props.model) return;
const v = props.model[props.name];
if (Array.isArray(v) ? v.length : typeof v !== 'undefined') {
getInitOption().then((data) => {
options.value = data;
});
}
});
if (body) {
if (typeof body === 'function') {
body = body(mForm, {
model: props.model,
formValue: mForm?.values,
formValues: mForm?.values,
config: props.config,
}) as Record<string, any>;
}
props.config.remote &&
onMounted(() => {
select.value?.scrollbar.wrap$.addEventListener('scroll', async (e: Event) => {
const el = e.currentTarget as HTMLDivElement;
if (moreLoadingVisible.value) {
return;
}
if (el.scrollHeight - el.clientHeight - el.scrollTop > 1) {
return;
}
if (total.value <= options.value.length) {
return;
}
moreLoadingVisible.value = true;
pgIndex.value += 1;
options.value = await getOptions();
moreLoadingVisible.value = false;
});
body.query = query.value;
body.pgSize = pgSize.value;
body.pgIndex = pgIndex.value;
postOptions.data = body;
}
const requestFuc = getConfig('request') as Function;
if (typeof option.beforeRequest === 'function') {
postOptions = option.beforeRequest(mForm, postOptions, {
model: props.model,
formValue: mForm?.values,
});
}
if (option.method?.toLocaleLowerCase() === 'jsonp') {
postOptions.jsonpCallback = option.jsonpCallback || 'callback';
}
let res = await requestFuc(postOptions);
if (typeof option.afterRequest === 'function') {
res = option.afterRequest(mForm, res, {
model: props.model,
formValue: mForm?.values,
formValues: mForm?.values,
config: props.config,
});
}
const optionsData = res[option.root];
if (res.total > 0) {
total.value = res.total;
}
remoteData.value = remoteData.value.concat(optionsData);
if (optionsData) {
if (typeof option.item === 'function') {
items = option.item(optionsData);
} else if (optionsData.map) {
items = mapOptions(optionsData);
}
}
loading.value = false;
//
const selectedOptions: SelectOption[] | SelectGroupOption[] = [];
if (props.config.multiple && props.model[props.name]) {
options.value.forEach((o: any) => {
const isInclude = props.model?.[props.name].includes(o.value);
if (isInclude && !(items as any[]).find((op: any) => op.value === o.value)) {
selectedOptions.push(o);
}
});
}
return pgIndex.value === 0 ? (selectedOptions as any[]).concat(items) : (options.value as any).concat(items);
};
const getInitLocalOption = async () => {
if (!props.model) return [];
const value = props.model[props.name];
const { config } = props;
localOptions.value = await getOptions();
remote.value = false;
if (config.group) {
if (config.multiple && value.findIndex) {
return (localOptions.value as SelectGroupOption[]).filter(
(group) => group.options.findIndex((item) => value.find((v: any) => equalValue(item.value, v)) > -1) > -1,
);
}
return (localOptions.value as SelectGroupOption[]).filter(
(group) => group.options.findIndex((item) => equalValue(item.value, value)) > -1,
);
}
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) => equalValue(item.value, value));
};
const getInitOption = async () => {
if (!props.model) return [];
const { config } = props;
const { option } = config;
let options: SelectOption[] | SelectGroupOption[] = [];
let url = option.initUrl;
if (!url) {
return getInitLocalOption();
}
if (typeof url === 'function') {
url = await url(mForm, { model: props.model, formValue: mForm?.values });
}
const postOptions: Record<string, any> = {
method: option.method || 'POST',
url,
data: {
id: props.model[props.name],
},
mode: option.mode,
headers: option.headers || {},
json: option.json || false,
};
if (option.method?.toLocaleLowerCase() === 'jsonp') {
postOptions.jsonpCallback = option.jsonpCallback || 'callback';
}
const requestFuc = getConfig('request') as Function;
const res = await requestFuc(postOptions);
let initData = res[option.root];
if (initData) {
if (!Array.isArray(initData)) {
initData = [initData];
}
if (typeof option.item === 'function') {
options = option.item(initData);
} else if (initData.map) {
options = mapOptions(initData);
}
}
return options;
};
if (typeof props.config.options === 'function') {
watchEffect(() => {
typeof props.config.options === 'function' &&
Promise.resolve(
props.config.options(mForm, {
model: props.model,
prop: props.prop,
formValues: mForm?.values,
formValue: mForm?.values,
config: props.config,
}),
).then((data) => {
options.value = data;
});
});
} else if (Array.isArray(props.config.options)) {
watchEffect(() => {
options.value = props.config.options as SelectOption[] | SelectGroupOption[];
});
} else if (props.config.option) {
onBeforeMount(() => {
if (!props.model) return;
const v = props.model[props.name];
if (Array.isArray(v) ? v.length : typeof v !== 'undefined') {
getInitOption().then((data) => {
options.value = data;
});
}
});
}
return {
select,
loading,
remote,
options: options as Ref<SelectOption[]>,
groupOptions: options as Ref<SelectGroupOption[]>,
moreLoadingVisible,
popperClass: mForm?.popperClass,
props.config.remote &&
onMounted(async () => {
await nextTick();
tMagicSelect.value?.scrollbarWrap?.addEventListener('scroll', async (e: Event) => {
const el = e.currentTarget as HTMLDivElement;
if (moreLoadingVisible.value) {
return;
}
if (el.scrollHeight - el.clientHeight - el.scrollTop > 1) {
return;
}
if (total.value <= options.value.length) {
return;
}
moreLoadingVisible.value = true;
pgIndex.value += 1;
options.value = await getOptions();
moreLoadingVisible.value = false;
});
});
getOptions,
const popperClass = mForm?.popperClass;
getRequestFuc() {
return getConfig('request');
},
const changeHandler = (value: any) => {
emit('change', value);
};
changeHandler(value: any) {
emit('change', value);
},
const visibleHandler = async (visible: boolean) => {
if (!visible) return;
async visibleHandler(visible: boolean) {
if (!visible) return;
if (!props.config.remote) return;
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();
}
};
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;
} else if (options.value.length <= (props.config.multiple ? props.model?.[props.name].length : 1)) {
options.value = await getOptions();
}
},
const remoteMethod = async (q: string) => {
if (!localOptions.value.length) {
query.value = q;
pgIndex.value = 0;
options.value = await getOptions();
// el-select
if (props.config.multiple)
setTimeout(() => {
tMagicSelect.value?.setSelected();
}, 0);
}
};
async remoteMethod(q: string) {
if (!localOptions.value.length) {
query.value = q;
pgIndex.value = 0;
options.value = await getOptions();
// el-select
if (props.config.multiple)
setTimeout(() => {
select.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,65 +1,62 @@
<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);
useAddField(props.prop);
return {
n: computed(() => props.name || props.config.name || ''),
activeValue: computed(() => {
if (typeof props.config.activeValue === 'undefined') {
if (props.config.filter === 'number') {
return 1;
}
} else {
return props.config.activeValue;
}
const changeHandler = (value: number) => {
emit('change', value);
};
return true;
}),
inactiveValue: computed(() => {
if (typeof props.config.inactiveValue === 'undefined') {
if (props.config.filter === 'number') {
return 0;
}
} else {
return props.config.inactiveValue;
}
const activeValue = computed(() => {
if (typeof props.config.activeValue === 'undefined') {
if (props.config.filter === 'number') {
return 1;
}
} else {
return props.config.activeValue;
}
return false;
}),
changeHandler: (v: boolean | number | string) => {
emit('change', v);
},
};
},
return true;
});
const inactiveValue = computed(() => {
if (typeof props.config.inactiveValue === 'undefined') {
if (props.config.filter === 'number') {
return 0;
}
} else {
return props.config.inactiveValue;
}
return false;
});
</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,117 +20,109 @@
{{ 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,
},
},
const emit = defineEmits(['change', 'input']);
emits: ['change', 'input'],
useAddField(props.prop);
setup(props, { emit }) {
const mForm = inject<FormState | undefined>('mForm');
const mForm = inject<FormState | undefined>('mForm');
useAddField(props.prop);
const changeHandler = (value: number) => {
emit('change', value);
};
const modelName = computed(() => props.name || props.config.name || '');
return {
modelName,
const inputHandler = (v: string) => {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
};
changeHandler(v: string | number) {
emit('change', v);
},
const buttonClickHandler = () => {
if (typeof props.config.append === 'string') return;
inputHandler(v: string | number) {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
},
if (props.config.append?.handler) {
props.config.append.handler(mForm, {
model: props.model,
values: mForm?.values,
});
}
};
buttonClickHandler() {
if (typeof props.config.append === 'string') return;
const keyUpHandler = ($event: KeyboardEvent) => {
if (!props.model) return;
if (!props.name) return;
if (props.config.append?.handler) {
props.config.append.handler(mForm, {
model: props.model,
values: mForm?.values,
});
}
},
const arrowUp = $event.key === 'ArrowUp';
const arrowDown = $event.key === 'ArrowDown';
keyUpHandler($event: KeyboardEvent) {
if (!props.model) return;
if (!modelName.value) return;
if (!arrowUp && !arrowDown) {
return;
}
const arrowUp = $event.key === 'ArrowUp';
const arrowDown = $event.key === 'ArrowDown';
const value = props.model[props.name];
let num;
let unit;
if (isNumber(value)) {
num = +value;
} else {
value.replace(/^([0-9.]+)([a-z%]+)$/, ($0: string, $1: string, $2: string) => {
num = +$1;
unit = $2;
});
}
if (!arrowUp && !arrowDown) {
return;
}
if (num === undefined) {
return;
}
const value = props.model[modelName.value];
let num;
let unit;
if (isNumber(value)) {
num = +value;
} else {
value.replace(/^([0-9.]+)([a-z%]+)$/, ($0: string, $1: string, $2: string) => {
num = +$1;
unit = $2;
});
}
const ctrl = navigator.platform.match('Mac') ? $event.metaKey : $event.ctrlKey;
const shift = $event.shiftKey;
const alt = $event.altKey;
if (num === undefined) {
return;
}
if (arrowUp) {
if (ctrl) {
num += 100;
} else if (alt) {
num = (num * 10000 + 1000) / 10000;
} else if (shift) {
num = num + 10;
} else {
num += 1;
}
} else if (arrowDown) {
if (ctrl) {
num -= 100;
} else if (alt) {
num = (num * 10000 - 1000) / 10000;
} else if (shift) {
num -= 10;
} else {
num -= 1;
}
}
const ctrl = navigator.platform.match('Mac') ? $event.metaKey : $event.ctrlKey;
const shift = $event.shiftKey;
const alt = $event.altKey;
if (arrowUp) {
if (ctrl) {
num += 100;
} else if (alt) {
num = (num * 10000 + 1000) / 10000;
} else if (shift) {
num = num + 10;
} else {
num += 1;
}
} else if (arrowDown) {
if (ctrl) {
num -= 100;
} else if (alt) {
num = (num * 10000 - 1000) / 10000;
} else if (shift) {
num -= 10;
} else {
num -= 1;
}
}
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;
},
},
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
const mForm = inject<FormState | null>('mForm');
const mForm = inject<FormState | null>('mForm');
const changeHandler = (value: number) => {
emit('change', value);
};
return {
mForm,
changeHandler: (v: string) => {
emit('change', v);
},
inputHandler: (v: string) => {
emit('input', v);
mForm?.$emit('field-input', props.prop, v);
},
};
},
});
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;
},
},
useAddField(props.prop);
setup(props, { emit }) {
useAddField(props.prop);
return {
changeHandler: (v: Date) => {
emit('change', v);
},
};
},
});
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: {