2026-06-09 17:49:15 +08:00
|
|
|
|
<template>
|
2026-06-10 10:15:10 +08:00
|
|
|
|
<view class="dynamic-form">
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<view v-for="(group, gi) in fields" :key="gi" class="form-group">
|
|
|
|
|
|
<!-- single -->
|
|
|
|
|
|
<block v-if="group.type === 'single'">
|
|
|
|
|
|
<view v-if="group.field.type === 'text'" class="form-item">
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<input type="text" :value="formData[group.field.key]" :placeholder="group.field.placeholder"
|
|
|
|
|
|
placeholder-class="placeholder" class="form-input" @input="onInput(group.field.key, $event)" />
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<view v-else-if="group.field.type === 'number'" class="form-item"
|
|
|
|
|
|
:style="group.field.tip ? 'flex-wrap: wrap;' : ''">
|
|
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<input type="digit" :value="formData[group.field.key]" :placeholder="group.field.placeholder"
|
|
|
|
|
|
placeholder-class="placeholder" class="form-input" @input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
<text v-if="group.field.tips && !group.field.tip" class="form-tips">{{ group.field.tips }}</text>
|
|
|
|
|
|
<view v-if="group.field.tip" class="price-tip">
|
|
|
|
|
|
<text class="price-tip-card">{{ group.field.tip }}</text>
|
|
|
|
|
|
</view>
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
2026-06-23 09:55:51 +08:00
|
|
|
|
<view v-else-if="group.field.type === 'textarea'" class="detail-row">
|
|
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<view class="detail-textarea-wrap">
|
|
|
|
|
|
<textarea class="detail-textarea" :value="formData[group.field.key]" :placeholder="group.field.placeholder"
|
|
|
|
|
|
placeholder-class="placeholder" maxlength="200" @input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
<text class="detail-count">{{ (formData[group.field.key] || "").length }}/200</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="group.field.type === 'select'" class="form-item">
|
|
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<form-item-select :value="formData[group.field.key]" :options="group.field.options || []"
|
|
|
|
|
|
:placeholder="group.field.placeholder" @input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="group.field.type === 'single_select'" class="form-item" style="flex-wrap: wrap;">
|
|
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<view class="select-options">
|
|
|
|
|
|
<form-item-radio :value="formData[group.field.key]" :options="group.field.options || []"
|
|
|
|
|
|
@input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="group.field.type === 'multi_select'" class="form-item" style="flex-wrap: wrap;">
|
|
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<view class="select-options">
|
|
|
|
|
|
<form-item-checkbox :value="formData[group.field.key]" :options="group.field.options || []"
|
|
|
|
|
|
:maxCount="group.field.maxCount || 0" @input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<view v-else-if="group.field.type === 'image_multi'" class="form-item-up upload-item">
|
|
|
|
|
|
<view>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<text class="upload-tip">(仅可上传{{ group.field.maxCount || 9 }}个图片)</text>
|
|
|
|
|
|
</view>
|
2026-06-23 09:55:51 +08:00
|
|
|
|
<view class="upload-content">
|
|
|
|
|
|
<form-item-image :value="formData[group.field.key]" :maxCount="group.field.maxCount || 9"
|
|
|
|
|
|
:userId="uploadUserId" @input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
</view>
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<view v-else-if="group.field.type === 'video'" class="form-item-up upload-item">
|
|
|
|
|
|
<view>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<text class="upload-tip">(仅可上传{{ group.field.maxCount || 1 }}个视频)</text>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="upload-content">
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<form-item-video :value="formData[group.field.key]" :maxCount="group.field.maxCount || 1"
|
|
|
|
|
|
:maxSize="group.field.maxSize || group.field.maxFileSize || 100"
|
|
|
|
|
|
:userId="uploadUserId"
|
|
|
|
|
|
@input="onInput(group.field.key, $event)" />
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</view>
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<view v-else-if="group.field.type === 'richtext_editor'" class="form-item">
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<text :class="['form-label', group.field.is_required === true ? 'required' : '']">{{ group.field.label
|
|
|
|
|
|
}}</text>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<form-item-richtext :value="formData[group.field.key]" @input="onInput(group.field.key, $event)" />
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<view v-else-if="group.field.type === 'service_step'" class="form-item-up upload-item">
|
|
|
|
|
|
<form-item-service-step :value="formData[group.field.key]" :label="group.field.label"
|
|
|
|
|
|
:required="group.field.is_required === true" @input="onInput(group.field.key, $event)" />
|
|
|
|
|
|
</view>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</block>
|
|
|
|
|
|
<!-- section -->
|
|
|
|
|
|
<view v-if="group.type === 'section'" class="section-card">
|
|
|
|
|
|
<view v-if="group.sectionTitle" class="section-title-bar">
|
|
|
|
|
|
<text class="section-title">{{ group.sectionTitle }}</text>
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<view v-for="(field, fi) in group.fields" :key="field.key || fi" class="section-field">
|
|
|
|
|
|
<view v-if="field.type === 'text'" class="form-item no-bg">
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<input type="text" :value="formData[field.key]" :placeholder="field.placeholder"
|
|
|
|
|
|
placeholder-class="placeholder" class="form-input" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'textarea'" class="detail-row">
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<view class="detail-textarea-wrap">
|
|
|
|
|
|
<textarea class="detail-textarea" :value="formData[field.key]" :placeholder="field.placeholder"
|
|
|
|
|
|
placeholder-class="placeholder" maxlength="200" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
<text class="detail-count">{{ (formData[field.key] || "").length }}/200</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<view v-else-if="field.type === 'number'" class="form-item no-bg"
|
|
|
|
|
|
:style="field.tip ? 'flex-wrap: wrap;' : ''">
|
2026-06-10 16:23:47 +08:00
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<input type="digit" :value="formData[field.key]" :placeholder="field.placeholder"
|
|
|
|
|
|
placeholder-class="placeholder" class="form-input" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
<text v-if="field.tips && !field.tip" class="form-tips">{{ field.tips }}</text>
|
|
|
|
|
|
<view v-if="field.tip" class="price-tip">
|
|
|
|
|
|
<text class="price-tip-card">{{ field.tip }}</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'select'" class="form-item no-bg">
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<form-item-select :value="formData[field.key]" :options="field.options || []"
|
|
|
|
|
|
:placeholder="field.placeholder" @input="onInput(field.key, $event)" />
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'single_select'" class="form-item no-bg" style="flex-wrap: wrap;">
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<view class="select-options">
|
|
|
|
|
|
<form-item-radio :value="formData[field.key]" :options="field.options || []"
|
|
|
|
|
|
@input="onInput(field.key, $event)" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'multi_select'" class="form-item no-bg" style="flex-wrap: wrap;">
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<view class="select-options">
|
|
|
|
|
|
<form-item-checkbox :value="formData[field.key]" :options="field.options || []"
|
|
|
|
|
|
:maxCount="field.maxCount || 0" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'image_multi'" class="form-item-up upload-item no-bg">
|
|
|
|
|
|
<view>
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<text class="upload-tip">(仅可上传{{ field.maxCount || 9 }}个图片)</text>
|
|
|
|
|
|
</view>
|
2026-06-23 09:55:51 +08:00
|
|
|
|
<view class="upload-content">
|
|
|
|
|
|
<form-item-image :value="formData[field.key]" :maxCount="field.maxCount || 9"
|
|
|
|
|
|
:userId="uploadUserId" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
</view>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'video'" class="form-item-up upload-item no-bg">
|
|
|
|
|
|
<view>
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<text class="upload-tip">(仅可上传{{ field.maxCount || 1 }}个视频)</text>
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view class="upload-content">
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<form-item-video :value="formData[field.key]" :maxCount="field.maxCount || 1"
|
|
|
|
|
|
:maxSize="field.maxSize || field.maxFileSize || 100" :userId="uploadUserId"
|
|
|
|
|
|
@input="onInput(field.key, $event)" />
|
2026-06-10 16:23:47 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-else-if="field.type === 'richtext_editor'" class="form-item no-bg">
|
|
|
|
|
|
<text :class="['form-label', field.is_required === true ? 'required' : '']">{{ field.label }}</text>
|
|
|
|
|
|
<form-item-richtext :value="formData[field.key]" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
</view>
|
2026-06-11 20:48:30 +08:00
|
|
|
|
<view v-else-if="field.type === 'service_step'" class="form-item-up upload-item no-bg">
|
|
|
|
|
|
<form-item-service-step :value="formData[field.key]" :label="field.label"
|
|
|
|
|
|
:required="field.is_required === true" @input="onInput(field.key, $event)" />
|
|
|
|
|
|
</view>
|
2026-06-10 10:15:10 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2026-06-09 17:49:15 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import FormItemText from './FormItemText.vue';
|
|
|
|
|
|
import FormItemTextarea from './FormItemTextarea.vue';
|
|
|
|
|
|
import FormItemNumber from './FormItemNumber.vue';
|
|
|
|
|
|
import FormItemImage from './FormItemImage.vue';
|
|
|
|
|
|
import FormItemVideo from './FormItemVideo.vue';
|
|
|
|
|
|
import FormItemSelect from './FormItemSelect.vue';
|
|
|
|
|
|
import FormItemRadio from './FormItemRadio.vue';
|
|
|
|
|
|
import FormItemCheckbox from './FormItemCheckbox.vue';
|
|
|
|
|
|
import FormItemRichtext from './FormItemRichtext.vue';
|
2026-06-11 20:48:30 +08:00
|
|
|
|
import FormItemServiceStep from './FormItemServiceStep.vue';
|
2026-06-09 17:49:15 +08:00
|
|
|
|
|
|
|
|
|
|
export default {
|
2026-06-10 10:15:10 +08:00
|
|
|
|
name: 'DynamicForm',
|
|
|
|
|
|
components: {
|
|
|
|
|
|
'form-item-text': FormItemText,
|
|
|
|
|
|
'form-item-textarea': FormItemTextarea,
|
|
|
|
|
|
'form-item-number': FormItemNumber,
|
|
|
|
|
|
'form-item-image': FormItemImage,
|
|
|
|
|
|
'form-item-video': FormItemVideo,
|
|
|
|
|
|
'form-item-select': FormItemSelect,
|
|
|
|
|
|
'form-item-radio': FormItemRadio,
|
|
|
|
|
|
'form-item-checkbox': FormItemCheckbox,
|
2026-06-11 20:48:30 +08:00
|
|
|
|
'form-item-richtext': FormItemRichtext,
|
|
|
|
|
|
'form-item-service-step': FormItemServiceStep
|
2026-06-10 10:15:10 +08:00
|
|
|
|
},
|
|
|
|
|
|
props: {
|
|
|
|
|
|
fields: {
|
|
|
|
|
|
type: Array,
|
|
|
|
|
|
default: function () { return []; }
|
|
|
|
|
|
},
|
|
|
|
|
|
value: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: function () { return {}; }
|
2026-06-11 20:48:30 +08:00
|
|
|
|
},
|
|
|
|
|
|
uploadUserId: {
|
|
|
|
|
|
type: [Number, String],
|
|
|
|
|
|
default: null
|
2026-06-10 10:15:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
data: function () {
|
|
|
|
|
|
return {
|
|
|
|
|
|
formData: {}
|
|
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
watch: {
|
|
|
|
|
|
value: {
|
|
|
|
|
|
handler: function (val) {
|
|
|
|
|
|
this.formData = Object.assign({}, val);
|
|
|
|
|
|
},
|
|
|
|
|
|
immediate: true
|
|
|
|
|
|
},
|
|
|
|
|
|
fields: {
|
|
|
|
|
|
handler: function () {
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
var data = {};
|
2026-06-10 16:23:47 +08:00
|
|
|
|
this.fields.forEach(function (group) {
|
|
|
|
|
|
if (group.type === 'single' && group.field) {
|
|
|
|
|
|
self._initFieldData(data, group.field);
|
|
|
|
|
|
} else if (group.type === 'section' && group.fields) {
|
|
|
|
|
|
group.fields.forEach(function (field) {
|
|
|
|
|
|
self._initFieldData(data, field);
|
|
|
|
|
|
});
|
2026-06-10 10:15:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
this.formData = Object.assign({}, this.formData, data);
|
|
|
|
|
|
},
|
|
|
|
|
|
immediate: true
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
2026-06-10 16:23:47 +08:00
|
|
|
|
_initFieldData: function (data, field) {
|
|
|
|
|
|
if (this.formData[field.key] !== undefined) {
|
|
|
|
|
|
data[field.key] = this.formData[field.key];
|
|
|
|
|
|
} else if (field.default_value !== undefined) {
|
|
|
|
|
|
data[field.key] = field.default_value;
|
2026-06-11 20:48:30 +08:00
|
|
|
|
} else if (
|
|
|
|
|
|
field.type === 'multi_select' ||
|
|
|
|
|
|
field.type === 'image_multi' ||
|
|
|
|
|
|
field.type === 'service_step' ||
|
|
|
|
|
|
(field.type === 'video' && field.maxCount > 1)
|
|
|
|
|
|
) {
|
2026-06-10 16:23:47 +08:00
|
|
|
|
data[field.key] = [];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
data[field.key] = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-06-10 10:15:10 +08:00
|
|
|
|
onInput: function (key, event) {
|
|
|
|
|
|
var value = event;
|
|
|
|
|
|
if (event && event.target && event.target.value !== undefined) {
|
|
|
|
|
|
value = event.target.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$set(this.formData, key, value);
|
|
|
|
|
|
this.$emit('input', this.formData);
|
|
|
|
|
|
},
|
|
|
|
|
|
getData: function () {
|
|
|
|
|
|
return Object.assign({}, this.formData);
|
|
|
|
|
|
},
|
|
|
|
|
|
validate: function () {
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
for (var i = 0; i < this.fields.length; i++) {
|
2026-06-10 16:23:47 +08:00
|
|
|
|
var group = this.fields[i];
|
|
|
|
|
|
var fieldsToCheck = [];
|
|
|
|
|
|
if (group.type === 'single' && group.field) {
|
|
|
|
|
|
fieldsToCheck.push(group.field);
|
|
|
|
|
|
} else if (group.type === 'section' && group.fields) {
|
|
|
|
|
|
fieldsToCheck = group.fields;
|
|
|
|
|
|
}
|
|
|
|
|
|
for (var j = 0; j < fieldsToCheck.length; j++) {
|
|
|
|
|
|
var field = fieldsToCheck[j];
|
|
|
|
|
|
if (field.is_required === true) {
|
|
|
|
|
|
var value = self.formData[field.key];
|
|
|
|
|
|
if (!value || (Array.isArray(value) && value.length === 0)) {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: field.label + '不能为空',
|
|
|
|
|
|
icon: 'none'
|
|
|
|
|
|
});
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-06-10 10:15:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-09 17:49:15 +08:00
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.dynamic-form {
|
2026-06-10 10:15:10 +08:00
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 16:23:47 +08:00
|
|
|
|
.form-group {
|
2026-06-10 10:15:10 +08:00
|
|
|
|
width: 100%;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item {
|
2026-06-10 10:15:10 +08:00
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-flow: row nowrap;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-label {
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.required::before {
|
|
|
|
|
|
content: '*';
|
|
|
|
|
|
color: #ff0000;
|
|
|
|
|
|
margin-right: 4rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-tips {
|
|
|
|
|
|
font-size: 24rpx;
|
2026-06-10 16:23:47 +08:00
|
|
|
|
color: #333333;
|
|
|
|
|
|
margin-left: 0;
|
2026-06-10 10:15:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-tip {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
margin-top: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-tip-card {
|
|
|
|
|
|
padding: 8rpx 12rpx;
|
|
|
|
|
|
background: #FDF0F0;
|
|
|
|
|
|
border-radius: 8rpx;
|
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #E8101E;
|
|
|
|
|
|
line-height: 32rpx;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-item-up {
|
2026-06-10 10:15:10 +08:00
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
padding: 24rpx;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-flow: column nowrap;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: flex-start;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
border-radius: 10rpx;
|
|
|
|
|
|
margin-bottom: 24rpx;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 10:15:10 +08:00
|
|
|
|
.upload-item {
|
|
|
|
|
|
padding-bottom: 30rpx;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 10:15:10 +08:00
|
|
|
|
.upload-tip {
|
|
|
|
|
|
font-size: 24rpx;
|
|
|
|
|
|
color: #999999;
|
|
|
|
|
|
margin-left: 10rpx;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 10:15:10 +08:00
|
|
|
|
.upload-content {
|
|
|
|
|
|
margin-top: 20rpx;
|
|
|
|
|
|
width: 100%;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 10:15:10 +08:00
|
|
|
|
.select-options {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
margin-top: 16rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 16:23:47 +08:00
|
|
|
|
.section-card {
|
2026-06-10 10:15:10 +08:00
|
|
|
|
background: #ffffff;
|
|
|
|
|
|
border-radius: 20rpx;
|
2026-06-10 16:23:47 +08:00
|
|
|
|
padding: 0 24rpx 24rpx;
|
2026-06-10 10:15:10 +08:00
|
|
|
|
margin-bottom: 24rpx;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 16:23:47 +08:00
|
|
|
|
/* section内字段间距20rpx,通过section-field包装器控制 */
|
2026-06-11 20:48:30 +08:00
|
|
|
|
.section-field+.section-field {
|
2026-06-10 16:23:47 +08:00
|
|
|
|
padding-top: 12rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* section内字段去掉独立白色背景和底部间距 */
|
|
|
|
|
|
.section-card .form-item,
|
|
|
|
|
|
.section-card .form-item-up {
|
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
|
padding-left: 0;
|
|
|
|
|
|
padding-right: 0;
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-card .detail-row {
|
|
|
|
|
|
padding-top: 0;
|
|
|
|
|
|
padding-bottom: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title-bar {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
padding: 24rpx 0 20rpx;
|
|
|
|
|
|
font-size: 30rpx;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
line-height: 40rpx;
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 10:15:10 +08:00
|
|
|
|
.detail-row {
|
2026-06-10 16:23:47 +08:00
|
|
|
|
padding: 20rpx 0 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-row:first-child {
|
|
|
|
|
|
padding-top: 0;
|
2026-06-10 10:15:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-row .form-label {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
margin-bottom: 20rpx;
|
|
|
|
|
|
line-height: 40rpx;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-textarea-wrap {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
background: #f7f7f7;
|
|
|
|
|
|
border-radius: 16rpx;
|
|
|
|
|
|
padding: 24rpx 24rpx 52rpx;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.detail-textarea {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 108rpx;
|
|
|
|
|
|
min-height: 108rpx;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
border: 0;
|
|
|
|
|
|
font-size: 28rpx;
|
|
|
|
|
|
line-height: 40rpx;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
box-sizing: border-box;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-10 10:15:10 +08:00
|
|
|
|
.detail-count {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
right: 24rpx;
|
|
|
|
|
|
bottom: 16rpx;
|
|
|
|
|
|
font-size: 22rpx;
|
|
|
|
|
|
color: #999999;
|
|
|
|
|
|
line-height: 32rpx;
|
2026-06-09 17:49:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
</style>
|