kboss/f/web-kboss/src/views/H5/components/H5_dialog/MobileFullScreenConsultDialog.vue
2026-07-09 16:18:13 +08:00

587 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
:visible.sync="dialogVisible"
:fullscreen="true"
custom-class="mobile-consult-fullscreen-dialog"
:append-to-body="true"
:close-on-click-modal="false"
:close-on-press-escape="!submitSuccess"
:show-close="false"
@close="handleClose"
>
<div v-if="submitSuccess" class="submit-success-page">
<div class="success-icon">
<i class="el-icon-check"></i>
</div>
<h2 class="success-title">已完成</h2>
<p class="success-desc">您的咨询信息已提交我们会尽快与您联系</p>
</div>
<div v-else class="mobile-consult-wrap">
<h2 class="mobile-consult-title">{{ title }}</h2>
<p class="mobile-consult-subtitle">期待与您开启AI创新未来</p>
<el-form
ref="consultForm"
:model="formData"
:rules="rules"
label-position="top"
class="mobile-consult-form"
>
<el-form-item label="1. 请填写您的姓名" prop="name">
<el-input
v-model.trim="formData.name"
maxlength="20"
placeholder="请输入您的姓名"
/>
</el-form-item>
<el-form-item label="2. 请填写您的联系方式" prop="phone">
<el-input
v-model.trim="formData.phone"
maxlength="11"
placeholder="请输入您的联系电话"
/>
</el-form-item>
<el-form-item label="3. 请填写您的邮箱" prop="email">
<el-input
v-model.trim="formData.email"
maxlength="60"
placeholder="请输入您的邮箱"
/>
</el-form-item>
<el-form-item label="4. 请填写您的公司名称" prop="company">
<el-input
v-model.trim="formData.company"
maxlength="80"
placeholder="请输入您的公司名称"
/>
</el-form-item>
<el-form-item label="5. 请选择您的企业类型(单选)" prop="enterprise_type">
<el-radio-group v-model="formData.enterprise_type" class="option-grid">
<el-radio
v-for="item in enterpriseOptions"
:key="item.id"
:label="item.id"
class="option-item"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="6. 请选择您所在的省份(单选)" prop="region">
<el-select
v-model="formData.region"
filterable
placeholder="输入省份名称或拼音首字母搜索..."
class="full-select"
>
<el-option
v-for="item in provinceOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="7. 您想咨询的方向(可多选)">
<el-checkbox-group v-model="formData.consult_direction_list" class="direction-list">
<el-checkbox
v-for="item in consultDirectionOptions"
:key="item.id"
:label="item.id"
class="direction-item"
>
<span class="direction-main">{{ item.title }}</span>
<span class="direction-sub">{{ item.desc }}</span>
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="8. 如果您有其他问题需要咨询,请留言">
<el-input
v-model.trim="formData.content"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
maxlength="500"
show-word-limit
placeholder="请输入您想咨询的内容..."
/>
</el-form-item>
</el-form>
<div class="qrcode-area">
<p>长按二维码添加微信获取专属服务</p>
<img src="@/assets/image/newkefu.png" alt="官方客服二维码">
</div>
<el-checkbox v-model="formData.checked" class="privacy-checkbox">
<span>
您填写的信息仅用于本次业务对接沟通公司将严格落实信息安全保护机制不泄露不滥用您的任何个人资料
</span>
</el-checkbox>
<el-button
type="primary"
class="submit-btn"
:loading="loading"
@click="handleSubmit"
>
提交咨询
</el-button>
</div>
</el-dialog>
</template>
<script>
import { reqConsultForm, reqProductConsult } from '@/api/H5'
export default {
name: 'MobileFullScreenConsultDialog',
props: {
visible: {
type: Boolean,
default: false
},
title: {
type: String,
default: '开元云科技合作咨询'
},
qrCode: {
type: String,
default: ''
},
currentUrl: {
type: String,
default: ''
},
submitApi: {
type: Function,
default: null
}
},
data() {
const validatePhone = (rule, value, callback) => {
if (!value) {
callback(new Error('请输入联系电话'))
} else if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error('请输入正确的手机号'))
} else {
callback()
}
}
return {
loading: false,
formData: this.getInitialFormData(),
enterpriseOptions: [],
provinceOptions: [],
consultDirectionOptions: [],
submitSuccess: false,
rules: {
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
phone: [{ required: true, validator: validatePhone, trigger: 'blur' }],
email: [
{ required: true, message: '请输入邮箱', trigger: 'blur' },
{ type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
],
company: [{ required: true, message: '请输入公司名称', trigger: 'blur' }],
enterprise_type: [{ required: true, message: '请选择企业类型', trigger: 'change' }],
region: [{ required: true, message: '请选择所在省份', trigger: 'change' }]
},
originalOverflow: ''
}
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(value) {
this.$emit('update:visible', value)
}
}
},
watch: {
visible: {
immediate: true,
handler(newVal) {
if (newVal) {
this.resetForm()
this.fetchConsultOptions()
this.disableBodyScroll()
} else {
this.enableBodyScroll()
}
}
}
},
methods: {
getInitialFormData() {
return {
custom_type: '1',
name: '',
phone: '',
email: '',
company: '',
enterprise_type: '',
region: '',
consult_direction_list: [],
content: '',
checked: false
}
},
resetForm() {
this.formData = this.getInitialFormData()
this.submitSuccess = false
this.$nextTick(() => {
if (this.$refs.consultForm) this.$refs.consultForm.clearValidate()
})
},
disableBodyScroll() {
this.originalOverflow = document.body.style.overflow
document.body.style.overflow = 'hidden'
},
enableBodyScroll() {
document.body.style.overflow = this.originalOverflow || ''
},
handleClose() {
this.dialogVisible = false
this.$emit('close')
},
normalizeDictOptions(list, type) {
return list
.filter(item => item && item.dict_type === type)
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0))
.map(item => ({
id: item.dict_key,
name: item.dict_value
}))
},
normalizeDirectionOptions(list) {
return list
.filter(item => item && item.dict_type === 'direction')
.sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0))
.map(item => {
const value = item.dict_value || ''
const match = value.match(/^(.+?)(.+?)$/)
return {
id: String(item.dict_key),
name: value,
title: match ? match[1] : value,
desc: match ? match[2] : ''
}
})
},
extractConsultOptionList(res) {
if (res && Array.isArray(res.data)) return res.data
if (res && res.data && Array.isArray(res.data.data)) return res.data.data
return []
},
async fetchConsultOptions() {
try {
const res = await reqConsultForm()
const list = this.extractConsultOptionList(res)
if (!list.length) return
const enterpriseOptions = this.normalizeDictOptions(list, 'enterprise_type')
const provinceOptions = this.normalizeDictOptions(list, 'region')
const consultDirectionOptions = this.normalizeDirectionOptions(list)
if (enterpriseOptions.length) this.enterpriseOptions = enterpriseOptions
if (provinceOptions.length) this.provinceOptions = provinceOptions
if (consultDirectionOptions.length) this.consultDirectionOptions = consultDirectionOptions
} catch (error) {
// 保留本地兜底选项,避免接口异常时无法填写表单。
}
},
buildSubmitPayload() {
return {
custom_type: this.formData.custom_type,
name: this.formData.name,
phone: this.formData.phone,
email: this.formData.email,
company: this.formData.company,
enterprise_type: this.formData.enterprise_type,
region: this.formData.region,
consult_direction: this.formData.consult_direction_list.join(','),
content: this.formData.content,
source: '公众号',
url_link: this.currentUrl || window.location.href
}
},
async handleSubmit() {
if (!this.formData.checked) {
this.$message.warning('请先勾选信息安全与隐私保护说明')
return
}
this.$refs.consultForm.validate(async valid => {
if (!valid) return
this.loading = true
try {
const payload = this.buildSubmitPayload()
const response = this.submitApi ? await this.submitApi(payload) : await reqProductConsult(payload)
if (response && (response.status === true || response.status === 'true')) {
this.$emit('success', response)
this.submitSuccess = true
} else {
this.$message.error((response && response.msg) || '提交失败,请稍后再试')
}
} catch (error) {
this.$message.error('提交失败,请稍后再试')
} finally {
this.loading = false
}
})
}
},
beforeDestroy() {
this.enableBodyScroll()
}
}
</script>
<style scoped lang="less">
::v-deep .mobile-consult-fullscreen-dialog {
margin: 0 !important;
width: 100% !important;
max-width: 100% !important;
height: 100vh;
border-radius: 0;
.el-dialog__header {
padding: 14px 14px 8px;
border-bottom: 0;
}
.el-dialog__title {
font-size: 18px;
font-weight: 700;
color: #111827;
}
.el-dialog__body {
height: calc(100vh - 60px);
padding: 12px 14px 18px;
overflow-y: auto;
}
}
.mobile-consult-wrap {
padding-bottom: 8px;
}
.submit-success-page {
min-height: calc(100vh - 110px);
padding: 80px 24px 24px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
box-sizing: border-box;
}
.success-icon {
width: 88px;
height: 88px;
border-radius: 50%;
background: #22c55e;
color: #fff;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 14px 28px rgba(34, 197, 94, 0.22);
i {
font-size: 46px;
font-weight: 700;
}
}
.success-title {
margin: 24px 0 10px;
color: #111827;
font-size: 24px;
font-weight: 700;
}
.mobile-consult-title {
margin: 0;
font-size: 24px;
line-height: 1.25;
color: #111827;
}
.mobile-consult-subtitle {
margin: 6px 0 16px;
font-size: 12px;
color: #9ca3af;
}
.mobile-consult-form {
::v-deep .el-form-item {
margin-bottom: 18px;
}
::v-deep .el-form-item__content,
::v-deep .el-input,
::v-deep .el-select,
::v-deep .el-textarea {
width: 100%;
}
::v-deep .el-form-item__label {
padding-bottom: 6px;
font-size: 13px;
line-height: 1.3;
color: #1f2937;
font-weight: 600;
}
::v-deep .el-form-item__error {
position: static;
padding-top: 6px;
line-height: 1.25;
font-size: 12px;
}
::v-deep .el-input__inner {
height: 40px;
border-radius: 10px;
border: 1px solid #e5e7eb;
}
::v-deep .el-form-item.is-error .el-input__inner,
::v-deep .el-form-item.is-error .el-textarea__inner {
border-color: #f56c6c;
}
::v-deep .el-textarea__inner {
border-radius: 10px;
border: 1px solid #e5e7eb;
}
}
.option-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
width: 100%;
}
.option-item {
margin-right: 0 !important;
padding: 9px 8px;
border: 1px solid #e5e7eb;
border-radius: 10px;
white-space: nowrap;
box-sizing: border-box;
display: flex;
align-items: center;
width: 100%;
::v-deep .el-radio__label {
padding-left: 6px;
font-size: 12px;
color: #374151;
}
}
.full-select {
width: 100%;
}
.direction-list {
display: flex;
flex-direction: column;
gap: 8px;
width: 100%;
}
.direction-item {
margin-right: 0 !important;
border: 1px solid #e5e7eb;
border-radius: 10px;
padding: 10px;
display: flex;
align-items: flex-start;
width: 100%;
box-sizing: border-box;
::v-deep .el-checkbox__label {
display: inline-flex;
flex-direction: column;
gap: 2px;
padding-left: 8px;
}
}
.direction-main {
font-size: 14px;
color: #111827;
line-height: 1.2;
}
.direction-sub {
font-size: 12px;
color: #9ca3af;
line-height: 1.2;
}
.qrcode-area {
margin: 16px 0;
text-align: center;
padding: 18px 0;
background: #f7f8fa;
border-radius: 12px;
p {
margin: 0 0 10px;
font-size: 12px;
color: #6b7280;
}
img {
width: 98px;
height: 98px;
border-radius: 8px;
border: 1px dashed #3b82f6;
}
}
.privacy-checkbox {
margin: 0 0 14px;
display: flex;
align-items: flex-start;
::v-deep .el-checkbox__input {
margin-top: 3px;
}
::v-deep .el-checkbox__label {
padding-left: 8px;
color: #6b7280;
font-size: 12px;
line-height: 1.6;
white-space: normal;
}
}
.submit-btn {
width: 100%;
height: 44px;
border: none;
border-radius: 999px;
background: #111827;
color: #fff;
font-size: 16px;
font-weight: 600;
}
</style>