feat: 实现心理健康评估系统核心功能
添加心理健康评估系统前端核心组件和功能,包括: 1. 评估表单发送与展示功能 2. 客户信息分析页面 3. 智能回复系统集成 4. 企业微信SDK集成 5. 响应式设计和移动端适配 实现与后端API的交互逻辑,包括客户信息获取、表单提交和智能回复生成
This commit is contained in:
334
src/components/SendPage.vue
Normal file
334
src/components/SendPage.vue
Normal file
@@ -0,0 +1,334 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🌟 青少年心理健康评估</h1>
|
||||
<p class="subtitle">专业、科学、个性化的心理健康分析</p>
|
||||
</div>
|
||||
|
||||
<div class="cta-section">
|
||||
<h2>开始您的专业评估之旅</h2>
|
||||
<p class="cta-description">
|
||||
只需5-10分钟,即可获得专业的家庭教育档案信息报告
|
||||
</p>
|
||||
|
||||
<!-- 修改部分:按钮增加了 disabled 属性、动态 class 和动态文本 -->
|
||||
<button
|
||||
class="cta-button"
|
||||
:class="{ 'disabled': isCoolingDown }"
|
||||
@click="handleSendForm"
|
||||
:disabled="isCoolingDown"
|
||||
>
|
||||
{{ isCoolingDown ? `📝 请等待 ${countdown} 秒...` : '📝 发送评估表' }}
|
||||
</button>
|
||||
|
||||
<p class="note">
|
||||
💡 评估完全免费,结果仅供参考,如需专业帮助请咨询心理医生
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import * as ww from '@wecom/jssdk'
|
||||
|
||||
const isWWReady = ref(false)
|
||||
const errorMessage = ref<string | null>(null)
|
||||
|
||||
// --- 防重复点击逻辑 ---
|
||||
const isCoolingDown = ref(false) // 是否处于冷却期
|
||||
const countdown = ref(0) // 倒计时秒数
|
||||
|
||||
const handleSendForm = () => {
|
||||
// 如果正在冷却中,直接阻断
|
||||
if (isCoolingDown.value) return
|
||||
|
||||
// 启动10秒倒计时
|
||||
startCooldown(10)
|
||||
|
||||
// 执行业务逻辑
|
||||
GetFormUrl()
|
||||
}
|
||||
|
||||
// 倒计时工具函数
|
||||
const startCooldown = (seconds: number) => {
|
||||
isCoolingDown.value = true
|
||||
countdown.value = seconds
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdown.value--
|
||||
if (countdown.value <= 0) {
|
||||
clearInterval(timer)
|
||||
isCoolingDown.value = false // 倒计时结束,恢复按钮可用
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
// --------------------
|
||||
|
||||
async function getConfigSignature(url: string) {
|
||||
try {
|
||||
const response = await fetch('https://sidebar.wx.nycjy.cn/api/v1/wecom/config-signature', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ url })
|
||||
})
|
||||
if (!response.ok) throw new Error('获取企业签名失败')
|
||||
return await response.json()
|
||||
} catch (error) {
|
||||
console.error('[getConfigSignature]', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function getAgentConfigSignature(url: string) {
|
||||
try {
|
||||
const response = await fetch('https://sidebar.wx.nycjy.cn/api/v1/wecom/agent-config-signature', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ url })
|
||||
})
|
||||
if (!response.ok) throw new Error('获取应用签名失败')
|
||||
return await response.json()
|
||||
} catch (error) {
|
||||
console.error('[getAgentConfigSignature]', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const initWeWork = async () => {
|
||||
try {
|
||||
await ww.register({
|
||||
corpId: 'wwf72acc5a681dca93',
|
||||
agentId: 1000105,
|
||||
jsApiList: ['sendChatMessage', 'getCurExternalContact', 'getContext'],
|
||||
getConfigSignature,
|
||||
getAgentConfigSignature
|
||||
})
|
||||
isWWReady.value = true
|
||||
} catch (error) {
|
||||
isWWReady.value = false
|
||||
errorMessage.value = '企业微信JS-SDK初始化失败,请刷新页面重试。'
|
||||
}
|
||||
}
|
||||
|
||||
const getUserInfo = async () => {
|
||||
try {
|
||||
const response = await ww.getCurExternalContact()
|
||||
return response.userId
|
||||
} catch (error) {
|
||||
console.error('[getCurExternalContact] 失败', error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const GetFormUrl = async () => {
|
||||
try {
|
||||
const userId = await getUserInfo()
|
||||
const response = await fetch(`https://liaison.nycjy.cn/api/v1/archive/form-url?user_id=${userId}`, {
|
||||
method: 'GET',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
})
|
||||
|
||||
const formUrl = await response.json()
|
||||
console.log('[GetFormUrl]', formUrl.data)
|
||||
sendFormLink(formUrl.data.form_url)
|
||||
if (!response.ok) throw new Error('获取表单URL失败')
|
||||
} catch (error) {
|
||||
console.error('[GetFormUrl]', error)
|
||||
// 如果希望出错时允许立即重试,可以在这里取消冷却:
|
||||
// isCoolingDown.value = false
|
||||
alert('请求失败,请检查网络或稍后重试。')
|
||||
}
|
||||
}
|
||||
|
||||
const sendFormLink = async (formUrl: string) => {
|
||||
if (!isWWReady.value) {
|
||||
alert('企业微信功能尚未准备好,请稍等片刻...')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await ww.sendChatMessage({
|
||||
msgtype: 'news',
|
||||
news: {
|
||||
title: '家庭教育档案信息表',
|
||||
desc: '通过专业的评估工具,了解孩子的成长需求,为每个孩子制定个性化的成长方案',
|
||||
imgUrl: 'https://forms.nycjy.cn/favicon.ico',
|
||||
link: formUrl
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
console.error('发送消息失败:', error)
|
||||
alert('发送失败,详情请查看控制台日志。')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await initWeWork()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
}
|
||||
|
||||
.header {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 1.2rem;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 2rem;
|
||||
margin-bottom: 4rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 16px 48px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.card.highlight {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.card h3 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 1rem;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.card p {
|
||||
color: inherit;
|
||||
opacity: 0.9;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
text-align: center;
|
||||
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||
border-radius: 20px;
|
||||
padding: 3rem 2rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.cta-section h2 {
|
||||
font-size: 2rem;
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.cta-description {
|
||||
font-size: 1.1rem;
|
||||
color: #5a6c7d;
|
||||
margin-bottom: 2rem;
|
||||
max-width: 600px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.cta-button {
|
||||
background: linear-gradient(135deg, #4CAF50, #45a049);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1rem 2.5rem;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
border-radius: 50px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(76, 175, 80, 0.3);
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.cta-button:hover {
|
||||
background: linear-gradient(135deg, #45a049, #3d8b40);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 25px rgba(76, 175, 80, 0.4);
|
||||
}
|
||||
|
||||
/* 禁用状态样式 */
|
||||
.cta-button.disabled {
|
||||
background: #bdc3c7; /* 灰色背景 */
|
||||
cursor: not-allowed;
|
||||
transform: none; /* 移除悬浮效果 */
|
||||
box-shadow: none;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.cta-button.disabled:hover {
|
||||
background: #bdc3c7;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.note {
|
||||
font-size: 0.9rem;
|
||||
color: #7f8c8d;
|
||||
margin: 0;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.cta-section {
|
||||
padding: 2rem 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user