You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

505 lines
14 KiB

<template>
<div class="complaint-page">
<!-- 顶部导航栏 -->
<van-nav-bar title="投诉建议" left-arrow @click-left="onClickLeft" />
<!-- 表单内容 -->
<van-form class="form-content" @submit="onSubmit" @failed="onFailed">
<!-- 选择卡片 -->
<div class="form-card">
<van-field v-model="form.bureauDept" is-link readonly name="bureauDept" label="执法单位" placeholder="请选择"
:rules="[{ required: true, message: '请选择执法单位' }]" class="form-field"
@click="showPicker('bureauDept')" required />
<van-field v-model="form.complaintType" is-link readonly name="complaintType" label="投诉类型"
placeholder="请选择" :rules="[{ required: true, message: '请选择投诉类型' }]" class="form-field"
@click="showPicker('complaintType')" required />
<van-field v-model="form.complaintItem" is-link readonly name="complaintItem" label="投诉事项"
placeholder="请选择" class="form-complaint-item" @click="showPicker('complaintItem')" />
</div>
<!-- 投诉内容 -->
<div class="form-card">
<div class="content-label">投诉内容</div>
<van-field v-model="form.content" name="content" label="" type="textarea" rows="4" autosize
placeholder="请输入投诉内容" maxlength="100" show-word-limit class="content-field" />
</div>
<!-- 附件 -->
<div class="form-card">
<div class="content-label">附件<span class="sub-text">(不多于8张)</span></div>
<van-uploader v-model="form.images" multiple :max-count="8" class="uploader" />
</div>
<!-- 涉事地址 -->
<div class="form-card">
<van-field v-model="form.addressArea" name="addressArea"
:rules="[{ required: true, message: '请选择所在地区' }]" class="form-field">
<template #input>
<div class="form-pick-item" @click="showPicker('address')">
<span class="pick-label required">涉事地址</span>
<div class="pick-right">
<span class="city-text">深圳市</span>
<span class="pick-value" :class="{ placeholder: !form.addressArea }">{{ form.addressArea
|| '选择所在地区' }}</span>
<van-icon name="arrow" class="pick-arrow" />
</div>
</div>
</template>
</van-field>
<van-field v-model="form.addressDetail" name="addressDetail" placeholder="详细地址(写字楼、小区、门牌等)"
class="address-field" />
</div>
<!-- 联系人信息 -->
<div class="form-card">
<van-field v-model="form.contactName" name="contactName" label="联系人" placeholder="联系人"
:rules="[{ required: true, message: '请填写联系人' }]" class="contact-field" required />
<van-field v-model="form.contactPhone" name="contactPhone" label="联系电话" placeholder="联系电话" type="tel"
:rules="[
{ required: true, message: '请填写联系电话' },
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }
]" class="contact-field" required />
<!-- 是否授权 -->
<div class="authorize-bar">
<van-icon name="info" color="#2b7de9" size="18" />
<span class="authorize-text">是否授权</span>
<van-radio-group v-model="form.authorized" direction="horizontal">
<van-radio :name="true">
<template #icon="{ checked }">
<div class="custom-radio-icon" :class="{ checked }">
<van-icon v-if="checked" name="success" size="10" color="#fff" />
</div>
</template>
同意
</van-radio>
<van-radio :name="false">
<template #icon="{ checked }">
<div class="custom-radio-icon" :class="{ checked }">
<van-icon v-if="checked" name="success" size="10" color="#fff" />
</div>
</template>
不同意
</van-radio>
</van-radio-group>
</div>
<div class="remark-text">注:选择“同意”,处置部门将通过登记的联系人、联系电话与您联系,并对您的个人信息严格保密。</div>
</div>
<!-- 底部提交栏 -->
<div class="footer-bar">
<van-button type="primary" block round native-type="submit" :loading="submitting"
loading-text="提交中...">提交</van-button>
</div>
</van-form>
<!-- 投诉事项选择弹窗 -->
<ComplaintItemPicker v-model:show="showComplaintItemPicker" :list="complaintItemList"
@select="onComplaintItemSelect" />
<!-- 地址级联选择弹窗 -->
<van-popup v-model:show="showAddressPicker" position="bottom" :style="{ width: '100%', height: '60%' }">
<van-cascader v-model="selectedAddress" title="选择所在地区" :options="addressOptions"
@close="showAddressPicker = false" @finish="onAddressFinish" />
</van-popup>
<!-- 投诉类型选择弹窗 -->
<ComplaintTypePicker v-model:show="showComplaintTypePicker" :columns="complaintTypeList"
@confirm="onComplaintTypeConfirm" />
<!-- 执法单位查询弹窗 -->
<BureauPicker v-model:show="showBureauPicker" :list="bureauList" @confirm="onBureauConfirm" />
</div>
</template>
<script setup name="complaintSuggestion">
import { showToast } from 'vant';
import 'vant/es/toast/style';
import { mockBureauList, mockComplaintTypeList, mockComplaintItemList, mockAddressOptions } from '@/mock/complaint';
import BureauPicker from '@/components/complaint/BureauPicker.vue';
import ComplaintTypePicker from '@/components/complaint/ComplaintTypePicker.vue';
import ComplaintItemPicker from '@/components/complaint/ComplaintItemPicker.vue';
const router = useRouter();
// ========== 表单数据 ==========
const form = ref({
bureauDept: '',
complaintType: '',
complaintItem: '',
content: '',
images: [],
addressArea: '',
addressDetail: '',
contactName: '',
contactPhone: '',
authorized: true,
});
// ========== 执法单位弹窗数据 ==========
const showBureauPicker = ref(false);
const bureauList = ref(mockBureauList);
// ========== 投诉类型弹窗数据 ==========
const showComplaintTypePicker = ref(false);
const complaintTypeList = ref(mockComplaintTypeList);
const onComplaintTypeConfirm = (value) => {
form.value.complaintType = value;
};
// ========== 投诉事项弹窗数据 ==========
const showComplaintItemPicker = ref(false);
const complaintItemList = ref(mockComplaintItemList);
const onComplaintItemSelect = (item) => {
form.value.complaintItem = item.title;
};
// ========== 地址级联选择数据 ==========
const showAddressPicker = ref(false);
const selectedAddress = ref([]);
const addressOptions = mockAddressOptions;
const onAddressFinish = ({ selectedOptions }) => {
const addressText = selectedOptions.map(option => option.text).join(' ');
form.value.addressArea = addressText;
showAddressPicker.value = false;
};
// ========== 返回上一页 ==========
const onClickLeft = () => {
router.back();
};
// ========== picker 事件 ==========
const showPicker = (field) => {
if (field === 'bureauDept') {
showBureauPicker.value = true;
} else if (field === 'complaintType') {
showComplaintTypePicker.value = true;
} else if (field === 'complaintItem') {
showComplaintItemPicker.value = true;
} else if (field === 'address') {
showAddressPicker.value = true;
} else {
showToast(`${field}选择功能开发中`);
}
};
// ========== 执法单位确定 ==========
const onBureauConfirm = (value) => {
if (!value) return;
form.value.bureauDept = value;
};
// ========== 提交状态 ==========
const submitting = ref(false);
// ========== 提交表单 ==========
const onSubmit = () => {
if (submitting.value) return;
submitting.value = true;
showToast('提交成功,我们将尽快处理');
setTimeout(() => {
submitting.value = false;
router.back();
}, 1500);
};
// ========== 校验失败 ==========
const onFailed = (errorInfo) => {
// 表单校验失败,错误信息已在 van-form 中展示
};
</script>
<style lang="less" scoped>
.complaint-page {
display: flex;
flex-direction: column;
height: 100%;
background-color: #f5f7fa;
.form-content {
flex: 1;
overflow-y: auto;
padding: 0.27rem 0.27rem 1.5rem;
}
}
.form-complaint-item {
:deep(.van-field__label) {
padding-left: 0.2rem;
}
}
/* ===== 表单卡片 ===== */
.form-card {
background: #fff;
border-radius: 0.2rem;
margin-bottom: 0.27rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
overflow: hidden;
}
/* ===== 选择项 ===== */
.form-pick-item {
display: flex;
align-items: center;
// padding: 0.3rem;
min-height: 1.1rem;
box-sizing: border-box;
}
.pick-label {
font-size: 0.35rem;
color: #333;
width: 1.8rem;
flex-shrink: 0;
padding-left: 0.25rem;
box-sizing: border-box;
position: relative;
&.required::before {
content: '*';
color: #f44336;
position: absolute;
left: 0;
}
}
.pick-right {
flex: 1;
display: flex;
align-items: center;
}
.city-text {
font-size: 0.32rem;
color: #333;
margin-right: 0.1rem;
}
.pick-value {
flex: 1;
font-size: 0.32rem;
color: #333;
margin-right: 0.1rem;
&.placeholder {
color: #bbb;
}
}
.pick-arrow {
font-size: 0.32rem;
color: #ccc;
margin-left: auto;
}
.divider {
height: 1px;
background: #f5f5f5;
margin: 0 0.3rem;
}
/* ===== 表单字段(覆盖 van-field 默认样式) ===== */
.form-field {
background: transparent;
padding: 0.15rem 0.4rem 0.3rem;
:deep(.van-field__body) {
display: block;
}
:deep(.van-field__error-message) {
font-size: 0.26rem;
// padding: 0 0.3rem 0.1rem;
}
}
.form-field::after {
display: none;
}
/* ===== 投诉内容 ===== */
.content-field {
padding: 0.15rem 0.6rem 0.3rem;
background: transparent;
:deep(.van-field__body) {
flex-direction: column;
align-items: flex-start;
}
:deep(.van-field__label) {
font-size: 0.35rem;
color: #333;
padding-top: 0.2rem;
width: auto;
margin-right: 0;
}
:deep(.van-field__control) {
font-size: 0.32rem;
color: #333;
min-height: 2rem;
text-align: left;
}
:deep(.van-field__word-limit) {
font-size: 0.28rem;
color: #bbb;
text-align: right;
padding-top: 0.1rem;
}
:deep(.van-field__error-message) {
font-size: 0.26rem;
padding-left: 0;
}
}
/* ===== 附件上传 ===== */
.content-label {
font-size: 0.37333rem;
color: #333;
padding: 0.35rem 0.6rem 0.1rem;
.sub-text {
font-size: 0.28rem;
color: #999;
}
}
.uploader {
padding: 0.1rem 0.6rem 0.3rem;
:deep(.van-uploader__upload) {
background: #f5f7fa;
border-radius: 0.1rem;
}
}
/* ===== 地址字段 ===== */
.address-field {
padding: 0.15rem 0.3rem 0.3rem;
background: transparent;
margin-left: 70px;
box-sizing: border-box;
width: 80%;
:deep(.van-field__control) {
font-size: 0.32rem;
color: #999;
text-align: left;
}
:deep(.van-field__error-message) {
font-size: 0.26rem;
padding-left: 0;
}
}
/* ===== 联系人字段 ===== */
.contact-field {
padding: 0.35rem 0.5rem;
background: transparent;
:deep(.van-field__label) {
font-size: 0.35rem;
color: #333;
width: 1.8rem;
}
:deep(.van-field__control) {
font-size: 0.32rem;
color: #333;
text-align: left;
}
:deep(.van-field__error-message) {
font-size: 0.26rem;
text-align: left;
}
}
.contact-field::after {
display: none;
}
/* ===== 是否授权栏 ===== */
.authorize-bar {
display: flex;
align-items: center;
background: #fff;
border-radius: 0.2rem;
padding: 0.25rem 0.48rem;
gap: 0.2rem;
.authorize-text {
flex: 1;
font-size: 0.32rem;
color: #333;
}
}
.remark-text {
font-size: 0.28rem;
color: #999;
padding:0px 20px 20px 20px;
box-sizing: border-box;
}
.custom-radio-icon {
width: 16px;
height: 16px;
border: 1px solid #c8c9cc;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
&.checked {
background-color: #2b7de9;
border-color: #2b7de9;
}
}
:deep(.van-radio) {
margin-right: 0.2rem;
&:last-child {
margin-right: 0;
}
}
:deep(.van-radio__label) {
font-size: 0.28rem;
color: #333;
}
/* ===== 底部提交栏 ===== */
.footer-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 0.2rem 0.4rem;
background: #fff;
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
z-index: 10;
:deep(.van-button--primary) {
background: linear-gradient(135deg, #2b7de9, #5ba3f5);
border: none;
}
}
/* ===== 弹窗组件样式已迁移到对应子组件 ===== */
</style>