mirror of
https://github.com/crmeb/CRMEB.git
synced 2026-10-29 14:01:07 +00:00
feat(移动端订单): 优化商品自定义表单控件样式与交互
This commit is contained in:
parent
7405042247
commit
a8d86e1301
@ -10,48 +10,68 @@
|
||||
<view class="custom-form-section" v-if="visible && fields.length > 0">
|
||||
<view class="section-title">{{ $t('必填信息') }}</view>
|
||||
<view class="form-list">
|
||||
<view class="form-item" v-for="(field, index) in fields" :key="index">
|
||||
<view class="form-item" v-for="(field, index) in fields" :key="index" :class="{ 'is-bordered': index > 0 }">
|
||||
<view class="form-label">
|
||||
{{ field.label }}
|
||||
<text class="required" v-if="field.required">*</text>
|
||||
<text class="required-holder" v-else></text>
|
||||
{{ field.label }}
|
||||
</view>
|
||||
<view class="form-input-wrap">
|
||||
<!-- 文本类:文本框/数字/邮件/身份证/手机号,按类型切换键盘 -->
|
||||
<input
|
||||
v-if="isTextInput(field)"
|
||||
class="form-input"
|
||||
:class="{ 'is-focus': focusField === field.field }"
|
||||
:type="textInputKeyboard(field)"
|
||||
:placeholder="$t('请输入') + field.label"
|
||||
:value="formData[field.field]"
|
||||
@focus="focusField = field.field"
|
||||
@blur="focusField = ''"
|
||||
@input="onInput(field.field, $event)"
|
||||
/>
|
||||
<!-- 下拉选择(自定义 options) -->
|
||||
<!-- 下拉选择(自定义 options,历史值) -->
|
||||
<picker
|
||||
v-else-if="field.type === 'picker'"
|
||||
class="form-picker-wrap"
|
||||
:range="field.options"
|
||||
range-key="label"
|
||||
@change="onPickerChange(field, $event)"
|
||||
>
|
||||
<view class="form-picker">
|
||||
{{ formData[field.field] || $t('请选择') }}
|
||||
<view class="form-picker" :class="{ 'is-set': formData[field.field] }">
|
||||
<view class="pick-main">
|
||||
<text class="iconfont pick-icon icon-fenlei1"></text>
|
||||
<text class="pick-value">{{ pickLabel(field) || $t('请选择') }}</text>
|
||||
</view>
|
||||
<text class="picker-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
<!-- 日期(管理端类型值 data,兼容 date) -->
|
||||
<picker
|
||||
v-else-if="field.type === 'data' || field.type === 'date'"
|
||||
class="form-picker-wrap"
|
||||
mode="date"
|
||||
@change="onValueChange(field.field, $event)"
|
||||
>
|
||||
<view class="form-picker">
|
||||
{{ formData[field.field] || $t('请选择') + field.label }}
|
||||
<view class="form-picker" :class="{ 'is-set': formData[field.field] }">
|
||||
<view class="pick-main">
|
||||
<text class="iconfont pick-icon icon-rilitubiao"></text>
|
||||
<text class="pick-value">{{ formData[field.field] || $t('请选择') + field.label }}</text>
|
||||
</view>
|
||||
<text class="picker-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
<!-- 时间 -->
|
||||
<picker v-else-if="field.type === 'time'" mode="time" @change="onValueChange(field.field, $event)">
|
||||
<view class="form-picker">
|
||||
{{ formData[field.field] || $t('请选择') + field.label }}
|
||||
<picker
|
||||
v-else-if="field.type === 'time'"
|
||||
class="form-picker-wrap"
|
||||
mode="time"
|
||||
@change="onValueChange(field.field, $event)"
|
||||
>
|
||||
<view class="form-picker" :class="{ 'is-set': formData[field.field] }">
|
||||
<view class="pick-main">
|
||||
<text class="iconfont pick-icon icon-shijian"></text>
|
||||
<text class="pick-value">{{ formData[field.field] || $t('请选择') + field.label }}</text>
|
||||
</view>
|
||||
<text class="picker-arrow">›</text>
|
||||
</view>
|
||||
</picker>
|
||||
@ -71,8 +91,11 @@
|
||||
<textarea
|
||||
v-else-if="field.type === 'textarea'"
|
||||
class="form-textarea"
|
||||
:class="{ 'is-focus': focusField === field.field }"
|
||||
:placeholder="$t('请输入') + field.label"
|
||||
:value="formData[field.field]"
|
||||
@focus="focusField = field.field"
|
||||
@blur="focusField = ''"
|
||||
@input="onInput(field.field, $event)"
|
||||
></textarea>
|
||||
</view>
|
||||
@ -82,7 +105,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { computed, ref } from 'vue';
|
||||
import i18n from '@/utils/lang.js';
|
||||
import util from '@/utils/util.js';
|
||||
|
||||
@ -102,6 +125,9 @@ const emit = defineEmits(['update:modelValue', 'change']);
|
||||
/** 当前表单数据(基于 value 拷贝避免直接修改 prop) */
|
||||
const formData = computed(() => props.modelValue || {});
|
||||
|
||||
/** 当前聚焦的输入字段(用于高亮样式) */
|
||||
const focusField = ref('');
|
||||
|
||||
/** 文本类输入类型(input 为历史值,其余为管理端类型) */
|
||||
var TEXT_INPUT_TYPES = ['input', 'text', 'number', 'email', 'id', 'phone'];
|
||||
/** 各类型格式校验规则,返回错误提示文案,通过返回空串 */
|
||||
@ -121,6 +147,14 @@ function textInputKeyboard(field) {
|
||||
return field.type === 'number' || field.type === 'phone' ? 'number' : 'text';
|
||||
}
|
||||
|
||||
/** 下拉字段按已选 value 反查选项文案(无匹配时回退显示原值) */
|
||||
function pickLabel(field) {
|
||||
var val = formData.value[field.field];
|
||||
if (val === undefined || val === null || val === '') return '';
|
||||
var opt = (field.options || []).find(function (o) { return o.value === val; });
|
||||
return opt ? opt.label : String(val);
|
||||
}
|
||||
|
||||
/** 更新字段值并通知父组件 */
|
||||
function setFieldValue(field, val) {
|
||||
var next = { ...formData.value, [field]: val };
|
||||
@ -186,64 +220,135 @@ defineExpose({ validate });
|
||||
margin: 20rpx;
|
||||
background: #fff;
|
||||
border-radius: 12rpx;
|
||||
padding: 24rpx 30rpx;
|
||||
padding: 24rpx 30rpx 12rpx;
|
||||
}
|
||||
.section-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 30rpx;
|
||||
font-weight: bold;
|
||||
color: #282828;
|
||||
margin-bottom: 20rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
/* 标题左侧主题色装饰条 */
|
||||
.section-title::before {
|
||||
content: '';
|
||||
width: 6rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 3rpx;
|
||||
background: var(--view-theme, #e93323);
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
.form-list {
|
||||
border-top: 1px dashed #eee;
|
||||
padding-top: 16rpx;
|
||||
padding-top: 4rpx;
|
||||
}
|
||||
.form-item {
|
||||
padding: 16rpx 0;
|
||||
padding: 20rpx 0;
|
||||
}
|
||||
/* 相邻选择器小程序 WXSS 支持不稳,用 class 显式加分隔线 */
|
||||
.form-item.is-bordered {
|
||||
border-top: 1px solid #f5f5f5;
|
||||
}
|
||||
.form-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 26rpx;
|
||||
color: #333;
|
||||
margin-bottom: 12rpx;
|
||||
margin-bottom: 14rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
/* 必填星号前置,与必填占位符等宽,保证所有标题左对齐 */
|
||||
.form-label .required {
|
||||
display: inline-block;
|
||||
width: 24rpx;
|
||||
color: var(--view-theme, #e93323);
|
||||
margin-left: 4rpx;
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
.required-holder {
|
||||
display: inline-block;
|
||||
width: 24rpx;
|
||||
}
|
||||
.form-input-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.form-input {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.form-input,
|
||||
.form-textarea {
|
||||
flex: 1;
|
||||
height: 160rpx;
|
||||
width: 100%;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.form-picker {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
background: #f8f8f8;
|
||||
border-radius: 8rpx;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #282828;
|
||||
box-sizing: border-box;
|
||||
transition: background-color 0.2s, border-color 0.2s;
|
||||
}
|
||||
.form-input {
|
||||
height: 80rpx;
|
||||
}
|
||||
.form-textarea {
|
||||
height: 160rpx;
|
||||
padding: 20rpx 24rpx;
|
||||
}
|
||||
/* 聚焦态:白底 + 主题色描边 */
|
||||
.form-input.is-focus,
|
||||
.form-textarea.is-focus {
|
||||
background: #fff;
|
||||
border-color: var(--view-theme, #e93323);
|
||||
}
|
||||
/* 下拉/日期/时间选择器:占满整行(picker 包裹层不伸展会导致内容宽度塌缩) */
|
||||
.form-picker-wrap {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
.form-picker {
|
||||
width: 100%;
|
||||
height: 80rpx;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 12rpx;
|
||||
padding: 0 24rpx;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #999;
|
||||
transition: background-color 0.2s, border-color 0.2s;
|
||||
}
|
||||
.form-picker:active {
|
||||
background: #f2f2f2;
|
||||
border-color: var(--view-theme, #e93323);
|
||||
}
|
||||
.pick-main {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-width: 0;
|
||||
}
|
||||
/* 日期/时间/下拉的类型图标 */
|
||||
.pick-icon {
|
||||
font-size: 30rpx;
|
||||
color: var(--view-theme, #e93323);
|
||||
margin-right: 12rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
.pick-value {
|
||||
font-size: 28rpx;
|
||||
color: #bbb;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
/* 已选择值用深色 */
|
||||
.form-picker.is-set .pick-value {
|
||||
color: #282828;
|
||||
}
|
||||
.picker-arrow {
|
||||
font-size: 36rpx;
|
||||
font-size: 32rpx;
|
||||
color: #ccc;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
.form-upload {
|
||||
position: relative;
|
||||
@ -254,7 +359,7 @@ defineExpose({ validate });
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px dashed #ddd;
|
||||
border-radius: 8rpx;
|
||||
border-radius: 12rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@ -264,7 +369,7 @@ defineExpose({ validate });
|
||||
.upload-preview {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 8rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
.upload-del {
|
||||
position: absolute;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user