refactor(ns-newform.vue): 优化字段样式,支持组件独立的间距和内边距设置

This commit is contained in:
2026-09-04 09:13:25 +08:00
parent fc5c7e682c
commit 11cc4f6319

View File

@@ -2,7 +2,7 @@
<view class="form-wrap form-component" :style="formWrapStyle" >
<view v-for="(item, index) in formData" :key="index">
<!-- 文本输入框 -->
<view v-if="item.controller == 'Text'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Text'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', { 'vertical-layout': mode === 'vertical' }]" :style="rowCellStyle">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -20,7 +20,7 @@
</view>
<!-- 多行文本输入框 -->
<view v-if="item.controller == 'Textarea'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Textarea'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', 'flex-box', 'textarea', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -43,7 +43,7 @@
</view>
<!-- 下拉选择器 -->
<view v-if="item.controller == 'Select'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Select'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<picker
mode="selector"
:range="item.value.options"
@@ -64,7 +64,7 @@
</view>
<!-- 复选框 -->
<view v-if="item.controller == 'Checkbox'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Checkbox'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -93,7 +93,7 @@
</view>
<!-- 单选框 -->
<view v-if="item.controller == 'Radio'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Radio'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -122,7 +122,7 @@
</view>
<!-- 图片上传 -->
<view v-if="item.controller == 'Img'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Img'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', 'flex-box', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -141,7 +141,7 @@
</view>
<!-- 日期选择器 -->
<view v-if="item.controller == 'Date'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Date'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -163,7 +163,7 @@
</view>
<!-- 日期范围选择器 -->
<view v-if="item.controller == 'Datelimit'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Datelimit'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', 'flex-box', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -202,7 +202,7 @@
</view>
<!-- 时间选择器 -->
<view v-if="item.controller == 'Time'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Time'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -224,7 +224,7 @@
</view>
<!-- 时间范围选择器 -->
<view v-if="item.controller == 'Timelimit'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'Timelimit'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', 'flex-box', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -263,7 +263,7 @@
</view>
<!-- 城市选择器 -->
<view v-if="item.controller == 'City'" class="order-wrap" :class="rowWrapClass" :style="rowWrapStyle">
<view v-if="item.controller == 'City'" class="order-wrap" :class="rowWrapClass" :style="item._fieldWrapStyle">
<view :class="['order-cell', { 'vertical-layout': mode === 'vertical' }]">
<view class="name" :style="item._labelStyle">
<text class="required" :style="item._starStyle">{{ item.value.required ? '*' : '' }}</text>
@@ -503,12 +503,6 @@ export default {
'--show-row-divider': cfg.showRowDivider === false ? '0' : '1'
});
},
// 行间距样式:仅上下内边距,水平由外层 form-wrap 控制(与后端 form-gorup 对齐)
rowWrapStyle() {
return this.$util.objToStyle({
padding: `${this.$util.rpx(this.config.rowPaddingY)} 0`
});
},
// 行容器 class:用于控制分割线显隐
rowWrapClass() {
return this.config.showRowDivider === false ? 'order-wrap no-divider' : 'order-wrap';
@@ -593,9 +587,41 @@ export default {
const widthStyle = this.nameWidthStyle || {};
const nameMarginStyle = this.nameStyle || {};
// 字段级间距(每个组件独立,互不影响):数值解析(''/null/undefined/非数字 → null = 未设置,跟随全局)
const spNum = (x) => {
if (x === undefined || x === null || x === '') return null;
const n = parseFloat(x);
return isNaN(n) ? null : n;
};
// 行容器基础上下内边距(原 rowWrapStyle 语义:全局行间距 rowPaddingY;左右恒 0)
const rowPadY = this.$util.rpx(this.config.rowPaddingY);
const clampRpx = (n, min, max) => this.$util.rpx(Math.max(min, Math.min(max, n)));
this.formData.forEach((item) => {
const itemValue = (item && item.value) || {};
// ---- 字段级间距(每个组件独立):外边距直出(允许负值上移贴近相邻组件)----
// clamp 与后端 fieldSpacingStyle 一致(margin -100..400 / padding 0..400 rpx)
const mTop = spNum(itemValue.margin_top), mBottom = spNum(itemValue.margin_bottom),
mLeft = spNum(itemValue.margin_left), mRight = spNum(itemValue.margin_right);
const fieldMargin = {};
if (mTop !== null) fieldMargin['margin-top'] = clampRpx(mTop, -100, 400);
if (mBottom !== null) fieldMargin['margin-bottom'] = clampRpx(mBottom, -100, 400);
if (mLeft !== null) fieldMargin['margin-left'] = clampRpx(mLeft, -100, 400);
if (mRight !== null) fieldMargin['margin-right'] = clampRpx(mRight, -100, 400);
const pTop = spNum(itemValue.padding_top), pBottom = spNum(itemValue.padding_bottom),
pLeft = spNum(itemValue.padding_left), pRight = spNum(itemValue.padding_right);
// 11 种输入控件行容器(.order-wrap,原 rowWrapStyle 承载层):
// margin 直出;padding 覆盖对应边(未设置的边跟随全局:上下 rowPaddingY / 左右 0)
if (item.controller != 'InfoCard') {
const fieldWrapStyle = Object.assign({}, fieldMargin);
fieldWrapStyle['padding-top'] = pTop !== null ? clampRpx(pTop, 0, 400) : rowPadY;
fieldWrapStyle['padding-bottom'] = pBottom !== null ? clampRpx(pBottom, 0, 400) : rowPadY;
fieldWrapStyle['padding-left'] = pLeft !== null ? clampRpx(pLeft, 0, 400) : '0';
fieldWrapStyle['padding-right'] = pRight !== null ? clampRpx(pRight, 0, 400) : '0';
this.$set(item, '_fieldWrapStyle', fieldWrapStyle);
}
const style = Object.assign({}, widthStyle);
// item 级别 label 字号 / 颜色覆盖(与原 getItemLabelStyle 逻辑一致)
@@ -662,6 +688,13 @@ export default {
wrapStyle['border-radius'] = '6px';
wrapStyle['padding'] = '8px 10px';
}
// 字段级间距(InfoCard 行容器即卡片本身,无全局行内边距):margin 直出;
// padding 覆盖背景卡默认内边距的对应边(无 bg 时直接生效)
Object.keys(fieldMargin).forEach((k) => { wrapStyle[k] = fieldMargin[k]; });
if (pTop !== null) wrapStyle['padding-top'] = clampRpx(pTop, 0, 400);
if (pBottom !== null) wrapStyle['padding-bottom'] = clampRpx(pBottom, 0, 400);
if (pLeft !== null) wrapStyle['padding-left'] = clampRpx(pLeft, 0, 400);
if (pRight !== null) wrapStyle['padding-right'] = clampRpx(pRight, 0, 400);
this.$set(item, '_infoCardWrapStyle', wrapStyle);
// 空白判定:去标签 + &nbsp; + 去空白后为空 → 不渲染富文本(与后台一致,避免空白块)
const raw = itemValue.content == null ? '' : String(itemValue.content);