2026-08-04 16:58:16 +08:00

549 lines
14 KiB
Vue

<template>
<div>
<el-dialog
:title="$t('contactSales.title')"
:visible="dialogVisible"
width="400px"
center
append-to-body
:close-on-click-modal="false"
top="8vh"
custom-class="talk-dialog"
@close="cancelBtn"
@update:visible="handleDialogVisibleUpdate"
>
<div class="talk-body">
<div class="talk-form">
<el-form ref="ruleForm" :rules="rules" label-position="top" label-width="80px" :model="addData">
<div class="form-grid">
<el-form-item :label="$t('contactSales.nameLabel')" prop="name">
<el-input v-model.trim="addData.name" maxlength="20" :placeholder="$t('contactSales.namePlaceholder')" />
</el-form-item>
<el-form-item prop="phone" :label="$t('contactSales.phoneLabel')">
<el-input v-model.trim="addData.phone" maxlength="11" :placeholder="$t('contactSales.phonePlaceholder')" />
</el-form-item>
<el-form-item :label="$t('contactSales.emailLabel')" prop="email" class="form-item--compact">
<el-input v-model.trim="addData.email" maxlength="60" :placeholder="$t('contactSales.emailPlaceholder')" />
</el-form-item>
<el-form-item :label="$t('contactSales.companyLabel')" prop="company" class="form-item--compact">
<el-input v-model.trim="addData.company" maxlength="80" :placeholder="$t('contactSales.companyPlaceholder')" />
</el-form-item>
<el-form-item :label="$t('contactSales.enterpriseLabel')" prop="enterprise_type" class="form-item--full">
<el-radio-group v-model="addData.enterprise_type" class="option-grid">
<el-radio
v-for="item in localizedEnterpriseOptions"
:key="item.id"
:label="item.id"
class="option-item"
>
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('contactSales.provinceLabel')" prop="region" class="form-item--full">
<el-select
v-model="addData.region"
filterable
:placeholder="$t('contactSales.provincePlaceholder')"
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="$t('contactSales.messageLabel')" class="form-item--message">
<el-input
v-model.trim="addData.content"
type="textarea"
:autosize="{ minRows: 3, maxRows: 5 }"
maxlength="500"
show-word-limit
:placeholder="$t('contactSales.messagePlaceholder')"
/>
</el-form-item>
</div>
</el-form>
<el-checkbox v-model="checked" class="agreement-checkbox">
{{ $t('contactSales.privacy') }}
</el-checkbox>
</div>
<div v-if="qrCodeUrl" class="qcode">
<img :src="qrCodeUrl" alt="官方客服二维码">
<span>{{ $t('contactSales.qrcode') }}</span>
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button class="cancel-btn" @click="cancelBtn">{{ $t('contactSales.cancel') }}</el-button>
<el-button class="submit-btn" type="primary" :loading="addBtnLoading" @click="confirmBtn">{{ $t('contactSales.submit') }}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import Vue from 'vue'
import { mapState } from 'vuex'
import { reqNewHomeConsult } from '@/api/newHome'
import { reqConsultForm } from '@/api/H5'
export default Vue.extend({
name: 'Talk',
data() {
const validatePhone = (rule, value, callback) => {
if (!value) {
callback(new Error(this.$t('contactSales.phonePlaceholder')))
} else if (!/^1[3-9]\d{9}$/.test(value)) {
callback(new Error(this.$t('contactSales.phoneInvalid') || '请输入正确的手机号'))
} else {
callback()
}
}
return {
rules: {
name: [
{ required: true, message: this.$t('contactSales.namePlaceholder'), trigger: 'blur' }
],
phone: [
{ required: true, validator: validatePhone, trigger: 'blur' }
],
email: [
{ required: true, message: this.$t('contactSales.emailPlaceholder'), trigger: 'blur' },
{ type: 'email', message: this.$t('contactSales.emailInvalid') || '请输入正确的邮箱地址', trigger: 'blur' }
],
company: [
{ required: true, message: this.$t('contactSales.companyPlaceholder'), trigger: 'blur' }
],
enterprise_type: [
{ required: true, message: this.$t('contactSales.enterpriseRequired') || '请选择企业类型', trigger: 'change' }
],
region: [
{ required: true, message: this.$t('contactSales.provinceRequired') || '请选择所在省份', trigger: 'change' }
]
},
addBtnLoading: false,
checked: false,
dialogVisible: false,
enterpriseOptions: [],
provinceOptions: [],
addData: {
content: '', // 需求内容
custom_type: '1', // 客户类型 0-个人 1-企业
name: '', // 姓名
phone: '', // 手机号
company: '', // 公司名称
email: '', // 邮箱
enterprise_type: '',
region: ''
},
labelPosition: 'right',
formLabelAlign: {
name: '',
region: '',
type: ''
}
}
},
computed: {
...mapState({
showTalk: (state) => state.product.showTalk,
logoInfoNew: state => state.product.logoInfoNew
}),
qrCodeUrl() {
return this.logoInfoNew && this.logoInfoNew.home && this.logoInfoNew.home.qrCode
? this.logoInfoNew.home.qrCode
: ''
},
localizedEnterpriseOptions() {
return this.enterpriseOptions.map(item => ({
...item,
name: this.getEnterpriseOptionName(item.name)
}))
}
},
watch: {
showTalk: {
immediate: true,
handler(value) {
this.dialogVisible = value
if (value) {
this.fetchConsultOptions()
}
}
}
},
methods: {
getEnterpriseOptionName(name) {
const map = {
大型企业: 'largeEnterprise',
中小企业: 'sme',
OPC个人: 'opcIndividual',
高校科研机构: 'university',
高校: 'university',
政府: 'government',
其他: 'other'
}
const key = map[name]
if (!key) return name
const i18nKey = `contactSales.enterpriseOptions.${key}`
const translated = this.$t(i18nKey)
return translated === i18nKey ? name : translated
},
getDefaultFormData() {
return {
content: '',
custom_type: '1',
name: '',
phone: '',
company: '',
email: '',
enterprise_type: '',
region: ''
}
},
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
}))
},
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')
if (enterpriseOptions.length) this.enterpriseOptions = enterpriseOptions
if (provinceOptions.length) this.provinceOptions = provinceOptions
} catch (error) {
// 字典接口异常时保持当前选项,避免影响基础提交。
}
},
handleDialogVisibleUpdate(value) {
this.dialogVisible = value
if (!value) {
this.$store.commit('setShowTalk', false)
}
},
cancelBtn() {
this.dialogVisible = false
this.$store.commit('setShowTalk', false)
},
confirmBtn() {
if (!this.checked) {
this.$message.warning(this.$t('contactSales.privacyRequired') || '请勾选同意协议后再提交!')
return
}
this.$refs['ruleForm'].validate((valid) => {
if (valid) {
this.addBtnLoading = true
const submitData = {
...this.addData,
source: '官网',
url_link: window.location.href
}
reqNewHomeConsult(submitData).then(response => {
this.addBtnLoading = false
if (response.status) {
this.$message({
type: 'success',
message: '感谢您关注人工智能服务平台,我们将尽快联系您!~'
})
this.$store.commit('setShowTalk', false)
this.addData = this.getDefaultFormData()
this.checked = false
} else {
this.$message.error(response.msg || '提交失败,请稍后再试!')
}
}).catch(error => {
console.error('提交咨询失败:', error)
this.$message.error('提交失败,请稍后再试!')
})
} else {
this.$message.error(this.$t('contactSales.formInvalid') || '请完善表单信息~')
return false
}
})
}
}
})
</script>
<style scoped lang="scss">
::v-deep .talk-dialog {
border-radius: 22px;
overflow: hidden;
box-shadow: 0 24px 70px rgba(15, 23, 42, 0.18);
// z-index: 99999999999999999!important;
width: 600px !important;
}
::v-deep .el-dialog__header {
display: flex;
justify-content: center;
align-items: center;
padding: 18px 22px 6px;
border: none;
}
::v-deep .el-dialog__title {
color: #111827;
font-size: 22px;
line-height: 1.4;
font-weight: 700;
}
::v-deep .el-dialog__headerbtn {
top: 26px;
right: 28px;
font-size: 20px;
}
::v-deep .el-dialog__body {
padding: 6px 24px 4px;
}
::v-deep .el-form-item__label {
padding-bottom: 8px;
color: #374151;
font-size: 15px;
line-height: 1.4;
font-weight: 600;
}
::v-deep .el-form-item {
margin-bottom: 18px;
}
::v-deep .el-form-item__error {
position: static;
padding-top: 6px;
line-height: 1.25;
font-size: 12px;
}
::v-deep .el-input__inner,
::v-deep .el-textarea__inner {
border-radius: 12px;
border-color: #e5eaf3;
color: #111827;
font-size: 15px;
pointer-events: auto;
user-select: text;
}
::v-deep .el-input__inner {
height: 42px;
line-height: 42px;
}
::v-deep .el-textarea__inner {
padding: 12px 14px;
line-height: 1.7;
}
::v-deep .el-radio__label {
color: #374151;
font-size: 15px;
}
.talk-body {
position: relative;
min-height: 430px;
padding-bottom: 8px;
display: block;
}
.talk-form {
width: 100%;
min-width: 0;
padding-right: 0;
}
::v-deep .talk-form .el-form-item__content,
::v-deep .talk-form .el-input,
::v-deep .talk-form .el-select,
::v-deep .talk-form .el-textarea {
width: 100%;
}
.form-grid {
display: block;
}
.form-item--compact {
width: 100%;
}
.form-item--full {
width: 100%;
}
.form-item--message {
width: calc(100% - 184px);
}
.full-select {
width: 100%;
}
.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 #e5eaf3;
border-radius: 12px;
white-space: nowrap;
box-sizing: border-box;
width: 100%;
::v-deep .el-radio__label {
padding-left: 6px;
color: #374151;
font-size: 13px;
}
}
.agreement-checkbox {
margin-top: 8px;
max-width: calc(100% - 184px);
display: inline-flex;
align-items: flex-start;
::v-deep .el-checkbox__label {
color: #6b7280;
font-size: 13px;
line-height: 1.7;
white-space: normal;
}
::v-deep .el-checkbox__input {
margin-top: 4px;
}
}
.qcode {
width: 150px;
min-height: 162px;
padding: 12px 10px;
position: absolute;
right: 0;
bottom: 18px;
border-radius: 18px;
background: linear-gradient(180deg, #f4f8ff 0%, #ffffff 100%);
border: 1px solid #edf1f7;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
box-sizing: border-box;
img {
width: 104px;
height: 104px;
object-fit: cover;
border-radius: 12px;
}
span {
margin-top: 10px;
color: #4b5563;
font-size: 13px;
font-weight: 600;
}
}
::v-deep .el-dialog__footer {
display: flex;
justify-content: center;
align-items: center;
gap: 14px;
padding: 6px 22px 22px;
}
.cancel-btn,
.submit-btn {
min-width: 150px;
height: 42px;
padding: 0 28px;
border-radius: 999px;
font-size: 15px;
font-weight: 600;
}
.cancel-btn {
border: 1px solid #d8e0ef;
color: #4b5563;
background: #fff;
}
.submit-btn {
border: 0;
background: linear-gradient(90deg, #275AFF 0%, #2EBDFA 100%);
box-shadow: 0 12px 28px rgba(39, 90, 255, 0.22);
}
@media (max-width: 900px) {
::v-deep .talk-dialog {
width: 92vw !important;
}
.talk-body {
min-height: auto;
display: block;
padding-right: 0;
padding-bottom: 8px;
}
.talk-form {
padding-right: 0;
}
.qcode {
position: static;
width: 100%;
min-height: auto;
margin-top: 20px;
}
.form-item--compact {
width: 100%;
}
.form-item--full {
width: 100%;
}
.form-item--message {
width: 100%;
}
.agreement-checkbox {
max-width: 100%;
}
}
</style>