diff --git a/packages/ui-vue/components/drawer/src/designer/drawer.design.component.tsx b/packages/ui-vue/components/drawer/src/designer/drawer.design.component.tsx index 8fe3180c455440321b13a93251f0a492d7200f6c..32530dd4364262e7e3a0ba309a7ebf581a37fae1 100644 --- a/packages/ui-vue/components/drawer/src/designer/drawer.design.component.tsx +++ b/packages/ui-vue/components/drawer/src/designer/drawer.design.component.tsx @@ -17,7 +17,7 @@ import { defineComponent, ref, inject, onMounted, Transition, withModifiers, computed, watch, CSSProperties, nextTick } from 'vue'; import { DesignerItemContext, DesignerHostService, useDesignerComponent, FDesignerInnerItem, UseDragula, ComponentSchema } from '@farris/ui-vue/components/designer-canvas'; -import FResponseToolbarDesignComponent, { responseToolbarResolver } from '@farris/ui-vue/components/response-toolbar'; +import { responseToolbarResolver, FResponseToolbarDesign } from '@farris/ui-vue/components/response-toolbar'; import { useDesignerRules } from './use-designer-rules'; import { drawerPropsDesignerProps } from './drawer.design.props'; import './drawer.design.scss'; @@ -32,14 +32,19 @@ export default defineComponent({ /** 可拖拽区域的父容器 */ const containerElementRef = ref(); + const footerToolbarId = props.id + '_footer-toolbar'; + const headerToolbarId = props.id + '_header-toolbar'; + const designerHostService = inject('designer-host-service'); const designItemContext = inject('design-item-context') as DesignerItemContext; const designerRulesComposition = useDesignerRules(designItemContext, containerElementRef, designerHostService); const componentInstance = useDesignerComponent(contextElementRef, designItemContext, designerRulesComposition); - const toolbarSchema = ref(designItemContext.schema.toolbar || { 'type': 'response-toolbar', 'buttons': [] }); + + designItemContext.schema.footerToolbar = designItemContext.schema.footerToolbar || { id: footerToolbarId, 'type':'response-toolbar', 'buttons': [] }; + const toolbarSchema = ref(designItemContext.schema.footerToolbar); const responseToolbarPropsRef = ref(responseToolbarResolver(toolbarSchema.value)); - watch(() => designItemContext.schema.toolbar, () => { - toolbarSchema.value = designItemContext.schema.toolbar || { 'type': 'response-toolbar', 'buttons': [] }; + watch(() => designItemContext.schema.footerToolbar, () => { + toolbarSchema.value = designItemContext.schema.footerToolbar; responseToolbarPropsRef.value = responseToolbarResolver(toolbarSchema.value); }, { deep: true }); const parent = inject('design-item-context'); @@ -48,6 +53,18 @@ export default defineComponent({ return [...responseToolbarPropsRef.value.items]; }); + designItemContext.schema.headerToolbar = designItemContext.schema.headerToolbar || { id: headerToolbarId, 'type': 'response-toolbar', 'buttons': [] }; + const headertoolbarSchema = ref(designItemContext.schema.headerToolbar); + const responseHeaderToolbarPropsRef = ref(responseToolbarResolver(headertoolbarSchema.value)); + watch(() => designItemContext.schema.headerToolbar, () => { + headertoolbarSchema.value = designItemContext.schema.headerToolbar; + responseHeaderToolbarPropsRef.value = responseToolbarResolver(headertoolbarSchema.value); + }, { deep: true }); + const headerToolbars = computed(() => { + return [...responseHeaderToolbarPropsRef.value.items]; + }); + + onMounted(() => { contextElementRef.value.componentInstance = componentInstance; }); @@ -127,19 +144,18 @@ export default defineComponent({ designItemContext.setupContext.emit('selectionChange', schemaType, schemaValue, componentId, componentInstance); } - const renderResponseToolbar = () => { - if (items.value && items.value.length > 0) { - return { + return - + ; - } }; const renderDrawerCloseIcon = () => { @@ -151,17 +167,23 @@ export default defineComponent({ }; const renderDrawerHeader = () => { + if (!props.showHeader) { + return null; + } return ( -
-
+
+ {props.customHeader?
: + <>
{context.slots.title ? context.slots.title() : props.title}
-
-
- {renderResponseToolbar()} - {props.showClose && renderDrawerCloseIcon()} -
+
+ { renderHeaderToolbar(headerToolbarId,headerToolbars.value, headertoolbarSchema.value)} +
+
+ {props.showClose && renderDrawerCloseIcon()} +
+ }
); }; @@ -196,6 +218,7 @@ export default defineComponent({
; }; + const renderDrawerContent = () => (
@@ -205,7 +228,10 @@ export default defineComponent({
{context.slots.default?.()}
- + {props.showFooter &&}
diff --git a/packages/ui-vue/components/drawer/src/designer/drawer.design.props.ts b/packages/ui-vue/components/drawer/src/designer/drawer.design.props.ts index 0b63627747905f9e5883bb11c58127483d52052c..43ab16828d211885e0a72d71497483a29fc32211 100644 --- a/packages/ui-vue/components/drawer/src/designer/drawer.design.props.ts +++ b/packages/ui-vue/components/drawer/src/designer/drawer.design.props.ts @@ -2,6 +2,8 @@ import { ExtractPropTypes } from "vue"; import { drawerProps } from "../drawer.props"; export const drawerPropsDesignerProps = Object.assign({}, drawerProps, { - componentId: { type: String, default: '' } + componentId: { type: String, default: '' }, + footerContentType: { type: String, default: 'toolbar' }, + customHeader: { type: Boolean, default: false }, }); export type DrawerDesignerProps = ExtractPropTypes; diff --git a/packages/ui-vue/components/drawer/src/drawer.component.tsx b/packages/ui-vue/components/drawer/src/drawer.component.tsx index 852ea07294d30503a44e9c7ec907ca33ed10c705..5f23911ed47225445d3e6bdc3eb208c276c59040 100644 --- a/packages/ui-vue/components/drawer/src/drawer.component.tsx +++ b/packages/ui-vue/components/drawer/src/drawer.component.tsx @@ -15,8 +15,10 @@ * limitations under the License. */ -import { computed, defineComponent, ref, SetupContext, Teleport, Transition, watch, withModifiers } from "vue"; +import { computed, defineComponent, Ref, ref, SetupContext, Teleport, Transition, watch, withModifiers } from "vue"; import FButton from '@farris/ui-vue/components/button'; +import FResponseToolbar from '@farris/ui-vue/components/response-toolbar'; + import { DrawerButton, DrawerProps, drawerProps } from "./drawer.props"; import './drawer.css'; import { useDrawerLocale } from "./locale/locale"; @@ -24,19 +26,12 @@ import { useDrawerLocale } from "./locale/locale"; export default defineComponent({ name: 'FDrawer', props: drawerProps, - emits: ['afterClose', 'update:modelValue'] as (string[] & ThisType) | undefined, + emits: ['afterClose', 'update:modelValue', 'click'] as (string[] & ThisType) | undefined, setup(props: DrawerProps, context: SetupContext) { const { drawerLocale } = useDrawerLocale(); const modelValue = ref(props.modelValue); const buttons = ref(props.buttons); - // const host = computed(() => { - // if (typeof props.host === 'string') { - // const container = document.querySelector(props.host); - // return container; - // } - // return props.host; - // }); const drawerContainerClass = computed(() => { const showNotInBody = typeof props.host === 'string' ? props.host !== 'body' : document.querySelector(props.host) !== document.body; @@ -47,11 +42,6 @@ export default defineComponent({ }; }); - const wrapperClassObject = computed(() => { - return { - - }; - }); const maskClass = computed(() => { const customMaskClass = {}; @@ -106,11 +96,6 @@ export default defineComponent({ context.emit('afterClose'); } }); - - }; - - const onConfirm = (e: MouseEvent) => { - onClose(e); }; watch(() => props.modelValue, (newModelValue: boolean, oldModelValue: boolean) => { @@ -119,6 +104,10 @@ export default defineComponent({ } }); + const onClickToolbarItem = (payload: any, itemId: string) => { + context.emit('click', payload, itemId); + }; + const buttonClickParams = { close: () => { modelValue.value = false; @@ -134,54 +123,80 @@ export default defineComponent({ } } - const getFooterButtons = () => { - if (!buttons.value || !buttons.value.length) { - return [ - { id: 'drawer-footer-cancel', text: drawerLocale.cancel, class: 'btn btn-secondary mr-2', handle: onClose }, - { id: 'drawer-footer-confirm', text: drawerLocale.confirm, class: 'btn btn-primary', handle: onConfirm } - ]; - } - return buttons.value; + + const renderToolbar = (buttons, toolbarCustomClass: Record) => { + return ; }; function renderFooterButtons() { - - const footerButtons = getFooterButtons(); - return <>{ - footerButtons && footerButtons.map((button: DrawerButton) => { - const isDisabled = ref(button.disabled); - return ( - - ); - } - ) + if (props.footerToolbar?.buttons?.length) { + const customClass = {'f-utils-fill': true}; + if (props.footerToolbar?.appearance?.class) { + customClass[props.footerToolbar.appearance.class] = true; + } + return renderToolbar(props.footerToolbar.buttons, customClass); } - ; + return buttons.value && !!buttons.value.length && buttons.value.map((button: DrawerButton) => { + const isDisabled = ref(button.disabled); + return ( + + ); + }); } function renderFooter() { return context.slots.footerTemplate? context.slots.footerTemplate() : renderFooterButtons(); } + function renderHeader() { + const customClass = {'f-utils-fill': true}; + if (props.headerToolbar?.appearance?.class) { + customClass[props.headerToolbar.appearance.class] = true; + } + + return context.slots.headerTemplate? context.slots.headerTemplate() : <> +
+ {props.title} +
+ { props.headerToolbar &&
+ { renderToolbar(props.headerToolbar.buttons || [], customClass)} +
} + {props.showClose && +
+ { + onClose(e as MouseEvent); + }, ['stop'])}> +
+ } + ; + } + + context.expose({ + open: () => { modelValue.value = true; }, + close: () => { modelValue.value = false; } + }); + return () => { return ( -
+ {
{modelValue.value &&
{ props.allowClickMaskToClose && onClose(e as MouseEvent); }, ['stop'])}>
}
-
- {props.showHeader &&
-
- {context.slots.headerTemplate ? context.slots.headerTemplate() : props.title} -
- {props.showClose && -
- { - onClose(e as MouseEvent); - }, ['stop'])}> -
- } + {props.showHeader &&
+ {renderHeader()}
}
{context.slots.content?.()}
- {props.showFooter &&
-
+
} ); }; diff --git a/packages/ui-vue/components/drawer/src/drawer.css b/packages/ui-vue/components/drawer/src/drawer.css index 11acd12529f3a1de5c5d391337c0231569dc3508..d2235161a74e21897cce647bbf9485a49a69ab10 100644 --- a/packages/ui-vue/components/drawer/src/drawer.css +++ b/packages/ui-vue/components/drawer/src/drawer.css @@ -18,8 +18,32 @@ display: flex; padding: 16px 24px; justify-content: space-between; + position: relative; } +.f-drawer-title { + flex: 1 1 auto; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #000; + font-size: 1.125rem; +} + +.f-drawer-title::before{ + content: ''; + width: .25rem; + height: 1rem; + background: #2A87FF; + border-radius: 2px; + position: absolute; + top: 50%; + left: 10px; + margin-top: -.5rem; +} + + .f-drawer-close:hover { cursor: pointer; } diff --git a/packages/ui-vue/components/drawer/src/drawer.props.ts b/packages/ui-vue/components/drawer/src/drawer.props.ts index ae3dbc1d0b8687f7592dfb93b074e4587b96f337..6e16fcb78948d10527a04fb763f4404de17b8549 100644 --- a/packages/ui-vue/components/drawer/src/drawer.props.ts +++ b/packages/ui-vue/components/drawer/src/drawer.props.ts @@ -27,6 +27,7 @@ export interface DrawerButton { } export const drawerProps = { + id: { type: String, required: true }, /** 背景色 */ backgroundColor: { type: String, default: '#fff' }, /** @@ -67,7 +68,12 @@ export const drawerProps = { host: { type: Object as PropType, default: 'body' }, showHeader: { type: Boolean, default: true }, + headerToolbar:{ type: Object as PropType, default: {} }, + headerTemplate:{ type: Object as PropType, default: null }, showFooter: { type: Boolean, default: true }, + footerTemplate: { type: Object as PropType, default: null }, + footerToolbar:{ type: Object as PropType, default: {} }, + footerHeight: { type: Number, default: 60 }, buttons: { type: Array, default: [] } } as Record; diff --git a/packages/ui-vue/components/drawer/src/property-config/drawer.property-config.ts b/packages/ui-vue/components/drawer/src/property-config/drawer.property-config.ts index 7a2800654955e58bf0cec35cc3c9cadb07e5cc95..076a6ab2458700a8446299f4f055ec9054db06ca 100644 --- a/packages/ui-vue/components/drawer/src/property-config/drawer.property-config.ts +++ b/packages/ui-vue/components/drawer/src/property-config/drawer.property-config.ts @@ -12,19 +12,143 @@ export class DrawerProperty extends BaseControlProperty { this.getBehaviorConfig(); + this.propertyConfig.categories['header'] = this.headerPropertyConfig(propertyData); + this.propertyConfig.categories['footer'] = this.footerPropertyConfig(propertyData); + return this.propertyConfig; } - getBehaviorConfig() { - this.propertyConfig.categories['behavior'] = { - title: "行为", + private headerPropertyConfig(propertyData) { + return { + title: "页头", description: "Behavior", properties: { + showHeader: { + title: "显示页头", + description: '是否显示页头', + type: 'boolean', + refreshPanelAfterChanged: true, + }, + customHeader:{ + title: "自定义页头", + description: '是否自定义页头', + type: 'boolean', + refreshPanelAfterChanged: true, + visible: propertyData.showHeader !== false + }, + showClose: { + title: "显示关闭按钮", + description: '是否显示关闭按钮', + type: 'boolean', + visible: propertyData.showHeader !== false && !propertyData.customHeader + }, title: { title: "标题", description: '标题', - type: 'string' + type: 'string', + visible: propertyData.showHeader !== false && !propertyData.customHeader + }, + headerTemplate: { + title: "页头模板", + type:'string', + description: '设置标题HTML模板,替代整个页头区域', + editor: { + type: "code-editor", + language: "html", + }, + visible: propertyData.showHeader !== false && propertyData.customHeader + } + }, + setPropertyRelates: (changeObject, prop) => { + if (!changeObject) { + return; + } + switch (changeObject && changeObject.propertyID) { + case 'title': { + changeObject.needRefreshControlTree = true; + break; + } + } + } + }; + } + + private footerPropertyConfig(propertyData) { + return { + title: "页脚", + description: "Behavior", + properties: { + showFooter: { + title: "显示页脚", + description: '是否显示页脚', + type: 'boolean', + default: true, + refreshPanelAfterChanged: true, + }, + footerHeight:{ + title: "页脚高度", + description: '页脚高度', + type: 'number', + editor: { + type: "number-spinner", + useThousands: false, + min: 30, + max: 1999 + }, + visible: propertyData.showFooter !== false + }, + footerContentType: { + title: "页脚内容类型", + description: '页脚内容类型', + type: 'select', + refreshPanelAfterChanged: true, + editor: { + type: 'combo-list', + textField: 'name', + valueField: 'value', + idField: 'value', + editable: false, + data: [{ value: 'toolbar', name: '工具栏'}, { value: 'html', name: '自定义模板' }, ] + }, + visible: propertyData.showFooter !== false }, + footerTemplate: { + title: "页脚模板", + type: 'string', + description: '设置标题HTML模板,替代图标和标题文字区域', + editor: { + type: "code-editor", + language: "html", + }, + visible: propertyData.footerContentType === 'html' && propertyData.showFooter !== false + } + }, + setPropertyRelates: (changeObject, prop) => { + if (!changeObject) { + return; + } + switch (changeObject && changeObject.propertyID) { + case 'showFooter': { + prop.footerToolbar = { id: prop.id + '_footer-toolbar', 'type': 'response-toolbar', 'buttons': [] }; + prop.footerTemplate = ''; + prop.footerContentType = 'toolbar'; + break; + } + case 'footerContentType': { + this.resetFooterContent(prop, changeObject.propertyValue); + break; + } + } + } + }; + } + + getBehaviorConfig() { + this.propertyConfig.categories['behavior'] = { + title: "行为", + description: "Behavior", + properties: { + position: { title: "显示位置", description: '抽屉显示位置', @@ -44,7 +168,13 @@ export class DrawerProperty extends BaseControlProperty { width: { title: "宽度", description: '抽屉宽度', - type: 'number' + type: 'number', + editor: { + type: "number-spinner", + useThousands: false, + min: 30, + max: 1999 + }, }, showMask: { title: "显示遮罩", @@ -53,25 +183,21 @@ export class DrawerProperty extends BaseControlProperty { default: true, visible: false }, - showFooter: { - title: "显示底部", - description: '是否显示底部', - type: 'boolean', - default: true - } - }, - setPropertyRelates(changeObject, prop) { - if (!changeObject) { - return; - } - switch (changeObject && changeObject.propertyID) { - case 'title': { - changeObject.needRefreshControlTree = true; - break; - } - } } }; } + + private resetFooterContent(propertyData, contentType = 'toolbar') { + switch (contentType) { + case 'toolbar': { + propertyData.footerTemplate = ''; + break; + } + case 'html': { + propertyData.footerToolbar = { id: propertyData.id + '_footer-toolbar', 'type': 'response-toolbar', 'buttons': [] }; + break; + } + } + } } diff --git a/packages/ui-vue/components/drawer/src/schema/drawer.schema.json b/packages/ui-vue/components/drawer/src/schema/drawer.schema.json index f6c1fa8f67099b1a0539574df1ef87efbfe5af00..10f4cdfd506a79c3cb42fbdf6ff8e7b76eb9ddf6 100644 --- a/packages/ui-vue/components/drawer/src/schema/drawer.schema.json +++ b/packages/ui-vue/components/drawer/src/schema/drawer.schema.json @@ -45,7 +45,7 @@ "width": { "description": "", "type": "number", - "default": 300 + "default": 500 }, "showMask": { "description": "", @@ -57,6 +57,55 @@ "type": "boolean", "default": true }, + "showClose": { + "description": "", + "type": "boolean", + "default": true + }, + "customHeader": { + "description": "", + "type": "boolean", + "default": false + }, + "headerTemplate": { + "description": "", + "type": "object" + }, + "headerToolbar": { + "description": "", + "type": "object", + "properties": { + "appearance": { + "description": "", + "type": "object", + "properties": { + "class": { + "type": "string", + "default": "" + } + } + }, + "id": { + "description": "", + "type": "string" + }, + "type": { + "description": "", + "type": "string", + "default": "response-toolbar" + }, + "alignment": { + "description": "The alignment of Response Toolbar Button.", + "type": "string", + "default": "right" + }, + "buttons": { + "description": "The items of Response Toolbar.", + "type": "array", + "default": [] + } + } + }, "showFooter": { "description": "", "type": "boolean", @@ -67,7 +116,7 @@ "type": "boolean", "default": false }, - "toolbar": { + "footerToolbar": { "description": "", "type": "object", "properties": { @@ -77,7 +126,7 @@ "properties": { "class": { "type": "string", - "default": "col-3" + "default": "" } } }, @@ -106,6 +155,20 @@ "description": "", "type": "array", "default": [] + }, + "footerTemplate": { + "description": "", + "type": "object" + }, + "footerContentType": { + "description": "", + "type": "string", + "default": "toolbar" + }, + "footerHeight": { + "description": "", + "type": "number", + "default": 60 } }, "required": [ diff --git a/packages/ui-vue/components/response-toolbar/index.ts b/packages/ui-vue/components/response-toolbar/index.ts index e4e515aa14853d2f7e58e544631daaa69d1dd7df..e9f8e03d98e572f8e9b23b3bcfe1bbe6fb46d8e3 100644 --- a/packages/ui-vue/components/response-toolbar/index.ts +++ b/packages/ui-vue/components/response-toolbar/index.ts @@ -21,5 +21,5 @@ FResponseToolbar.registerDesigner = (componentMap: Record, propsRes componentMap['response-toolbar-item'] = FResponseToolbarItemDesign; propsResolverMap['response-toolbar-item'] = itemPropsResolver; }; -export { FResponseToolbar, ResponseToolbarDropDownItem, ResponseToolbarGroup, ResponseToolbarItem, responseToolbarResolver }; +export { FResponseToolbar, ResponseToolbarDropDownItem, ResponseToolbarGroup, ResponseToolbarItem, responseToolbarResolver , FResponseToolbarDesign}; export default withInstall(FResponseToolbar);