mirror of
https://github.com/Tencent/tmagic-editor.git
synced 2025-04-06 03:57:56 +08:00
feat(editor): 组件配置中的样式支持单独一列显示
This commit is contained in:
parent
7a8da68edb
commit
5cd6d21b2e
@ -134,7 +134,7 @@ import type { MApp } from '@tmagic/core';
|
|||||||
|
|
||||||
import Framework from './layouts/Framework.vue';
|
import Framework from './layouts/Framework.vue';
|
||||||
import TMagicNavMenu from './layouts/NavMenu.vue';
|
import TMagicNavMenu from './layouts/NavMenu.vue';
|
||||||
import PropsPanel from './layouts/PropsPanel.vue';
|
import PropsPanel from './layouts/props-panel/PropsPanel.vue';
|
||||||
import Sidebar from './layouts/sidebar/Sidebar.vue';
|
import Sidebar from './layouts/sidebar/Sidebar.vue';
|
||||||
import Workspace from './layouts/workspace/Workspace.vue';
|
import Workspace from './layouts/workspace/Workspace.vue';
|
||||||
import codeBlockService from './services/codeBlock';
|
import codeBlockService from './services/codeBlock';
|
||||||
|
@ -91,7 +91,8 @@ export { default as KeyValue } from './fields/KeyValue.vue';
|
|||||||
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
|
export { default as CodeBlockList } from './layouts/sidebar/code-block/CodeBlockList.vue';
|
||||||
export { default as CodeBlockListPanel } from './layouts/sidebar/code-block/CodeBlockListPanel.vue';
|
export { default as CodeBlockListPanel } from './layouts/sidebar/code-block/CodeBlockListPanel.vue';
|
||||||
export { default as DataSourceConfigPanel } from './layouts/sidebar/data-source/DataSourceConfigPanel.vue';
|
export { default as DataSourceConfigPanel } from './layouts/sidebar/data-source/DataSourceConfigPanel.vue';
|
||||||
export { default as PropsPanel } from './layouts/PropsPanel.vue';
|
export { default as PropsPanel } from './layouts/props-panel/PropsPanel.vue';
|
||||||
|
export { default as PropsFormPanel } from './layouts/props-panel/FormPanel.vue';
|
||||||
export { default as ToolButton } from './components/ToolButton.vue';
|
export { default as ToolButton } from './components/ToolButton.vue';
|
||||||
export { default as ContentMenu } from './components/ContentMenu.vue';
|
export { default as ContentMenu } from './components/ContentMenu.vue';
|
||||||
export { default as Icon } from './components/Icon.vue';
|
export { default as Icon } from './components/Icon.vue';
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
v-model:left="columnWidth.left"
|
v-model:left="columnWidth.left"
|
||||||
v-model:right="columnWidth.right"
|
v-model:right="columnWidth.right"
|
||||||
:min-left="65"
|
:min-left="65"
|
||||||
:min-right="20"
|
:min-right="320"
|
||||||
:min-center="100"
|
:min-center="100"
|
||||||
:width="frameworkRect?.width || 0"
|
:width="frameworkRect?.width || 0"
|
||||||
@change="columnWidthChange"
|
@change="columnWidthChange"
|
||||||
@ -50,9 +50,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="page" #right>
|
<template v-if="page" #right>
|
||||||
<TMagicScrollbar>
|
<slot name="props-panel"></slot>
|
||||||
<slot name="props-panel"></slot>
|
|
||||||
</TMagicScrollbar>
|
|
||||||
</template>
|
</template>
|
||||||
</SplitView>
|
</SplitView>
|
||||||
|
|
||||||
@ -65,7 +63,6 @@
|
|||||||
import { computed, inject, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
|
import { computed, inject, onBeforeUnmount, onMounted, ref, useTemplateRef, watch } from 'vue';
|
||||||
|
|
||||||
import type { MPage, MPageFragment } from '@tmagic/core';
|
import type { MPage, MPageFragment } from '@tmagic/core';
|
||||||
import { TMagicScrollbar } from '@tmagic/design';
|
|
||||||
|
|
||||||
import SplitView from '@editor/components/SplitView.vue';
|
import SplitView from '@editor/components/SplitView.vue';
|
||||||
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions, Services } from '@editor/type';
|
import type { FrameworkSlots, GetColumnWidth, PageBarSortOptions, Services } from '@editor/type';
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="m-editor-props-panel" v-show="nodes.length === 1">
|
|
||||||
<slot name="props-panel-header"></slot>
|
|
||||||
<MForm
|
|
||||||
ref="configForm"
|
|
||||||
:class="propsPanelSize"
|
|
||||||
:popper-class="`m-editor-props-panel-popper ${propsPanelSize}`"
|
|
||||||
:size="propsPanelSize"
|
|
||||||
:init-values="values"
|
|
||||||
:config="curFormConfig"
|
|
||||||
:extend-state="extendState"
|
|
||||||
@change="submit"
|
|
||||||
@error="errorHandler"
|
|
||||||
></MForm>
|
|
||||||
|
|
||||||
<TMagicButton
|
|
||||||
v-if="!disabledShowSrc"
|
|
||||||
class="m-editor-props-panel-src-icon"
|
|
||||||
circle
|
|
||||||
size="large"
|
|
||||||
title="源码"
|
|
||||||
:type="showSrc ? 'primary' : ''"
|
|
||||||
@click="showSrc = !showSrc"
|
|
||||||
>
|
|
||||||
<MIcon :icon="DocumentIcon"></MIcon>
|
|
||||||
</TMagicButton>
|
|
||||||
|
|
||||||
<CodeEditor
|
|
||||||
v-if="showSrc"
|
|
||||||
class="m-editor-props-panel-src-code"
|
|
||||||
:height="`${editorContentHeight}px`"
|
|
||||||
:init-values="values"
|
|
||||||
:options="codeOptions"
|
|
||||||
:parse="true"
|
|
||||||
@save="saveCode"
|
|
||||||
></CodeEditor>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import {
|
|
||||||
computed,
|
|
||||||
getCurrentInstance,
|
|
||||||
inject,
|
|
||||||
onBeforeUnmount,
|
|
||||||
onMounted,
|
|
||||||
ref,
|
|
||||||
useTemplateRef,
|
|
||||||
watchEffect,
|
|
||||||
} from 'vue';
|
|
||||||
import { Document as DocumentIcon } from '@element-plus/icons-vue';
|
|
||||||
|
|
||||||
import type { MNode } from '@tmagic/core';
|
|
||||||
import { TMagicButton } from '@tmagic/design';
|
|
||||||
import type { ContainerChangeEventData, FormState, FormValue } from '@tmagic/form';
|
|
||||||
import { MForm } from '@tmagic/form';
|
|
||||||
|
|
||||||
import MIcon from '@editor/components/Icon.vue';
|
|
||||||
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
|
||||||
import type { PropsPanelSlots, Services } from '@editor/type';
|
|
||||||
|
|
||||||
import CodeEditor from './CodeEditor.vue';
|
|
||||||
|
|
||||||
defineSlots<PropsPanelSlots>();
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: 'MEditorPropsPanel',
|
|
||||||
});
|
|
||||||
|
|
||||||
defineProps<{
|
|
||||||
disabledShowSrc?: boolean;
|
|
||||||
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
|
||||||
}>();
|
|
||||||
|
|
||||||
const codeOptions = inject('codeOptions', {});
|
|
||||||
|
|
||||||
const emit = defineEmits(['mounted', 'submit-error', 'form-error']);
|
|
||||||
|
|
||||||
const showSrc = ref(false);
|
|
||||||
|
|
||||||
const internalInstance = getCurrentInstance();
|
|
||||||
const values = ref<FormValue>({});
|
|
||||||
const configForm = useTemplateRef<InstanceType<typeof MForm>>('configForm');
|
|
||||||
// ts类型应该是FormConfig, 但是打包时会出错,所以暂时用any
|
|
||||||
const curFormConfig = ref<any>([]);
|
|
||||||
const services = inject<Services>('services');
|
|
||||||
const node = computed(() => services?.editorService.get('node'));
|
|
||||||
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
|
||||||
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
|
|
||||||
const stage = computed(() => services?.editorService.get('stage'));
|
|
||||||
|
|
||||||
const { height: editorContentHeight } = useEditorContentHeight();
|
|
||||||
|
|
||||||
const init = async () => {
|
|
||||||
if (!node.value) {
|
|
||||||
curFormConfig.value = [];
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
|
||||||
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
|
|
||||||
values.value = node.value;
|
|
||||||
};
|
|
||||||
|
|
||||||
watchEffect(init);
|
|
||||||
services?.propsService.on('props-configs-change', init);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
emit('mounted', internalInstance);
|
|
||||||
});
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
services?.propsService.off('props-configs-change', init);
|
|
||||||
});
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
if (configForm.value && stage.value) {
|
|
||||||
configForm.value.formState.stage = stage.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
|
|
||||||
try {
|
|
||||||
const values = await configForm.value?.submitForm();
|
|
||||||
services?.editorService.update(values, { changeRecords: eventData.changeRecords });
|
|
||||||
} catch (e: any) {
|
|
||||||
emit('submit-error', e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const errorHandler = (e: any) => {
|
|
||||||
emit('form-error', e);
|
|
||||||
};
|
|
||||||
|
|
||||||
const saveCode = (values: MNode) => {
|
|
||||||
services?.editorService.update(values);
|
|
||||||
};
|
|
||||||
|
|
||||||
defineExpose({ configForm, submit });
|
|
||||||
</script>
|
|
123
packages/editor/src/layouts/props-panel/FormPanel.vue
Normal file
123
packages/editor/src/layouts/props-panel/FormPanel.vue
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
<template>
|
||||||
|
<div class="m-editor-props-form-panel">
|
||||||
|
<slot name="props-form-panel-header"></slot>
|
||||||
|
|
||||||
|
<TMagicScrollbar>
|
||||||
|
<MForm
|
||||||
|
ref="configForm"
|
||||||
|
:class="propsPanelSize"
|
||||||
|
:popper-class="`m-editor-props-panel-popper ${propsPanelSize}`"
|
||||||
|
:label-width="labelWidth"
|
||||||
|
:label-position="labelPosition"
|
||||||
|
:size="propsPanelSize"
|
||||||
|
:init-values="values"
|
||||||
|
:config="config"
|
||||||
|
:extend-state="extendState"
|
||||||
|
@change="submit"
|
||||||
|
@error="errorHandler"
|
||||||
|
></MForm>
|
||||||
|
</TMagicScrollbar>
|
||||||
|
|
||||||
|
<TMagicButton
|
||||||
|
v-if="!disabledShowSrc"
|
||||||
|
class="m-editor-props-panel-src-icon"
|
||||||
|
circle
|
||||||
|
title="源码"
|
||||||
|
:type="showSrc ? 'primary' : ''"
|
||||||
|
@click="showSrc = !showSrc"
|
||||||
|
>
|
||||||
|
<MIcon :icon="DocumentIcon"></MIcon>
|
||||||
|
</TMagicButton>
|
||||||
|
|
||||||
|
<CodeEditor
|
||||||
|
v-if="showSrc"
|
||||||
|
class="m-editor-props-panel-src-code"
|
||||||
|
:height="`${editorContentHeight}px`"
|
||||||
|
:init-values="codeValueKey ? values[codeValueKey] : values"
|
||||||
|
:options="codeOptions"
|
||||||
|
:parse="true"
|
||||||
|
@save="saveCode"
|
||||||
|
></CodeEditor>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, getCurrentInstance, inject, onMounted, ref, useTemplateRef, watchEffect } from 'vue';
|
||||||
|
import { Document as DocumentIcon } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import { TMagicButton, TMagicScrollbar } from '@tmagic/design';
|
||||||
|
import type { ContainerChangeEventData, FormConfig, FormState, FormValue } from '@tmagic/form';
|
||||||
|
import { MForm } from '@tmagic/form';
|
||||||
|
|
||||||
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
|
import { useEditorContentHeight } from '@editor/hooks/use-editor-content-height';
|
||||||
|
import type { Services } from '@editor/type';
|
||||||
|
|
||||||
|
import CodeEditor from '../CodeEditor.vue';
|
||||||
|
|
||||||
|
defineSlots<{
|
||||||
|
'props-form-panel-header'(props: {}): any;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorFormPanel',
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
config: FormConfig;
|
||||||
|
values: FormValue;
|
||||||
|
disabledShowSrc?: boolean;
|
||||||
|
labelWidth?: string;
|
||||||
|
codeValueKey?: string;
|
||||||
|
labelPosition?: string;
|
||||||
|
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
submit: [values: any, eventData?: ContainerChangeEventData];
|
||||||
|
'submit-error': [e: any];
|
||||||
|
'form-error': [e: any];
|
||||||
|
mounted: [internalInstance: any];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
const codeOptions = inject('codeOptions', {});
|
||||||
|
|
||||||
|
const showSrc = ref(false);
|
||||||
|
const propsPanelSize = computed(() => services?.uiService.get('propsPanelSize') || 'small');
|
||||||
|
const { height: editorContentHeight } = useEditorContentHeight();
|
||||||
|
const stage = computed(() => services?.editorService.get('stage'));
|
||||||
|
|
||||||
|
const configForm = useTemplateRef<InstanceType<typeof MForm>>('configForm');
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
if (configForm.value) {
|
||||||
|
configForm.value.formState.stage = stage.value;
|
||||||
|
configForm.value.formState.services = services;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const internalInstance = getCurrentInstance();
|
||||||
|
onMounted(() => {
|
||||||
|
emit('mounted', internalInstance);
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = async (v: FormValue, eventData: ContainerChangeEventData) => {
|
||||||
|
try {
|
||||||
|
const values = await configForm.value?.submitForm();
|
||||||
|
emit('submit', values, eventData);
|
||||||
|
} catch (e: any) {
|
||||||
|
emit('submit-error', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorHandler = (e: any) => {
|
||||||
|
emit('form-error', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveCode = (values: any) => {
|
||||||
|
emit('submit', props.codeValueKey ? { [props.codeValueKey]: values } : values);
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({ configForm, submit });
|
||||||
|
</script>
|
142
packages/editor/src/layouts/props-panel/PropsPanel.vue
Normal file
142
packages/editor/src/layouts/props-panel/PropsPanel.vue
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
<template>
|
||||||
|
<div class="m-editor-props-panel" v-show="nodes.length === 1">
|
||||||
|
<slot name="props-panel-header"></slot>
|
||||||
|
<FormPanel
|
||||||
|
ref="propertyFormPanel"
|
||||||
|
class="m-editor-props-property-panel"
|
||||||
|
:class="{ 'show-style-panel': showStylePanel }"
|
||||||
|
:config="curFormConfig"
|
||||||
|
:values="values"
|
||||||
|
:disabledShowSrc="disabledShowSrc"
|
||||||
|
:extendState="extendState"
|
||||||
|
@submit="submit"
|
||||||
|
@submit-error="errorHandler"
|
||||||
|
@form-error="errorHandler"
|
||||||
|
@mounted="mountedHandler"
|
||||||
|
></FormPanel>
|
||||||
|
|
||||||
|
<FormPanel
|
||||||
|
v-if="showStylePanel"
|
||||||
|
class="m-editor-props-style-panel"
|
||||||
|
label-position="top"
|
||||||
|
code-value-key="style"
|
||||||
|
:config="styleFormConfig"
|
||||||
|
:values="values"
|
||||||
|
:disabledShowSrc="disabledShowSrc"
|
||||||
|
:extendState="extendState"
|
||||||
|
@submit="submit"
|
||||||
|
@submit-error="errorHandler"
|
||||||
|
@form-error="errorHandler"
|
||||||
|
>
|
||||||
|
<template #props-form-panel-header>
|
||||||
|
<div class="m-editor-props-style-panel-title">
|
||||||
|
<span>样式</span>
|
||||||
|
<div>
|
||||||
|
<TMagicButton link size="small" @click="closeStylePanelHandler"><MIcon :icon="Close"></MIcon></TMagicButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</FormPanel>
|
||||||
|
|
||||||
|
<TMagicButton
|
||||||
|
v-if="!showStylePanel"
|
||||||
|
class="m-editor-props-panel-style-icon"
|
||||||
|
circle
|
||||||
|
:type="showStylePanel ? 'primary' : ''"
|
||||||
|
@click="showStylePanelHandler"
|
||||||
|
>
|
||||||
|
<MIcon :icon="Sugar"></MIcon>
|
||||||
|
</TMagicButton>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { computed, inject, onBeforeUnmount, ref, useTemplateRef, watchEffect } from 'vue';
|
||||||
|
import { Close, Sugar } from '@element-plus/icons-vue';
|
||||||
|
|
||||||
|
import type { MNode } from '@tmagic/core';
|
||||||
|
import { TMagicButton } from '@tmagic/design';
|
||||||
|
import type { ContainerChangeEventData, FormState, FormValue } from '@tmagic/form';
|
||||||
|
|
||||||
|
import MIcon from '@editor/components/Icon.vue';
|
||||||
|
import type { PropsPanelSlots, Services } from '@editor/type';
|
||||||
|
import { styleTabConfig } from '@editor/utils';
|
||||||
|
|
||||||
|
import FormPanel from './FormPanel.vue';
|
||||||
|
import { useStylePanel } from './use-style-panel';
|
||||||
|
|
||||||
|
defineSlots<PropsPanelSlots>();
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'MEditorPropsPanel',
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
disabledShowSrc?: boolean;
|
||||||
|
extendState?: (state: FormState) => Record<string, any> | Promise<Record<string, any>>;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits(['mounted', 'submit-error', 'form-error']);
|
||||||
|
|
||||||
|
const services = inject<Services>('services');
|
||||||
|
|
||||||
|
const values = ref<FormValue>({});
|
||||||
|
// ts类型应该是FormConfig, 但是打包时会出错,所以暂时用any
|
||||||
|
const curFormConfig = ref<any>([]);
|
||||||
|
const node = computed(() => services?.editorService.get('node'));
|
||||||
|
const nodes = computed(() => services?.editorService.get('nodes') || []);
|
||||||
|
|
||||||
|
const styleFormConfig = [
|
||||||
|
{
|
||||||
|
tabPosition: 'right',
|
||||||
|
items: styleTabConfig.items,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
if (!node.value) {
|
||||||
|
curFormConfig.value = [];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const type = node.value.type || (node.value.items ? 'container' : 'text');
|
||||||
|
curFormConfig.value = (await services?.propsService.getPropsConfig(type)) || [];
|
||||||
|
values.value = node.value;
|
||||||
|
};
|
||||||
|
|
||||||
|
watchEffect(init);
|
||||||
|
services?.propsService.on('props-configs-change', init);
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
services?.propsService.off('props-configs-change', init);
|
||||||
|
});
|
||||||
|
|
||||||
|
const submit = async (v: MNode, eventData?: ContainerChangeEventData) => {
|
||||||
|
try {
|
||||||
|
if (!v.id) {
|
||||||
|
v.id = values.value.id;
|
||||||
|
}
|
||||||
|
services?.editorService.update(v, { changeRecords: eventData?.changeRecords });
|
||||||
|
} catch (e: any) {
|
||||||
|
emit('submit-error', e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const errorHandler = (e: any) => {
|
||||||
|
emit('form-error', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const mountedHandler = (e: any) => {
|
||||||
|
emit('mounted', e);
|
||||||
|
};
|
||||||
|
|
||||||
|
const { showStylePanel, showStylePanelHandler, closeStylePanelHandler } = useStylePanel(services);
|
||||||
|
|
||||||
|
const propertyFormPanelRef = useTemplateRef('propertyFormPanel');
|
||||||
|
defineExpose({
|
||||||
|
getFormState() {
|
||||||
|
return propertyFormPanelRef.value?.configForm?.formState;
|
||||||
|
},
|
||||||
|
submit,
|
||||||
|
});
|
||||||
|
</script>
|
29
packages/editor/src/layouts/props-panel/use-style-panel.ts
Normal file
29
packages/editor/src/layouts/props-panel/use-style-panel.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
import { Protocol } from '@editor/services/storage';
|
||||||
|
import { Services } from '@editor/type';
|
||||||
|
|
||||||
|
export const useStylePanel = (services?: Services) => {
|
||||||
|
const showStylePanelStorageKey = 'props-panel-show-style-panel';
|
||||||
|
const showStylePanelStorageValue = services?.storageService.getItem(showStylePanelStorageKey, {
|
||||||
|
protocol: Protocol.BOOLEAN,
|
||||||
|
});
|
||||||
|
if (typeof showStylePanelStorageValue === 'boolean') {
|
||||||
|
services?.uiService.set('showStylePanel', showStylePanelStorageValue);
|
||||||
|
}
|
||||||
|
const showStylePanel = computed(() => services?.uiService.get('showStylePanel') ?? true);
|
||||||
|
const showStylePanelHandler = () => {
|
||||||
|
services?.uiService.set('showStylePanel', true);
|
||||||
|
services?.storageService.setItem(showStylePanelStorageKey, true, { protocol: Protocol.BOOLEAN });
|
||||||
|
};
|
||||||
|
const closeStylePanelHandler = () => {
|
||||||
|
services?.uiService.set('showStylePanel', false);
|
||||||
|
services?.storageService.setItem(showStylePanelStorageKey, false, { protocol: Protocol.BOOLEAN });
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
showStylePanel,
|
||||||
|
showStylePanelHandler,
|
||||||
|
closeStylePanelHandler,
|
||||||
|
};
|
||||||
|
};
|
@ -82,7 +82,8 @@ export class WebStorage extends BaseService {
|
|||||||
case Protocol.NUMBER:
|
case Protocol.NUMBER:
|
||||||
return Number(item);
|
return Number(item);
|
||||||
case Protocol.BOOLEAN:
|
case Protocol.BOOLEAN:
|
||||||
return Boolean(item);
|
if (item === 'true') return true;
|
||||||
|
if (item === 'false') return false;
|
||||||
default:
|
default:
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ import BaseService from './BaseService';
|
|||||||
const state = reactive<UiState>({
|
const state = reactive<UiState>({
|
||||||
uiSelectMode: false,
|
uiSelectMode: false,
|
||||||
showSrc: false,
|
showSrc: false,
|
||||||
|
showStylePanel: true,
|
||||||
zoom: 1,
|
zoom: 1,
|
||||||
stageContainerRect: {
|
stageContainerRect: {
|
||||||
width: 0,
|
width: 0,
|
||||||
|
@ -12,3 +12,5 @@ $sidebar-heder-background-color: $theme-color;
|
|||||||
$sidebar-content-background-color: #ffffff;
|
$sidebar-content-background-color: #ffffff;
|
||||||
|
|
||||||
$page-bar-height: 32px;
|
$page-bar-height: 32px;
|
||||||
|
|
||||||
|
$props-style-panel-width: 300px;
|
||||||
|
@ -1,5 +1,78 @@
|
|||||||
|
@use "common/var" as *;
|
||||||
|
|
||||||
.m-editor-props-panel {
|
.m-editor-props-panel {
|
||||||
padding: 0 10px 50px 10px;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
--props-style-panel-width: 300px;
|
||||||
|
|
||||||
|
.m-editor-props-form-panel {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.tmagic-design-scrollbar {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-editor-props-property-panel {
|
||||||
|
&.show-style-panel {
|
||||||
|
padding-right: var(--props-style-panel-width);
|
||||||
|
|
||||||
|
.m-editor-props-panel-src-icon {
|
||||||
|
right: calc(15px + var(--props-style-panel-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tmagic-design-form {
|
||||||
|
padding-right: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
> .m-container-tab {
|
||||||
|
> .tmagic-design-tabs {
|
||||||
|
> .el-tabs__content {
|
||||||
|
padding-top: 55px;
|
||||||
|
}
|
||||||
|
> .el-tabs__header.is-top {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-editor-props-style-panel {
|
||||||
|
position: absolute;
|
||||||
|
width: var(--props-style-panel-width);
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
background: #fff;
|
||||||
|
z-index: 12;
|
||||||
|
|
||||||
|
$style-panel-title-height: 38px;
|
||||||
|
|
||||||
|
.tmagic-design-scrollbar {
|
||||||
|
height: calc(100% - $style-panel-title-height - 1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.m-editor-props-style-panel-title {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0 5px;
|
||||||
|
height: $style-panel-title-height;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 2px solid $border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.m-editor-props-panel-src-icon {
|
.m-editor-props-panel-src-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -8,6 +81,13 @@
|
|||||||
z-index: 30;
|
z-index: 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.m-editor-props-panel-style-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
bottom: 60px;
|
||||||
|
z-index: 30;
|
||||||
|
}
|
||||||
|
|
||||||
.m-editor-props-panel-src-code.magic-code-editor {
|
.m-editor-props-panel-src-code.magic-code-editor {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
|
@ -231,6 +231,8 @@ export interface UiState {
|
|||||||
uiSelectMode: boolean;
|
uiSelectMode: boolean;
|
||||||
/** 是否显示整个配置源码, true: 显示, false: 不显示,默认为false */
|
/** 是否显示整个配置源码, true: 显示, false: 不显示,默认为false */
|
||||||
showSrc: boolean;
|
showSrc: boolean;
|
||||||
|
/** 是否将样式配置单独一列显示, true: 显示, false: 不显示,默认为true */
|
||||||
|
showStylePanel: boolean;
|
||||||
/** 画布显示放大倍数,默认为 1 */
|
/** 画布显示放大倍数,默认为 1 */
|
||||||
zoom: number;
|
zoom: number;
|
||||||
/** 画布容器的宽高 */
|
/** 画布容器的宽高 */
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { NODE_CONDS_KEY } from '@tmagic/core';
|
import { NODE_CONDS_KEY } from '@tmagic/core';
|
||||||
|
import { tMagicMessage } from '@tmagic/design';
|
||||||
import type { FormConfig, FormState, TabPaneConfig } from '@tmagic/form';
|
import type { FormConfig, FormState, TabPaneConfig } from '@tmagic/form';
|
||||||
|
|
||||||
export const arrayOptions = [
|
export const arrayOptions = [
|
||||||
@ -41,6 +42,7 @@ export const numberOptions = [
|
|||||||
|
|
||||||
export const styleTabConfig: TabPaneConfig = {
|
export const styleTabConfig: TabPaneConfig = {
|
||||||
title: '样式',
|
title: '样式',
|
||||||
|
display: ({ services }: any) => !(services.uiService.get('showStylePanel') ?? true),
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
name: 'style',
|
name: 'style',
|
||||||
@ -53,6 +55,7 @@ export const styleTabConfig: TabPaneConfig = {
|
|||||||
type: 'data-source-field-select',
|
type: 'data-source-field-select',
|
||||||
name: 'position',
|
name: 'position',
|
||||||
text: '固定定位',
|
text: '固定定位',
|
||||||
|
labelPosition: 'left',
|
||||||
checkStrictly: false,
|
checkStrictly: false,
|
||||||
dataSourceFieldType: ['string'],
|
dataSourceFieldType: ['string'],
|
||||||
fieldConfig: {
|
fieldConfig: {
|
||||||
@ -213,7 +216,7 @@ export const styleTabConfig: TabPaneConfig = {
|
|||||||
checkStrictly: false,
|
checkStrictly: false,
|
||||||
dataSourceFieldType: ['string'],
|
dataSourceFieldType: ['string'],
|
||||||
fieldConfig: {
|
fieldConfig: {
|
||||||
type: 'text',
|
type: 'img-upload',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -344,11 +347,13 @@ export const advancedTabConfig: TabPaneConfig = {
|
|||||||
{
|
{
|
||||||
name: 'created',
|
name: 'created',
|
||||||
text: 'created',
|
text: 'created',
|
||||||
|
labelPosition: 'top',
|
||||||
type: 'code-select',
|
type: 'code-select',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mounted',
|
name: 'mounted',
|
||||||
text: 'mounted',
|
text: 'mounted',
|
||||||
|
labelPosition: 'top',
|
||||||
type: 'code-select',
|
type: 'code-select',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -389,8 +394,21 @@ export const fillConfig = (config: FormConfig = [], labelWidth = '80px'): FormCo
|
|||||||
// 组件id,必须要有
|
// 组件id,必须要有
|
||||||
{
|
{
|
||||||
name: 'id',
|
name: 'id',
|
||||||
type: 'display',
|
text: 'ID',
|
||||||
text: 'id',
|
type: 'text',
|
||||||
|
disabled: true,
|
||||||
|
append: {
|
||||||
|
type: 'button',
|
||||||
|
text: '复制',
|
||||||
|
handler: async (vm, { model }) => {
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(`${model.id}`);
|
||||||
|
tMagicMessage.success('已复制');
|
||||||
|
} catch (err) {
|
||||||
|
tMagicMessage.error('复制失败');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user