diff --git a/components-diy/diy-form.vue b/components-diy/diy-form.vue index 5bdadbc..043a366 100644 --- a/components-diy/diy-form.vue +++ b/components-diy/diy-form.vue @@ -6,24 +6,44 @@ 否则会触发 MP 编译器注入的 propsToData 观察者(对应编译产物里 r=function(){var o=this.value.show_banner...this.$mp.data...}) 报 "Cannot read properties of undefined (reading 'show_banner')"。 --> - + - - {{ value && value.form_tip }} - - - - - {{ submitText }} + + + + {{ value && value.form_tip }} + + + + + {{ submitText }} + + + @@ -58,6 +78,14 @@ export default { type: Boolean, default: true }, + // 父级容器左右内边距(rpx)。卡片模式下让 banner 图片突破这个内边距左右铺满(如 system-form-wrap 的 padding-x)。 + // 仅在 hasBannerMode=true 时生效;默认 0(不铺满)。 + // 引入原因:formdata.vue 的 .system-form-wrap 默认 padding: 0 50rpx,banner 图片被左右各 50rpx 收缩, + // 后台配 form_banner 后图片应跨越屏幕宽度,而不是被容器内缩。 + containerInset: { + type: Number, + default: 0 + }, // 父级可选地接管提交:传入函数 prop 时,verify 通过后由它处理 formData; // 不传时走组件内置 defaultSubmit(兜底)。default 必须是 null,不能写成函数返回 null onSubmit: { @@ -108,6 +136,52 @@ export default { padding: `0 ${this._num(this.mergedFormConfig && this.mergedFormConfig.bannerPaddingX, 0)}rpx` }); }, + // 后台配了 banner 图片时启用卡片浮起布局(与参考设计左图一致);无 banner 走原平铺,零外溢 + hasBannerMode() { + const v = this.value || {}; + return !!(v.show_banner && v.form_banner); + }, + // 卡片容器内联样式:maring-top(负偏移让卡片向上压住 banner 底部)+ border-radius + padding-x + box-shadow + background。 + // 数值由 formConfig 走 camelCase/snake_case 双读,缺省走默认值,使后端零配置时也能看到卡片浮起效果。 + // 关键:卡片必须自带白底 + z-index(见 SCSS .form-card)—— 只有卡片自己的背景才能"盖住"banner 图片底部, + // 父级容器(如 formdata.vue 的 .system-form-wrap)是 banner 的祖先,其背景会被 banner 覆盖,无法形成浮起效果。 + cardWrapStyle() { + const cfg = this.mergedFormConfig || {}; + // 父级传了 containerInset(即 formdata.vue 场景),外层 system-form-wrap 已给左右 padding-x,这里默认 0 不重复 + // 其他场景(diy-group / diy-article 等未传 containerInset),回退到 bannerPaddingX 或 28rpx 保证卡片模式视觉正常 + const parentInset = this._num(this.containerInset, 0); + const defaultSidePad = parentInset > 0 ? 0 : this._num(cfg.bannerPaddingX, 28); + const sidePad = this._num(cfg.cardSidePad, defaultSidePad); + // overlap:负值,让卡片向上"压"住 banner 底部(与设计图一致) + const overlap = -Math.abs(this._num(cfg.cardOverlap, 48)); + // 卡片圆角:缺省 24rpx(浮起卡片的圆角,比外层卡片的 16rpx 更明显,突出浮起感) + const radius = this._num(cfg.cardRadius, 24); + // 阴影:缺省中等阴影(与设计方案一致)—— 由本容器承担浮起感 + const shadow = cfg.cardShadow || '0 8rpx 24rpx rgba(0,0,0,0.08)'; + // 卡片底色:纯白,与设计方案一致;可由后台 formConfig.cardBgColor 覆盖 + const bgColor = cfg.cardBgColor || '#ffffff'; + return this.$util.objToStyle({ + marginTop: overlap + 'rpx', + paddingLeft: sidePad + 'rpx', + paddingRight: sidePad + 'rpx', + borderRadius: radius + 'rpx', + boxShadow: shadow, + backgroundColor: bgColor + }); + }, + // Banner 容器破壳样式:仅在卡片模式下 + 父级传了 containerInset>0 时启用,让 banner 突破父容器 padding 跨越屏幕宽度。 + // 计算逻辑:负 margin-left/right + 等量扩宽 width,使 banner 元素左/右边界对齐到屏幕边缘。 + // 父级未传 containerInset 或传 0 时返回空样式,banner 保持原尺寸(零外溢)。 + bannerBleedStyle() { + const inset = this._num(this.containerInset, 0); + if (!this.hasBannerMode || inset <= 0) return ''; + const expand = inset * 2; + return this.$util.objToStyle({ + marginLeft: -inset + 'rpx', + marginRight: -inset + 'rpx', + width: 'calc(100% + ' + expand + 'rpx)' + }); + }, // 提示语样式:padding-x、font-size、color、bg-color、border-bottom(与后端 .form-preview-tip 对齐) tipStyle() { const cfg = this.mergedFormConfig || {}; @@ -229,7 +303,13 @@ export default { tipFontSize: pick('tipFontSize', 'tip_font_size'), tipColor: pick('tipColor', 'tip_color'), tipBgColor: pick('tipBgColor', 'tip_bg_color'), - tipBorderColor: pick('tipBorderColor', 'tip_border_color') + tipBorderColor: pick('tipBorderColor', 'tip_border_color'), + // 卡片浮起(仅 show_banner && form_banner 启用) + cardOverlap: pick('cardOverlap', 'card_overlap'), + cardRadius: pick('cardRadius', 'card_radius'), + cardShadow: pick('cardShadow', 'card_shadow'), + cardSidePad: pick('cardSidePad', 'card_side_pad'), + cardBgColor: pick('cardBgColor', 'card_bg_color') }; // 移除 undefined / 空字符串(保留 0/false) Object.keys(base).forEach(function(k) { @@ -392,12 +472,37 @@ export default { // 默认左右无边距(与后端 --banner-padding-x 对齐;由内联 bannerStyle 覆盖) margin: 0; + // banner 图片作为卡片上方的"底图":建立层叠上下文,让 .form-card 通过 z-index 浮在其上。 + // 仅在卡片浮起模式(.is-card-overlap)启用,不影响其它使用方(diy-group 等)的旧布局。 + &.is-card-overlap { + position: relative; + z-index: 1; + } + .banner-img { width: 100%; display: block; } } + // 卡片容器:浮在 banner 图片上方(参考设计左图)。 + // 仅在 hasBannerMode=true 时渲染(见 template v-if); + // 阴影 + margin-top(负偏移)+ 圆角 + 白底 全部由 cardWrapStyle 内联写入(读取后台 formConfig.card*)。 + // 关键:卡片必须自带白底并 z-index 高于 banner,才能真正"盖住"banner 图片底部形成浮起; + // 父级容器(formdata.vue 的 .system-form-wrap)虽也是白底,但它是 banner 的祖先、背景会被 banner 覆盖, + // 因此本容器的白底不可省略(与 .system-form-wrap 同色,视觉上从卡片向下自然延续到页面底部)。 + .form-card { + // 定位上下文 + 高于 banner 的层级:让卡片盖住 banner 底部(banner z-index:1) + position: relative; + z-index: 2; + // 兜底白底 / 圆角 / 阴影:与后端未配置时内联写入的默认值一致;后台配置时由内联 cardWrapStyle 覆盖 + background-color: #ffffff; + border-radius: 24rpx; + box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08); + // 兜底 padding-bottom:让卡片与后面内容(如推荐码、其它 diy 组件)有间距 + padding-bottom: 24rpx; + } + .form-tip { // 提示语:上下内边距 / 左右内边距 / 字号 / 颜色 / 背景 全部由 tipStyle 内联写入 padding: 0; diff --git a/components-diy/diy-member-my-order.vue b/components-diy/diy-member-my-order.vue index 6f6afcf..acfd66e 100644 --- a/components-diy/diy-member-my-order.vue +++ b/components-diy/diy-member-my-order.vue @@ -137,8 +137,8 @@ export default { }, computed: { warpCss() { - var obj = ''; - obj += 'background-color:' + this.value.componentBgColor + ';'; + var obj = ''; + obj += 'background-color:' + this.value.componentBgColor + ';'; if (this.value.componentAngle == 'round') { obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;'; obj += 'border-top-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;'; diff --git a/components-diy/diy-recommend-code.vue b/components-diy/diy-recommend-code.vue index d3c4b15..506f850 100644 --- a/components-diy/diy-recommend-code.vue +++ b/components-diy/diy-recommend-code.vue @@ -120,7 +120,7 @@ - - + 📤 diff --git a/pages_tool/form/formdata.vue b/pages_tool/form/formdata.vue index fd5345c..a79c0cf 100644 --- a/pages_tool/form/formdata.vue +++ b/pages_tool/form/formdata.vue @@ -5,6 +5,10 @@ + + - - + + @@ -56,12 +60,12 @@ - + - + @@ -210,12 +214,60 @@ export default { return { jsonData: this.detail.json_data, formConfig: Object.assign({}, this.detail.form_config || {}, styleDefaults), - show_banner: this.detail.show_banner, + // 全屏底图模式下隐藏卡片内 banner,避免与底图重复(见 hideInnerBanner) + show_banner: this.hideInnerBanner ? 0 : this.detail.show_banner, form_banner: this.detail.form_banner || 'public/uniapp/form/banner.png', show_tip: this.detail.show_tip, form_tip: this.detail.form_tip || this.$lang('form.formdata.formTipPlaceholder') } }, + // ===== 容器左右内缩(默认开启,不依赖 bgImage) ===== + // 图条图片:后台 form_config.bg_image / bgImage 下发(兼容 snake_case / camelCase)。 + // 未配置时不渲染图条,但表单卡片本身已有内缩效果(见 .system-form-wrap 默认 padding) + bgImage() { + if (!this.detail) return ''; + const fc = this.detail.form_config || {}; + const raw = fc.bgImage || fc.bg_image || fc.backgroundImage || fc.background_image || ''; + return raw ? this.$util.img(raw) : ''; + }, + // 容器左右内缩(rpx)。后台 form_config.container_inset / containerInset 可调; + // 默认 50rpx,在 750rpx 屏宽下左右各留 50rpx ≈ 6.7%,内容宽 ≈ 87%, + // 与参考图一致;CSS .system-form-wrap 中也写了这个默认 padding(防 H5 computed 未及时就绪时也生效) + containerInset() { + const fc = (this.detail && this.detail.form_config) || {}; + const raw = fc.containerInset !== undefined ? fc.containerInset : fc.container_inset; + const n = parseInt(raw); + return isNaN(n) ? 50 : Math.min(150, Math.max(0, n)); + }, + // 顶部图条高度(rpx)。后台 form_config.bg_image_height 可调;clamp 80-600 + bgImageHeight() { + const fc = (this.detail && this.detail.form_config) || {}; + const raw = fc.bgImageHeight !== undefined ? fc.bgImageHeight : fc.bg_image_height; + const n = parseInt(raw); + return isNaN(n) ? 200 : Math.min(600, Math.max(80, n)); + }, + // 图条与表单卡片之间的间距(rpx)。后台 form_config.card_gap 可调;clamp 0-80 + cardGap() { + const fc = (this.detail && this.detail.form_config) || {}; + const raw = fc.cardGap !== undefined ? fc.cardGap : fc.card_gap; + const n = parseInt(raw); + return isNaN(n) ? 24 : Math.min(80, Math.max(0, n)); + }, + // 图条内联样式:水平居中(width=100%-2*inset + 左右各 inset 边距),高度由配置控制 + bgLayerStyle() { + const inset = this.containerInset; + return 'width:calc(100% - ' + (inset * 2) + 'rpx);margin:0 ' + inset + 'rpx;height:' + this.bgImageHeight + 'rpx;background-image:url(' + this.bgImage + ');'; + }, + // 表单容器内联样式:覆盖 .system-form-wrap 默认 padding,使后台可配。始终返回有效样式(含默认值), + // 不再依赖 bgImage 开关 —— 表单默认就有左右内缩居中(不需后台任何配置) + containerStyle() { + return 'padding-left:' + this.containerInset + 'rpx;padding-right:' + this.containerInset + 'rpx;'; + }, + // 图条模式下隐藏 diy-form 卡片顶部的 banner,避免与图条重复 + // 如需保留卡片内 banner:把下面这行改成 `return false;` 即可 + hideInnerBanner() { + return !!this.bgImage; + }, // 行为设置:提交验证码开启时 diy-form 隐藏内置提交按钮,本页渲染验证码 + 自有提交按钮 showCaptcha() { return this.behavior.captcha == 1; @@ -905,16 +957,40 @@ page { // ===== 非鸿蒙端(H5 / 微信小程序 / App):保留原有 WebKit 滚动 / 安全区处理 ===== // 鸿蒙走下方 MP-HARMONY 专属的 scroll-view 方案,不编译这些 WebKit 属性 +/* + * ===== 顶部图条 ===== + * 位于 navbar 之下、表单卡片之上。宽度由内联 bgLayerStyle 注入(calc(100% - 2*inset)), + * 与下方表单卡片等宽、左右各留 inset 对称留白。圆角 + overflow 防止图片溢出。 + * 未配置 form_config.bg_image 时该节点不渲染(v-if="bgImage"),页面保持原来的纯白样式。 + */ +.formdata-bg { + position: relative; + background-size: cover; + background-position: center; + background-repeat: no-repeat; + border-radius: 16rpx; + overflow: hidden; + box-sizing: border-box; +} + /* #ifdef H5 || MP-WEIXIN || APP-PLUS */ .page { min-height: 100vh; overflow-y: auto; -webkit-overflow-scrolling: touch; box-sizing: border-box; + /* 页面容器不再垫白底 —— 只有红色框区域(.system-form-wrap 表单卡片)保留白底, + 图条上下、左右留白等区域透出页面默认底色 */ } .system-form-wrap { overflow: visible; margin: 0 0 60rpx 0; + /* ===== 表单卡片:唯一保留白底的区域 ===== + 87% 宽居中 + 16rpx 圆角 + 白底,对应红色框范围(从图条底部到页面底部) */ + padding: 0 50rpx; + box-sizing: border-box; + background-color: #ffffff; + border-radius: 16rpx; padding-bottom: env(safe-area-inset-bottom, 40rpx); } /* #endif */ @@ -934,6 +1010,7 @@ page { min-height: 0; // 允许内容收缩,内部 scroll-view 才能拿到确定高度并正确滚动 display: flex; flex-direction: column; + /* 鸿蒙端与 .page 保持一致:去掉白底,只有 .system-form-wrap 是白底卡片 */ } .form-scroll { flex: 1; @@ -943,10 +1020,23 @@ page { // 不再用 overflow:hidden 裁剪;提交按钮在 scroll-view 内,滚动可见 overflow: visible; margin: 0 0 60rpx 0; + // ===== 表单卡片:默认就是「白底圆角、左右内缩居中」(参考图效果),不依赖后台配置 ===== + padding: 0 50rpx; + box-sizing: border-box; + background-color: #ffffff; + border-radius: 16rpx; padding-bottom: 40rpx; // 鸿蒙不识别 env(safe-area-inset-bottom),固定值兜底 } /* #endif */ +/* + * ===== .form-card 兼容占位(旧模板/旧调用残留) ===== + * 旧实现用 .form-card 做条件类加强化,现已将白底/圆角合并到 .system-form-wrap 默认样式中。 + * 保留此类名是为了不破坏潜在的 :class="formCardClass" 引用(已从模板移除,但旧页面缓存可能用到); + * 这里只做 noop,不影响任何视觉。 + */ +.form-card {} + // 穿透 diy-form 包装层:消除其自带的 10rpx 圆角与白色背景, // 让 form 内容区域保持与原始 ns-newform 接入时一致的视觉 .system-form-wrap ::v-deep .diy-from {