Files
Yi.Admin/Yi.Ai.Vue3/src/components/ContactUs/index.vue
2026-01-11 14:13:43 +08:00

586 lines
12 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.
<script setup lang="ts">
import { ElImageViewer, ElMessage } from 'element-plus';
import { computed, onBeforeUnmount, onMounted, ref } from 'vue';
import { contactConfig } from '@/config/constants';
export interface ContactType {
customerService?: boolean; // 客服微信和二维码
communityGroup?: boolean; // 交流群二维码
afterSalesGroup?: boolean; // 售后群二维码
other?: boolean; // 其他
}
export type ContactScenario = 'regular' | 'afterSales' | 'all' | 'custom';
interface Props {
scenario?: ContactScenario; // 场景类型
customTypes?: ContactType; // 自定义展示类型
}
const props = withDefaults(defineProps<Props>(), {
scenario: 'regular',
});
const emit = defineEmits(['close']);
const visible = ref(true);
const showImageViewer = ref(false);
const currentImageUrl = ref('');
const isMobile = ref(false);
// 检测移动端
function checkMobile() {
isMobile.value = window.innerWidth < 768;
}
onMounted(() => {
checkMobile();
window.addEventListener('resize', checkMobile);
});
onBeforeUnmount(() => {
window.removeEventListener('resize', checkMobile);
});
// 根据场景计算需要展示的联系方式
const contactTypes = computed<ContactType>(() => {
if (props.scenario === 'custom' && props.customTypes) {
return props.customTypes;
}
switch (props.scenario) {
case 'regular':
return {
customerService: true,
communityGroup: true,
afterSalesGroup: false,
other: false,
};
case 'afterSales':
return {
customerService: true,
communityGroup: false,
afterSalesGroup: true,
other: false,
};
case 'all':
return {
customerService: true,
communityGroup: true,
afterSalesGroup: true,
other: true,
};
default:
return {
customerService: true,
communityGroup: true,
afterSalesGroup: false,
other: false,
};
}
});
function close() {
visible.value = false;
emit('close');
}
function onClose() {
emit('close');
}
// 复制微信号
function copyWechatId() {
navigator.clipboard.writeText(contactConfig.wechatId).then(() => {
ElMessage.success('微信号已复制');
}).catch(() => {
ElMessage.error('复制失败,请手动复制');
});
}
// 查看大图
function viewImage(imageUrl: string) {
currentImageUrl.value = imageUrl;
showImageViewer.value = true;
}
// 关闭图片查看器
function closeImageViewer() {
showImageViewer.value = false;
currentImageUrl.value = '';
}
</script>
<template>
<el-dialog
v-model="visible"
title="联系我们"
:width="isMobile ? '95%' : '600px'"
:fullscreen="isMobile"
:show-close="true"
destroy-on-close
class="contact-us-dialog"
@close="onClose"
>
<div class="contact-us-content">
<!-- 客服微信 -->
<div v-if="contactTypes.customerService" class="contact-section">
<div class="section-header">
<el-icon class="section-icon">
<i-ep-user />
</el-icon>
<h3 class="section-title">
客服微信
</h3>
</div>
<div class="section-content">
<div class="info-text">
<p class="info-label">
微信号
</p>
<p class="info-value">
{{ contactConfig.wechatId }}
</p>
<el-button size="small" type="primary" @click="copyWechatId">
复制微信号
</el-button>
</div>
<div v-if="contactConfig.images.customerService" class="qr-code">
<img
:src="contactConfig.images.customerService"
alt="客服微信二维码"
class="qr-image"
@click="viewImage(contactConfig.images.customerService)"
>
<p class="qr-label">
扫码添加客服点击放大
</p>
</div>
</div>
</div>
<!-- 交流群 -->
<div v-if="contactTypes.communityGroup" class="contact-section">
<div class="section-header">
<el-icon class="section-icon">
<i-ep-chat-dot-round />
</el-icon>
<h3 class="section-title">
交流群
</h3>
</div>
<div class="section-content">
<div class="info-text">
<p class="info-desc">
加入我们的交流群与其他用户一起探讨AI应用获取最新产品动态
</p>
</div>
<div v-if="contactConfig.images.communityGroup" class="qr-code">
<img
:src="contactConfig.images.communityGroup"
alt="交流群二维码"
class="qr-image"
@click="viewImage(contactConfig.images.communityGroup)"
>
<p class="qr-label">
扫码加入交流群点击放大
</p>
</div>
</div>
</div>
<!-- 售后群 -->
<div v-if="contactTypes.afterSalesGroup" class="contact-section">
<div class="section-header">
<el-icon class="section-icon">
<i-ep-service />
</el-icon>
<h3 class="section-title">
VIP售后服务群
</h3>
</div>
<div class="section-content">
<div class="info-text">
<p class="info-desc">
<el-tag type="warning" size="small">
VIP专享
</el-tag>
充值后加客服微信回复账号名可专享VIP售后服务
</p>
</div>
<div v-if="contactConfig.images.afterSalesGroup" class="qr-code">
<img
:src="contactConfig.images.afterSalesGroup"
alt="售后群二维码"
class="qr-image"
@click="viewImage(contactConfig.images.afterSalesGroup)"
>
<p class="qr-label">
扫码加入售后群点击放大
</p>
</div>
<div v-else class="qr-code-empty">
<el-alert
type="info"
:closable="false"
show-icon
>
<template #title>
<p>请联系客服微信 <strong>{{ contactConfig.wechatId }}</strong> 加入售后群</p>
</template>
</el-alert>
</div>
</div>
</div>
<!-- 温馨提示 -->
<el-alert
class="tips-alert"
type="info"
:closable="false"
show-icon
>
<template #title>
<div class="tips-content">
<p>温馨提示</p>
<ul>
<li>添加客服时请备注您的账号名称以便更好地为您服务</li>
<li>工作时间9:00-22:00我们会尽快回复您的消息</li>
<li>VIP用户可享受专属售后服务响应更快</li>
</ul>
</div>
</template>
</el-alert>
</div>
<!-- 图片查看器 -->
<ElImageViewer
v-if="showImageViewer"
:url-list="[currentImageUrl]"
:hide-on-click-modal="true"
@close="closeImageViewer"
/>
</el-dialog>
</template>
<style scoped lang="scss">
.contact-us-dialog {
:deep(.el-dialog__header) {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 20px;
border-radius: 8px 8px 0 0;
.el-dialog__title {
color: white;
font-size: 20px;
font-weight: 700;
}
.el-dialog__headerbtn .el-dialog__close {
color: white;
font-size: 20px;
&:hover {
color: #f0f0f0;
}
}
}
:deep(.el-dialog__body) {
padding: 24px;
}
}
.contact-us-content {
display: flex;
flex-direction: column;
gap: 24px;
}
.contact-section {
background: #fafbfc;
border-radius: 12px;
padding: 20px;
border: 1px solid #e8ecf0;
transition: all 0.3s;
&:hover {
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1);
}
}
.section-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 2px solid #e8ecf0;
}
.section-icon {
font-size: 24px;
color: #667eea;
background: rgba(102, 126, 234, 0.1);
padding: 8px;
border-radius: 8px;
}
.section-title {
margin: 0;
font-size: 18px;
font-weight: 700;
color: #1a1a1a;
}
.section-content {
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
}
.info-text {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
.info-label {
margin: 0;
font-size: 14px;
color: #909399;
font-weight: 500;
}
.info-value {
margin: 0;
font-size: 20px;
font-weight: 700;
color: #667eea;
font-family: 'Courier New', monospace;
}
.info-desc {
margin: 0;
font-size: 14px;
color: #606266;
line-height: 1.6;
}
.qr-code {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
.qr-image {
width: 120px;
height: auto;
border-radius: 8px;
border: 2px solid #e8ecf0;
padding: 4px;
background: white;
cursor: pointer;
transition: all 0.3s;
&:hover {
transform: scale(1.05);
border-color: #667eea;
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
}
}
.qr-label {
margin: 0;
font-size: 12px;
color: #909399;
text-align: center;
}
.tips-alert {
border-radius: 8px;
background: #f0f9ff;
border-color: #bae6fd;
.tips-content p {
margin: 0 0 8px 0;
font-weight: 600;
color: #0369a1;
}
.tips-content ul {
margin: 0;
padding-left: 20px;
}
.tips-content li {
margin: 4px 0;
font-size: 13px;
color: #0c4a6e;
}
}
/* 响应式布局 */
@media (max-width: 768px) {
.contact-us-dialog {
:deep(.el-dialog) {
width: 100% !important;
margin: 0 !important;
height: 100vh;
max-height: 100vh;
border-radius: 0;
display: flex;
flex-direction: column;
}
:deep(.el-dialog__header) {
padding: 16px;
flex-shrink: 0;
}
:deep(.el-dialog__body) {
padding: 12px;
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
}
.contact-us-content {
gap: 16px;
}
.contact-section {
padding: 14px;
}
.section-header {
flex-direction: row;
align-items: center;
gap: 10px;
margin-bottom: 12px;
padding-bottom: 10px;
.section-icon {
font-size: 20px;
padding: 6px;
}
.section-title {
font-size: 16px;
}
}
.section-content {
flex-direction: column;
align-items: flex-start;
gap: 14px;
}
.info-text {
width: 100%;
gap: 8px;
.info-label {
font-size: 13px;
}
.info-value {
font-size: 16px;
}
.info-desc {
font-size: 13px;
}
.el-button {
width: 100%;
}
}
.qr-code {
align-self: center;
width: 100%;
padding: 12px;
background: #f9fafb;
border-radius: 8px;
.qr-image {
width: 140px;
height: auto;
}
.qr-label {
font-size: 12px;
}
}
.qr-code-empty {
width: 100%;
.el-alert {
:deep(.el-alert__content) {
font-size: 13px;
}
}
}
.tips-alert {
:deep(.el-alert__content) {
.tips-content {
p {
font-size: 13px;
margin-bottom: 6px;
}
ul {
padding-left: 16px;
}
li {
font-size: 12px;
margin: 3px 0;
}
}
}
}
}
/* 小屏手机适配 */
@media (max-width: 480px) {
.contact-us-dialog {
:deep(.el-dialog__header) {
padding: 14px;
.el-dialog__title {
font-size: 17px;
}
}
:deep(.el-dialog__body) {
padding: 10px;
}
}
.contact-section {
padding: 12px;
}
.section-title {
font-size: 15px;
}
.qr-code .qr-image {
width: 120px;
height: auto;
}
}
</style>