m-Index.vue 28 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094
  1. <template>
  2. <div class="mobile-container">
  3. <!-- 顶部logo -->
  4. <div class="mobile-header">
  5. <div class="logo">
  6. <img src="@/assets/new_index/1.png" alt="logo" class="logo-img">
  7. </div>
  8. <!-- 用户信息区域 -->
  9. <div class="mobile-user-info" @mouseenter="showDropdown = true" @mouseleave="showDropdown = false">
  10. <div class="mobile-user-avatar">
  11. <div class="mobile-avatar-icon"></div>
  12. </div>
  13. <span class="mobile-username">{{ userInfo?.username || '用户' }}</span>
  14. <!-- 下拉菜单 -->
  15. <div v-if="showDropdown" class="mobile-dropdown-menu">
  16. <div class="mobile-dropdown-item mobile-logout-item" @click="handleLogout">
  17. <span>返回APP</span>
  18. </div>
  19. </div>
  20. </div>
  21. </div>
  22. <!-- 主内容区域 -->
  23. <div class="mobile-main-content">
  24. <!-- 主标题 -->
  25. <h1 class="mobile-main-title">蜀道安全管理AI智能助手</h1>
  26. <p class="mobile-sub-title">安全法规问答,智能识图提示,AI赋能筑造安心与高效</p>
  27. <!-- 搜索框 -->
  28. <div class="mobile-search-container">
  29. <div class="mobile-search-box">
  30. <input
  31. type="text"
  32. placeholder="请输入您想问的问题..."
  33. class="mobile-search-input"
  34. v-model="searchText"
  35. @keyup.enter="handleSearch"
  36. >
  37. <button class="mobile-voice-btn" @click="handleVoiceClick" :disabled="isSending" :class="{ 'recording': isListening }">
  38. <div class="mobile-icon-container">
  39. <img :src="voiceInputIcon" alt="语音" class="mobile-action-icon">
  40. <div v-if="isListening" class="mobile-recording-indicator"></div>
  41. </div>
  42. </button>
  43. <div class="mobile-divider"></div>
  44. <button class="mobile-send-btn" @click="handleSearch" :disabled="isSending || !searchText.trim()">
  45. <img :src="searchText.trim() && !isSending ? sendIconFilled : sendIconEmpty" alt="发送" class="mobile-send-icon">
  46. </button>
  47. </div>
  48. </div>
  49. <!-- 移动端卡片区域 -->
  50. <div class="mobile-cards-container">
  51. <!-- 常见问题卡片 -->
  52. <div class="mobile-common-questions-card">
  53. <div class="mobile-card-header">
  54. <div class="mobile-card-title-section">
  55. <img src="@/assets/new_index/13.png" alt="场景问题" class="mobile-card-avatar">
  56. <span class="mobile-card-title">场景问题</span>
  57. </div>
  58. <div class="mobile-refresh-button" @click="refreshQuestions">
  59. <span class="mobile-refresh-text">换一换</span>
  60. </div>
  61. </div>
  62. <div class="mobile-questions-content">
  63. <div class="mobile-questions-list">
  64. <div class="mobile-question-item" @click="goToAIQuestion(item.question)" v-for="(item, index) in recommendQuestions" :key="index">
  65. {{ index + 1 }}. {{ item.question }}
  66. </div>
  67. </div>
  68. </div>
  69. </div>
  70. <!-- 服务功能网格 -->
  71. <div class="mobile-services-grid">
  72. <div class="mobile-service-item" @click="goToHazardDetection">
  73. <div class="mobile-service-icon">
  74. <img src="@/assets/new_index/4.png" alt="隐患提示" class="mobile-service-bg">
  75. </div>
  76. <div class="mobile-service-info mobile-service-info-large">
  77. <div class="mobile-service-title mobile-service-title-large">隐患提示</div>
  78. <div class="mobile-service-desc mobile-service-desc-large">图片智能识别,风险隐患提示</div>
  79. </div>
  80. </div>
  81. <div class="mobile-service-item" @click="goToSafetyTraining">
  82. <div class="mobile-service-icon">
  83. <img src="@/assets/new_index/5.png" alt="安全培训" class="mobile-service-bg">
  84. </div>
  85. <div class="mobile-service-info mobile-service-info-large">
  86. <div class="mobile-service-title mobile-service-title-large">安全培训</div>
  87. <div class="mobile-service-desc mobile-service-desc-large">智能编排大纲,生成精美演示文稿</div>
  88. </div>
  89. </div>
  90. </div>
  91. <!-- 四个服务功能单独容器 -->
  92. <div class="mobile-four-services">
  93. <div class="mobile-service-item" @click="goToExamWorkshop">
  94. <div class="mobile-service-header">
  95. <div class="mobile-service-icon">
  96. <img src="@/assets/new_index/6-1.png" alt="考试工坊" class="mobile-icon-img">
  97. </div>
  98. <div class="mobile-service-title">考试工坊</div>
  99. </div>
  100. <div class="mobile-service-description">让组卷更省心,让出题更精准</div>
  101. <div class="mobile-service-tag" style="color: #2563EB;">生成考题 ›</div>
  102. </div>
  103. <div class="mobile-service-item" @click="goToAIWriting">
  104. <div class="mobile-service-header">
  105. <div class="mobile-service-icon">
  106. <img src="@/assets/new_index/7-1.png" alt="AI写作" class="mobile-icon-img">
  107. </div>
  108. <div class="mobile-service-title">AI写作</div>
  109. </div>
  110. <div class="mobile-service-description">一键创作公文,让文案更专业</div>
  111. <div class="mobile-service-tag" style="color: #16A34A;">开始创作 ›</div>
  112. </div>
  113. <div class="mobile-service-item" @click="goToPolicyDocument">
  114. <div class="mobile-service-header">
  115. <div class="mobile-service-icon">
  116. <img src="@/assets/new_index/8-1.png" alt="政策文件" class="mobile-icon-img">
  117. </div>
  118. <div class="mobile-service-title">政策文件</div>
  119. </div>
  120. <div class="mobile-service-description">汇集国家、行业及集团政策文件</div>
  121. <div class="mobile-service-tag" style="color: #EA580C;">了解更多 ›</div>
  122. </div>
  123. <div class="mobile-service-item" @click="openFeedbackModal">
  124. <div class="mobile-service-header">
  125. <div class="mobile-service-icon">
  126. <img src="@/assets/new_index/9-1.png" alt="意见反馈" class="mobile-icon-img">
  127. </div>
  128. <div class="mobile-service-title">意见反馈</div>
  129. </div>
  130. <div class="mobile-service-description">助力产品升级,期待您的反馈</div>
  131. <div class="mobile-service-tag" style="color: #9333EA;">参与反馈 ›</div>
  132. </div>
  133. </div>
  134. </div>
  135. </div>
  136. <!-- 底部版权信息 -->
  137. <div class="mobile-footer">
  138. <div class="mobile-footer-info">
  139. <span>工信部备案号: 蜀ICP备20251411234号-1</span>
  140. <span>川公网安备: 51010502011234号</span>
  141. </div>
  142. </div>
  143. <!-- 意见反馈弹窗 -->
  144. <FeedbackModal
  145. :visible="showFeedbackModal"
  146. @close="closeFeedbackModal"
  147. @submit="handleFeedbackSubmit"
  148. />
  149. </div>
  150. </template>
  151. <script setup>
  152. import { ref, computed, onMounted, watch } from 'vue'
  153. import { useRouter } from 'vue-router'
  154. import FeedbackModal from '@/components/MobileFeedbackModal.vue'
  155. import { apis } from '@/request/apis.js'
  156. // ===== 已删除:getUserId - 不再需要,改用token =====
  157. // import { getUserId } from '@/utils/userManager.js'
  158. import { useSpeechRecognition } from '@/composables/useSpeechRecognition.js'
  159. import { performLogout, getUsername } from '@/utils/auth.js'
  160. // 导入Element Plus组件
  161. import { ElMessage } from 'element-plus'
  162. // 导入图标
  163. import voiceInputIcon from '@/assets/Chat/18.png'
  164. import sendIconEmpty from '@/assets/Chat/15.png'
  165. import sendIconFilled from '@/assets/Chat/16.png'
  166. const router = useRouter()
  167. // 语音识别功能
  168. const {
  169. isSupported: speechSupported,
  170. isListening,
  171. isProcessing,
  172. transcript,
  173. error: speechError,
  174. startListening,
  175. stopListening
  176. } = useSpeechRecognition()
  177. // 响应式数据
  178. const searchText = ref('')
  179. const showFeedbackModal = ref(false)
  180. const isSending = ref(false)
  181. const recommendQuestions = ref([])
  182. // 用户信息相关
  183. const userInfo = ref(null)
  184. const showDropdown = ref(false)
  185. // 获取推荐问题
  186. const getRecommendQuestion = async () => {
  187. try {
  188. const res = await apis.getRecommendQuestion({ limit: 5 })
  189. recommendQuestions.value = res.data
  190. console.log('推荐问题数据结构:', recommendQuestions.value)
  191. } catch (error) {
  192. console.error('获取推荐问题失败:', error)
  193. }
  194. }
  195. // 换一换
  196. const refreshQuestions = () => {
  197. getRecommendQuestion()
  198. }
  199. // 语音输入相关方法
  200. const handleVoiceClick = () => {
  201. console.log('点击语音按钮')
  202. if (isProcessing.value) {
  203. ElMessage.info('语音正在转写,请稍候...')
  204. return
  205. }
  206. if (isListening.value) {
  207. stopVoiceInput()
  208. } else {
  209. startVoiceInput()
  210. }
  211. }
  212. const startVoiceInput = () => {
  213. console.log('开始语音输入')
  214. // 开始语音识别
  215. const success = startListening()
  216. if (!success) {
  217. ElMessage.error('语音识别启动失败,请检查麦克风权限')
  218. }
  219. }
  220. const stopVoiceInput = () => {
  221. console.log('停止语音输入')
  222. stopListening()
  223. ElMessage.info('语音转写中,请稍候...')
  224. }
  225. // 搜索功能
  226. const handleSearch = async () => {
  227. if (!searchText.value.trim()) {
  228. ElMessage.warning('请输入搜索内容')
  229. return
  230. }
  231. if (isSending.value) return
  232. isSending.value = true
  233. try {
  234. console.log('搜索内容:', searchText.value)
  235. // 跳转到AI问答页面,并传递搜索内容
  236. router.push({
  237. path: '/mobile/chat',
  238. query: {
  239. autoMessage: searchText.value
  240. }
  241. })
  242. } finally {
  243. isSending.value = false
  244. }
  245. }
  246. // 意见反馈弹窗相关方法
  247. const openFeedbackModal = () => {
  248. showFeedbackModal.value = true
  249. }
  250. const closeFeedbackModal = () => {
  251. showFeedbackModal.value = false
  252. }
  253. // 提交意见反馈
  254. const handleFeedbackSubmit = async (feedbackData) => {
  255. try {
  256. console.log('收到反馈数据:', feedbackData)
  257. console.log('feedback_img字段值:', feedbackData.feedback_img)
  258. // 处理图片数据 - 直接使用feedback_img字段
  259. let feedbackImg = feedbackData.feedback_img || ''
  260. console.log('处理后的feedbackImg:', feedbackImg)
  261. // 将前端数据格式转换为后端字段格式
  262. const feedbackPayload = {
  263. feedback_type: getFeedbackType(feedbackData.type),
  264. feedback_content: feedbackData.content,
  265. feedback_user_phone: feedbackData.contact || '',
  266. feedback_img: feedbackImg
  267. // ===== 已删除:user_id - 后端从token解析 =====
  268. }
  269. console.log(feedbackPayload,'feedbackPayload')
  270. // 调用API接口
  271. const response = await apis.submitFeedback(feedbackPayload)
  272. console.log(response,'response')
  273. // 提交成功
  274. ElMessage.success('反馈提交成功!感谢您的宝贵意见')
  275. closeFeedbackModal()
  276. } catch (error) {
  277. console.error('提交反馈失败:', error)
  278. ElMessage.error('提交反馈失败,请重试')
  279. }
  280. }
  281. // 将前端反馈类型转换为后端数字类型
  282. const getFeedbackType = (type) => {
  283. const typeMap = {
  284. 'function': 1, // 功能建议 -> 问题反馈
  285. 'interface': 2, // 界面优化 -> 界面优化
  286. 'experience': 3, // 体验问题 -> 体验问题
  287. 'other': 4 // 其他 -> 其他
  288. }
  289. return typeMap[type] || 1
  290. }
  291. // 推荐问题点击跳转
  292. const goToAIQuestion = (question) => {
  293. console.log('点击问题:', question)
  294. // 跳转到AI问答页面,并传递问题内容
  295. router.push({
  296. path: '/mobile/chat',
  297. query: {
  298. autoMessage: question
  299. }
  300. })
  301. }
  302. // 服务导航跳转方法
  303. const goToHazardDetection = () => {
  304. router.push('/mobile/hazard-detection')
  305. }
  306. const goToSafetyTraining = () => {
  307. router.push('/mobile/safety-hazard')
  308. }
  309. const goToExamWorkshop = () => {
  310. router.push('/mobile/exam-workshop')
  311. }
  312. const goToAIWriting = () => {
  313. router.push('/mobile/ai-writing')
  314. }
  315. const goToPolicyDocument = () => {
  316. router.push('/mobile/policy-document')
  317. }
  318. // 返回APP(登出逻辑)
  319. const handleLogout = () => {
  320. console.log('='.repeat(60))
  321. console.log('📱 用户点击"返回APP"按钮 - 执行登出逻辑')
  322. console.log('🌐 当前 URL:', window.location.href)
  323. console.log('🔍 检查 window.nativeClosePage:', typeof window.nativeClosePage)
  324. try {
  325. // 步骤1: 清除本地存储的用户信息和 token
  326. console.log('🧹 开始清除本地存储数据...')
  327. localStorage.removeItem('token')
  328. localStorage.removeItem('userInfo')
  329. localStorage.removeItem('username')
  330. sessionStorage.clear()
  331. console.log('✅ 本地存储数据已清除')
  332. // 步骤2: 调用原生方法关闭页面
  333. // 根据《移动客户端与H5对接规范》,优先使用 nativeClosePage() 方法
  334. if (window.nativeClosePage && typeof window.nativeClosePage === 'function') {
  335. console.log('✅ 检测到 nativeClosePage 方法,准备调用原生接口...')
  336. window.nativeClosePage()
  337. console.log('✅ 已成功调用 nativeClosePage()')
  338. return
  339. }
  340. // 降级方案1: 尝试使用 finishPage() 方法(部分 APP 可能使用此命名)
  341. if (window.finishPage && typeof window.finishPage === 'function') {
  342. console.log('✅ 检测到 finishPage 方法,准备调用原生接口...')
  343. window.finishPage()
  344. console.log('✅ 已成功调用 finishPage()')
  345. return
  346. }
  347. // 降级方案2: 使用 window.close() 关闭 WebView
  348. console.log('⚠️ 未检测到原生方法,尝试使用 window.close()')
  349. window.close()
  350. console.log('✅ 已调用 window.close()')
  351. // 如果 window.close() 没有立即关闭(比如在普通浏览器中),显示提示
  352. setTimeout(() => {
  353. console.warn('⚠️ window.close() 可能未生效,显示提示信息')
  354. ElMessage.info('如果页面未关闭,请使用 APP 的返回按钮')
  355. }, 500)
  356. } catch (error) {
  357. console.error('❌ 登出过程发生错误:', error)
  358. console.error('❌ 错误详情:', error.message)
  359. console.warn('⚠️ 当前环境:', navigator.userAgent)
  360. ElMessage.warning('无法自动关闭页面,请使用 APP 的返回按钮')
  361. }
  362. console.log('='.repeat(60))
  363. }
  364. // 监听语音识别错误
  365. watch(transcript, (newVal) => {
  366. if (!newVal || isListening.value) return
  367. searchText.value = newVal
  368. })
  369. watch(speechError, (newVal) => {
  370. if (newVal) {
  371. console.error('语音识别错误:', newVal)
  372. ElMessage.error(newVal)
  373. }
  374. })
  375. onMounted(() => {
  376. getRecommendQuestion()
  377. // 从本地存储获取用户名
  378. const username = getUsername()
  379. userInfo.value = {
  380. username: username || '蜀道用户'
  381. }
  382. console.log('用户信息:', userInfo.value)
  383. })
  384. </script>
  385. <style lang="less" scoped>
  386. .mobile-container {
  387. width: 100%;
  388. min-height: 100vh;
  389. background-image: url('@/assets/new_index/2.png');
  390. background-size: cover;
  391. background-repeat: no-repeat;
  392. background-position: center center;
  393. display: flex;
  394. flex-direction: column;
  395. padding-bottom: 80px;
  396. box-sizing: border-box;
  397. position: relative;
  398. }
  399. /* 移动端顶部logo区域 */
  400. .mobile-header {
  401. padding: 20px 20px 0 20px;
  402. display: flex;
  403. justify-content: space-between;
  404. align-items: center;
  405. .logo {
  406. display: flex;
  407. align-items: center;
  408. .logo-img {
  409. width: 120px;
  410. height: 35px;
  411. }
  412. }
  413. /* 移动端用户信息区域 */
  414. .mobile-user-info {
  415. position: relative;
  416. display: flex;
  417. align-items: center;
  418. gap: 6px;
  419. cursor: pointer;
  420. padding: 6px 10px;
  421. border-radius: 6px;
  422. transition: background-color 0.3s ease;
  423. height: 40px;
  424. .mobile-user-avatar {
  425. display: flex;
  426. align-items: center;
  427. justify-content: center;
  428. .mobile-avatar-icon {
  429. width: 28px;
  430. height: 28px;
  431. border-radius: 50%;
  432. background: #DBEAFE;
  433. display: flex;
  434. align-items: center;
  435. justify-content: center;
  436. position: relative;
  437. &::before {
  438. content: '👤';
  439. font-size: 14px;
  440. color: #2563EB;
  441. }
  442. }
  443. }
  444. .mobile-username {
  445. font-size: 14px;
  446. color: #000B63;
  447. font-weight: 500;
  448. white-space: nowrap;
  449. line-height: 1;
  450. display: flex;
  451. align-items: center;
  452. }
  453. /* 移动端下拉菜单 */
  454. .mobile-dropdown-menu {
  455. position: absolute;
  456. top: 100%;
  457. left: 0;
  458. right: 0;
  459. background: white;
  460. border-radius: 6px;
  461. box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  462. border: 1px solid #E5E7EB;
  463. z-index: 1000;
  464. overflow: hidden;
  465. .mobile-dropdown-item {
  466. padding: 10px 12px;
  467. font-size: 13px;
  468. color: #374151;
  469. cursor: pointer;
  470. transition: background-color 0.2s ease;
  471. &:hover {
  472. background-color: #F3F4F6;
  473. }
  474. &.mobile-logout-item {
  475. color: #DC2626;
  476. &:hover {
  477. background-color: #FEF2F2;
  478. }
  479. }
  480. }
  481. }
  482. }
  483. }
  484. /* 移动端主内容区域 */
  485. .mobile-main-content {
  486. flex: 1;
  487. display: flex;
  488. flex-direction: column;
  489. align-items: center;
  490. padding: 20px 16px 0 16px;
  491. width: 100%;
  492. box-sizing: border-box;
  493. }
  494. /* 移动端主标题 */
  495. .mobile-main-title {
  496. font-size: 32px;
  497. font-weight: 900;
  498. color: #000B63;
  499. text-align: center;
  500. margin: 0;
  501. line-height: 1.2;
  502. }
  503. .mobile-sub-title {
  504. font-size: 16px;
  505. color: #000B63;
  506. text-align: center;
  507. margin: 12px 0 0 0;
  508. line-height: 1.4;
  509. padding: 0 20px;
  510. }
  511. /* 移动端搜索框 */
  512. .mobile-search-container {
  513. width: 100%;
  514. margin: 24px 0 0 0;
  515. .mobile-search-box {
  516. position: relative;
  517. width: 100%;
  518. height: 48px;
  519. border: 2px solid #000B63;
  520. border-radius: 11.5px;
  521. background: #ffffff;
  522. display: flex;
  523. align-items: center;
  524. padding: 0 16px;
  525. .mobile-search-input {
  526. flex: 1;
  527. height: 100%;
  528. border: none;
  529. outline: none;
  530. font-size: 16px;
  531. background: transparent;
  532. padding: 0 12px 0 0;
  533. &::placeholder {
  534. color: #9CA3AF;
  535. }
  536. }
  537. .mobile-voice-btn {
  538. background: none;
  539. border: none;
  540. cursor: pointer;
  541. padding: 0;
  542. margin-right: 0;
  543. transition: all 0.3s ease;
  544. display: flex;
  545. align-items: center;
  546. justify-content: center;
  547. height: 100%;
  548. &:hover {
  549. opacity: 0.8;
  550. }
  551. .mobile-icon-container {
  552. width: 20px;
  553. height: 20px;
  554. display: flex;
  555. align-items: center;
  556. justify-content: center;
  557. flex-shrink: 0;
  558. position: relative;
  559. }
  560. .mobile-recording-indicator {
  561. position: absolute;
  562. top: -2px;
  563. right: -2px;
  564. width: 8px;
  565. height: 8px;
  566. background: #ef4444;
  567. border-radius: 50%;
  568. animation: blink 1s ease-in-out infinite;
  569. }
  570. .mobile-action-icon {
  571. width: 20px;
  572. height: 20px;
  573. max-width: 20px;
  574. max-height: 20px;
  575. object-fit: contain;
  576. }
  577. }
  578. .mobile-divider {
  579. width: 1px;
  580. height: 24px;
  581. background-color: #D6D5DE;
  582. margin: 0 8px;
  583. }
  584. .mobile-send-btn {
  585. background: none;
  586. border: none;
  587. cursor: pointer;
  588. padding: 0;
  589. transition: all 0.3s ease;
  590. display: flex;
  591. align-items: center;
  592. justify-content: center;
  593. height: 100%;
  594. &:hover:not(:disabled) {
  595. opacity: 0.8;
  596. }
  597. &:disabled {
  598. opacity: 0.5;
  599. cursor: not-allowed;
  600. }
  601. .mobile-send-icon {
  602. width: 60px;
  603. height: 28px;
  604. object-fit: contain;
  605. }
  606. }
  607. }
  608. }
  609. @keyframes blink {
  610. 0%, 100% {
  611. opacity: 1;
  612. }
  613. 50% {
  614. opacity: 0.5;
  615. }
  616. }
  617. /* 移动端卡片区域 */
  618. .mobile-cards-container {
  619. width: 100%;
  620. margin: 24px 0 0 0;
  621. display: flex;
  622. flex-direction: column;
  623. gap: 16px;
  624. }
  625. /* 移动端常见问题卡片 */
  626. .mobile-common-questions-card {
  627. background: linear-gradient(146.85deg, rgba(70, 114, 255, 1) 0%, rgba(125, 170, 252, 1) 100%);
  628. width: 100%;
  629. height: 400px;
  630. position: relative;
  631. border-radius: 16px;
  632. overflow: hidden;
  633. box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
  634. .mobile-card-header {
  635. position: absolute;
  636. top: 20px;
  637. left: 20px;
  638. right: 20px;
  639. display: flex;
  640. justify-content: space-between;
  641. align-items: center;
  642. z-index: 2;
  643. .mobile-card-title-section {
  644. display: flex;
  645. align-items: center;
  646. gap: 12px;
  647. .mobile-card-avatar {
  648. width: 40px;
  649. height: 40px;
  650. border-radius: 50%;
  651. object-fit: cover;
  652. // border: 2px solid rgba(255, 255, 255, 0.3);
  653. }
  654. .mobile-card-title {
  655. font-size: 22px;
  656. font-weight: 900;
  657. color: #FFFFFF;
  658. text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
  659. }
  660. }
  661. .mobile-refresh-button {
  662. background: rgba(255, 255, 255, 0.2);
  663. border: 1px solid rgba(255, 255, 255, 0.3);
  664. border-radius: 20px;
  665. // height: 36px;
  666. // width: 70px;
  667. padding: 8px 15px;
  668. cursor: pointer;
  669. transition: all 0.3s ease;
  670. display: flex;
  671. align-items: center;
  672. justify-content: center;
  673. backdrop-filter: blur(10px);
  674. &:hover {
  675. background: rgba(255, 255, 255, 0.3);
  676. transform: translateY(-1px);
  677. }
  678. .mobile-refresh-text {
  679. font-size: 14px;
  680. color: #FFFFFF;
  681. font-weight: 500;
  682. line-height: 1;
  683. }
  684. }
  685. }
  686. .mobile-questions-content {
  687. position: absolute;
  688. top: 70px;
  689. left: 20px;
  690. right: 20px;
  691. bottom: 20px;
  692. background: rgba(255, 255, 255, 0.95);
  693. border-radius: 16px;
  694. padding: 20px;
  695. overflow-y: auto;
  696. backdrop-filter: blur(10px);
  697. .mobile-questions-list {
  698. .mobile-question-item {
  699. margin-bottom: 16px;
  700. font-size: 16px;
  701. color: #1F2937;
  702. cursor: pointer;
  703. transition: all 0.3s ease;
  704. line-height: 1.5;
  705. padding: 8px 0;
  706. border-bottom: 1px solid rgba(0, 0, 0, 0.05);
  707. overflow: hidden;
  708. text-overflow: ellipsis;
  709. white-space: nowrap;
  710. &:hover {
  711. color: #4672FF;
  712. transform: translateX(4px);
  713. background: rgba(70, 114, 255, 0.05);
  714. border-radius: 8px;
  715. padding: 8px 12px;
  716. margin: 8px -12px;
  717. white-space: normal;
  718. overflow: visible;
  719. text-overflow: unset;
  720. }
  721. &:last-child {
  722. margin-bottom: 0;
  723. border-bottom: none;
  724. }
  725. }
  726. }
  727. }
  728. }
  729. /* 移动端服务功能网格 */
  730. .mobile-services-grid {
  731. display: grid;
  732. grid-template-columns: 1fr 1fr;
  733. gap: 12px;
  734. width: 100%;
  735. // margin-bottom: 16px;
  736. .mobile-service-item {
  737. height: 200px;
  738. }
  739. // 前两个服务功能的独立样式
  740. .mobile-service-info-large {
  741. position: absolute;
  742. left: 35px;
  743. bottom: 20px;
  744. .mobile-service-title-large {
  745. font-size: 18px;
  746. font-weight: 600;
  747. margin-bottom: 4px;
  748. line-height: 1.2;
  749. }
  750. .mobile-service-desc-large {
  751. font-size: 14px;
  752. line-height: 1.4;
  753. }
  754. }
  755. // 隐患提示卡片(第1个)- 黑色文字
  756. .mobile-service-item:nth-child(1) {
  757. .mobile-service-info-large {
  758. .mobile-service-title-large {
  759. font-size: 24px;
  760. font-weight: 600;
  761. color: #000000;
  762. }
  763. .mobile-service-desc-large {
  764. font-size: 16px;
  765. font-weight: 400;
  766. color: #4B5563;
  767. }
  768. }
  769. }
  770. // 安全培训卡片(第2个)- 白色文字
  771. .mobile-service-item:nth-child(2) {
  772. .mobile-service-info-large {
  773. bottom: 28px;
  774. .mobile-service-title-large {
  775. color: #FFFFFF;
  776. font-size: 24px;
  777. font-weight: 600;
  778. }
  779. .mobile-service-desc-large {
  780. color: #DBEAFE;
  781. font-size: 16px;
  782. font-weight: 400;
  783. }
  784. }
  785. }
  786. }
  787. /* 四个服务功能单独容器 */
  788. .mobile-four-services {
  789. display: flex;
  790. flex-wrap: wrap;
  791. gap: 12px;
  792. width: 100%;
  793. .mobile-service-item {
  794. flex: 1;
  795. min-width: calc(50% - 6px);
  796. max-width: calc(50% - 6px);
  797. // 考试工坊
  798. &:nth-child(1) {
  799. background-image: url('@/assets/new_index/6.png');
  800. }
  801. // AI写作
  802. &:nth-child(2) {
  803. background-image: url('@/assets/new_index/7.png');
  804. }
  805. // 政策文件
  806. &:nth-child(3) {
  807. background-image: url('@/assets/new_index/8.png');
  808. }
  809. // 意见反馈
  810. &:nth-child(4) {
  811. background-image: url('@/assets/new_index/9.png');
  812. }
  813. .mobile-service-header {
  814. position: absolute;
  815. left: 50%;
  816. top: 1px;
  817. transform: translateX(-50%);
  818. display: flex;
  819. align-items: center;
  820. justify-content: center;
  821. gap: 8px;
  822. }
  823. .mobile-service-icon {
  824. display: flex;
  825. align-items: center;
  826. justify-content: center;
  827. flex-shrink: 0;
  828. .mobile-icon-img {
  829. width: 30px;
  830. height: 30px;
  831. object-fit: contain;
  832. }
  833. }
  834. .mobile-service-title {
  835. font-size: 18px;
  836. color: #000000;
  837. font-weight: 600;
  838. line-height: 1;
  839. white-space: nowrap;
  840. flex-shrink: 0;
  841. }
  842. .mobile-service-description {
  843. position: absolute;
  844. left: 50%;
  845. top: 50px;
  846. transform: translateX(-50%);
  847. font-size: 12px;
  848. color: #4B5563;
  849. line-height: 1.4;
  850. text-align: center;
  851. width: 100%;
  852. }
  853. .mobile-service-tag {
  854. position: absolute;
  855. left: 20px;
  856. // bottom: 20px;
  857. font-size: 12px;
  858. font-weight: 500;
  859. white-space: nowrap;
  860. }
  861. }
  862. }
  863. .mobile-service-item {
  864. background-size: 100% 100%;
  865. background-repeat: no-repeat;
  866. border-radius: 12px;
  867. padding: 16px;
  868. cursor: pointer;
  869. transition: all 0.3s ease;
  870. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  871. position: relative;
  872. overflow: hidden;
  873. height: 120px;
  874. display: flex;
  875. flex-direction: column;
  876. justify-content: flex-end;
  877. &:hover {
  878. transform: translateY(-2px);
  879. box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  880. }
  881. // 隐患提示
  882. &:nth-child(1) {
  883. background-image: url('@/assets/new_index/4.png');
  884. }
  885. // 安全培训
  886. &:nth-child(2) {
  887. background-image: url('@/assets/new_index/5.png');
  888. }
  889. .mobile-service-icon {
  890. display: none; // 隐藏图标,使用背景图
  891. }
  892. .mobile-service-info {
  893. .mobile-service-title {
  894. font-size: 16px;
  895. color: #000000;
  896. font-weight: 600;
  897. margin-bottom: 4px;
  898. line-height: 1.2;
  899. }
  900. .mobile-service-desc {
  901. font-size: 12px;
  902. color: #4B5563;
  903. line-height: 1.3;
  904. }
  905. }
  906. }
  907. /* 移动端底部版权信息 */
  908. .mobile-footer {
  909. width: 100%;
  910. height: 40px;
  911. background-image: url('@/assets/new_index/10.png');
  912. background-size: 100% 100%;
  913. background-repeat: no-repeat;
  914. display: flex;
  915. align-items: center;
  916. justify-content: center;
  917. // margin-top: auto;
  918. position: absolute;
  919. bottom: 0;
  920. .mobile-footer-info {
  921. display: flex;
  922. flex-direction: row;
  923. align-items: center;
  924. gap: 38px;
  925. font-size: 10px;
  926. color: #6B7280;
  927. line-height: 1.2;
  928. text-align: center;
  929. padding: 0 16px;
  930. }
  931. }
  932. /* 移动端响应式调整 */
  933. @media (max-width: 480px) {
  934. .mobile-main-title {
  935. font-size: 28px;
  936. }
  937. .mobile-sub-title {
  938. font-size: 14px;
  939. }
  940. .mobile-services-grid {
  941. grid-template-columns: 1fr;
  942. }
  943. .mobile-service-item {
  944. .mobile-service-icon {
  945. height: 60px;
  946. }
  947. .mobile-service-info {
  948. .mobile-service-title {
  949. font-size: 14px;
  950. }
  951. .mobile-service-desc {
  952. font-size: 11px;
  953. }
  954. }
  955. }
  956. }
  957. </style>