ProjectCases.vue 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929
  1. <template>
  2. <div class="project-cases">
  3. <PageHeader />
  4. <main class="main-content">
  5. <div class="case-content">
  6. <!-- 项目封面区域 -->
  7. <section class="project-header">
  8. <div class="project-cover">
  9. <img
  10. :src="currentProject.coverImage"
  11. :alt="currentProject.title"
  12. class="project-cover-image"
  13. loading="lazy"
  14. >
  15. </div>
  16. <div class="project-info">
  17. <h1 class="project-title">{{ currentProject.title }}</h1>
  18. <p class="project-subtitle">{{ currentProject.subtitle }}</p>
  19. <div class="project-meta">
  20. <span class="meta-item">
  21. <el-icon><Calendar /></el-icon>
  22. {{ currentProject.date }}
  23. </span>
  24. <span class="meta-item">
  25. <el-icon><Location /></el-icon>
  26. {{ currentProject.location }}
  27. </span>
  28. <span class="meta-item">
  29. <el-icon><User /></el-icon>
  30. {{ currentProject.client }}
  31. </span>
  32. </div>
  33. <div class="project-tags">
  34. <span
  35. v-for="tag in currentProject.tags"
  36. :key="tag"
  37. class="project-tag"
  38. >
  39. {{ tag }}
  40. </span>
  41. </div>
  42. </div>
  43. </section>
  44. <!-- 项目概览 -->
  45. <section class="project-overview">
  46. <h2 class="section-title">项目概览</h2>
  47. <p class="overview-text">{{ currentProject.overview }}</p>
  48. </section>
  49. <!-- 案例展示 -->
  50. <section class="case-study">
  51. <h2 class="section-title">案例展示</h2>
  52. <div class="case-viewer-container">
  53. <!-- 媒体展示区域 -->
  54. <div class="media-display">
  55. <div class="media-container">
  56. <!-- 媒体内容 -->
  57. <div class="media-content">
  58. <!-- 图片展示 -->
  59. <div v-if="selectedCase && selectedCase.type !== 'video'" class="image-display">
  60. <img
  61. :src="selectedCase.image"
  62. :alt="selectedCase.title"
  63. class="display-image"
  64. >
  65. </div>
  66. <!-- 视频播放 -->
  67. <div v-if="selectedCase && selectedCase.type === 'video'" class="video-display">
  68. <video
  69. :src="selectedCase.video"
  70. :poster="selectedCase.thumbnail"
  71. controls
  72. class="display-video"
  73. ref="displayVideo"
  74. @click="toggleVideoPlay"
  75. @play="handleVideoPlay"
  76. @pause="handleVideoPause"
  77. preload="metadata"
  78. muted
  79. >
  80. 您的浏览器不支持视频播放。
  81. </video>
  82. <div v-if="!videoPlaying" class="video-play-button" @click="toggleVideoPlay">
  83. <el-icon class="play-icon"><VideoPlay /></el-icon>
  84. </div>
  85. </div>
  86. </div>
  87. </div>
  88. <div v-if="currentProject.caseStudies.length === 0" class="no-selection">
  89. <el-icon class="no-selection-icon"><Picture /></el-icon>
  90. <p>暂无案例数据</p>
  91. </div>
  92. </div>
  93. </div>
  94. </section>
  95. <!-- 技术亮点 -->
  96. <section class="technical-highlights">
  97. <h2 class="section-title">技术亮点</h2>
  98. <div class="horizontal-accordion-container">
  99. <div
  100. v-for="(item, index) in Object.values(currentProject.technicalHighlights)"
  101. :key="index"
  102. class="accordion-item"
  103. :class="{ 'active': activeAccordion === index }"
  104. @mouseenter="handleItemHover(index)"
  105. >
  106. <div class="accordion-header">
  107. <el-icon class="collapse-icon"><Star /></el-icon>
  108. <span>{{ item.title }}</span>
  109. </div>
  110. <div class="accordion-content">
  111. <p class="collapse-description">{{ item.description }}</p>
  112. <ul class="collapse-features">
  113. <li v-for="(feature, idx) in item.features" :key="idx">
  114. <el-icon class="feature-icon"><Check /></el-icon>
  115. {{ feature }}
  116. </li>
  117. </ul>
  118. </div>
  119. </div>
  120. </div>
  121. </section>
  122. <!-- 应用成效 -->
  123. <section class="application-effects">
  124. <h2 class="section-title">应用成效</h2>
  125. <div class="news-list-container">
  126. <div
  127. v-for="(news, index) in currentProject.applicationEffects"
  128. :key="index"
  129. class="news-list-item"
  130. @click="openNewsDetail(news)"
  131. >
  132. <div class="news-list-content">
  133. <div class="news-list-header">
  134. <span class="news-list-category">{{ news.category }}</span>
  135. <span class="news-list-date">{{ news.date }}</span>
  136. </div>
  137. <h3 class="news-list-title">{{ news.title }}</h3>
  138. <p class="news-list-summary">{{ news.summary }}</p>
  139. <div class="news-list-footer">
  140. <span class="read-more">阅读全文 <el-icon><ArrowRight /></el-icon></span>
  141. </div>
  142. </div>
  143. <div class="news-list-image">
  144. <img :src="news.image" :alt="news.title" class="list-image">
  145. </div>
  146. </div>
  147. </div>
  148. </section>
  149. <!-- 项目资料 -->
  150. <section class="project-materials">
  151. <h2 class="section-title">项目资料</h2>
  152. <!-- 分类导航 -->
  153. <div class="materials-tabs">
  154. <el-tabs v-model="activeMaterialTab" type="card" @tab-click="handleTabClick">
  155. <el-tab-pane label="全部" name="all">
  156. <template #label>
  157. <el-icon><Collection /></el-icon>
  158. 全部
  159. </template>
  160. <!-- 文件列表 -->
  161. <div class="materials-list">
  162. <div
  163. v-for="(material, index) in currentProject.materials"
  164. :key="material.id"
  165. class="material-item"
  166. >
  167. <div class="material-icon">
  168. <el-icon v-if="material.type === 'pdf' || material.category === '文件'"><Document /></el-icon>
  169. <el-icon v-else-if="material.type === 'word'"><EditPen /></el-icon>
  170. <el-icon v-else-if="material.type === 'excel'"><DataAnalysis /></el-icon>
  171. <el-icon v-else-if="material.type === 'csv'"><Reading /></el-icon>
  172. <el-icon v-else-if="material.type === 'zip'"><FolderOpened /></el-icon>
  173. <el-icon v-else-if="material.type === 'video' || material.category === '媒体'"><VideoPlay /></el-icon>
  174. <el-icon v-else><Picture /></el-icon>
  175. </div>
  176. <div class="material-info">
  177. <div class="material-name">{{ material.name }}</div>
  178. <div class="material-meta">
  179. <span class="meta-item">
  180. <el-icon><Calendar /></el-icon>
  181. {{ material.uploadDate }}
  182. </span>
  183. <span class="meta-item">
  184. <el-icon><Collection /></el-icon>
  185. {{ material.size }}
  186. </span>
  187. </div>
  188. <div class="material-description">{{ material.description }}</div>
  189. </div>
  190. <div class="material-actions">
  191. <el-button type="primary" size="small" @click="downloadMaterial(material)">
  192. <el-icon><Download /></el-icon>
  193. 下载
  194. </el-button>
  195. </div>
  196. </div>
  197. </div>
  198. </el-tab-pane>
  199. <el-tab-pane label="文件" name="文件">
  200. <template #label>
  201. <el-icon><Document /></el-icon>
  202. 文件
  203. </template>
  204. <!-- 文件列表 -->
  205. <div class="materials-list">
  206. <div
  207. v-for="(material, index) in currentProject.materials.filter(m => m.category === '文件')"
  208. :key="material.id"
  209. class="material-item"
  210. >
  211. <div class="material-icon">
  212. <el-icon v-if="material.type === 'pdf'"><Document /></el-icon>
  213. <el-icon v-else-if="material.type === 'word'"><EditPen /></el-icon>
  214. <el-icon v-else-if="material.type === 'excel'"><DataAnalysis /></el-icon>
  215. <el-icon v-else-if="material.type === 'csv'"><Reading /></el-icon>
  216. <el-icon v-else-if="material.type === 'zip'"><FolderOpened /></el-icon>
  217. <el-icon v-else><Document /></el-icon>
  218. </div>
  219. <div class="material-info">
  220. <div class="material-name">{{ material.name }}</div>
  221. <div class="material-meta">
  222. <span class="meta-item">
  223. <el-icon><Calendar /></el-icon>
  224. {{ material.uploadDate }}
  225. </span>
  226. <span class="meta-item">
  227. <el-icon><Collection /></el-icon>
  228. {{ material.size }}
  229. </span>
  230. </div>
  231. <div class="material-description">{{ material.description }}</div>
  232. </div>
  233. <div class="material-actions">
  234. <el-button type="primary" size="small" @click="downloadMaterial(material)">
  235. <el-icon><Download /></el-icon>
  236. 下载
  237. </el-button>
  238. </div>
  239. </div>
  240. </div>
  241. </el-tab-pane>
  242. <el-tab-pane label="媒体" name="媒体">
  243. <template #label>
  244. <el-icon><VideoPlay /></el-icon>
  245. 媒体
  246. </template>
  247. <!-- 文件列表 -->
  248. <div class="materials-list">
  249. <div
  250. v-for="(material, index) in currentProject.materials.filter(m => m.category === '媒体')"
  251. :key="material.id"
  252. class="material-item"
  253. >
  254. <div class="material-icon">
  255. <el-icon v-if="material.type === 'video'"><VideoPlay /></el-icon>
  256. <el-icon v-else-if="material.type === 'zip'"><FolderOpened /></el-icon>
  257. <el-icon v-else><Picture /></el-icon>
  258. </div>
  259. <div class="material-info">
  260. <div class="material-name">{{ material.name }}</div>
  261. <div class="material-meta">
  262. <span class="meta-item">
  263. <el-icon><Calendar /></el-icon>
  264. {{ material.uploadDate }}
  265. </span>
  266. <span class="meta-item">
  267. <el-icon><Collection /></el-icon>
  268. {{ material.size }}
  269. </span>
  270. </div>
  271. <div class="material-description">{{ material.description }}</div>
  272. </div>
  273. <div class="material-actions">
  274. <el-button type="primary" size="small" @click="downloadMaterial(material)">
  275. <el-icon><Download /></el-icon>
  276. 下载
  277. </el-button>
  278. </div>
  279. </div>
  280. </div>
  281. </el-tab-pane>
  282. </el-tabs>
  283. </div>
  284. </section>
  285. <!-- 返回首页按钮 -->
  286. <div class="back-button-container">
  287. <el-button type="primary" size="large" @click="goToHome">
  288. <el-icon><ArrowLeft /></el-icon>
  289. 返回首页
  290. </el-button>
  291. </div>
  292. </div>
  293. </main>
  294. <!-- 媒体查看器 -->
  295. <el-dialog
  296. v-model="mediaViewerVisible"
  297. :title="currentMediaItem.title"
  298. width="80%"
  299. :before-close="closeMediaViewer"
  300. class="media-viewer-dialog"
  301. >
  302. <div class="media-viewer-content">
  303. <!-- 图片查看器 -->
  304. <div v-if="currentMediaItem.type !== 'video'" class="image-viewer">
  305. <img
  306. :src="currentMediaItem.image"
  307. :alt="currentMediaItem.title"
  308. class="viewer-image"
  309. @click="closeMediaViewer"
  310. >
  311. </div>
  312. <!-- 视频播放器 -->
  313. <div v-if="currentMediaItem.type === 'video'" class="video-viewer">
  314. <video
  315. :src="currentMediaItem.video"
  316. :poster="currentMediaItem.thumbnail"
  317. controls
  318. class="viewer-video"
  319. ref="viewerVideo"
  320. @click="toggleViewerVideoPlay"
  321. @play="handleViewerVideoPlay"
  322. @pause="handleViewerVideoPause"
  323. preload="metadata"
  324. muted
  325. >
  326. 您的浏览器不支持视频播放。
  327. </video>
  328. <div v-if="!viewerVideoPlaying" class="video-play-button" @click="toggleViewerVideoPlay">
  329. <el-icon class="play-icon"><VideoPlay /></el-icon>
  330. </div>
  331. </div>
  332. <!-- 媒体描述 -->
  333. <div class="media-description">
  334. {{ currentMediaItem.description }}
  335. </div>
  336. <!-- 导航按钮 -->
  337. <div class="media-navigation">
  338. <el-button
  339. :disabled="currentMediaIndex === 0"
  340. @click="previousMedia"
  341. >
  342. <el-icon><ArrowLeft /></el-icon>
  343. 上一个
  344. </el-button>
  345. <span class="media-index">{{ currentMediaIndex + 1 }} / {{ currentProject.caseStudies.length }}</span>
  346. <el-button
  347. :disabled="currentMediaIndex === currentProject.caseStudies.length - 1"
  348. @click="nextMedia"
  349. >
  350. 下一个
  351. <el-icon><ArrowRight /></el-icon>
  352. </el-button>
  353. </div>
  354. </div>
  355. </el-dialog>
  356. <!-- 新闻详情弹窗 -->
  357. <el-dialog
  358. v-model="newsDetailVisible"
  359. :title="currentNews.title"
  360. width="70%"
  361. :before-close="closeNewsDetail"
  362. class="news-detail-dialog"
  363. >
  364. <div class="news-detail-content">
  365. <!-- 新闻元信息 -->
  366. <div class="news-detail-meta">
  367. <span class="news-detail-category">{{ currentNews.category }}</span>
  368. <span class="news-detail-date">{{ currentNews.date }}</span>
  369. <span class="news-detail-author">{{ currentNews.author }}</span>
  370. </div>
  371. <!-- 新闻内容 -->
  372. <div class="news-detail-body">
  373. <p class="news-detail-content">{{ currentNews.content }}</p>
  374. <!-- 关键数据 -->
  375. <div class="news-detail-highlight">
  376. <div class="highlight-number">{{ currentNews.number }}</div>
  377. <div class="highlight-label">{{ currentNews.description }}</div>
  378. </div>
  379. </div>
  380. <!-- 新闻底部 -->
  381. <div class="news-detail-footer">
  382. <el-button type="primary" @click="closeNewsDetail">关闭</el-button>
  383. </div>
  384. </div>
  385. </el-dialog>
  386. <!-- 微信链接弹窗 -->
  387. <el-dialog
  388. v-model="weixinLinkVisible"
  389. title="抗咸潮保供水专项行动"
  390. width="90%"
  391. :before-close="closeWeixinLink"
  392. class="weixin-link-dialog"
  393. >
  394. <div class="weixin-link-content">
  395. <iframe
  396. :src="currentWeixinLink"
  397. frameborder="0"
  398. style="width: 100%; height: 80vh;"
  399. title="抗咸潮保供水专项行动"
  400. ></iframe>
  401. </div>
  402. <template #footer>
  403. <el-button type="primary" @click="closeWeixinLink">关闭</el-button>
  404. </template>
  405. </el-dialog>
  406. <footer class="page-footer">
  407. <p>&copy; 2025 东南大区成果展示平台 - 金水内部测试系统</p>
  408. </footer>
  409. </div>
  410. </template>
  411. <script setup>
  412. import PageHeader from './PageHeader.vue'
  413. import { ArrowLeft, ArrowRight, Calendar, Location, User, Star, VideoPlay, Picture, Document, EditPen, DataAnalysis, Reading, FolderOpened, Collection, Download, Check, ArrowDown } from '@element-plus/icons-vue'
  414. import { ref, onMounted, computed } from 'vue'
  415. import { ElMessage } from 'element-plus'
  416. import { useRoute, useRouter } from 'vue-router'
  417. import { allProjects, getProjectById } from '@/data/projects'
  418. // 视频播放状态
  419. const videoPlaying = ref(false)
  420. const viewerVideoPlaying = ref(false)
  421. // 手风琴组件状态
  422. const activeAccordion = ref(0)
  423. // 鼠标悬停触发面板展开(手风琴模式自动收起其他)
  424. const handleItemHover = (key) => {
  425. activeAccordion.value = key
  426. }
  427. // 控制案例展示视频播放
  428. const toggleVideoPlay = (event) => {
  429. const video = document.querySelector('.display-video')
  430. if (video) {
  431. // 防止事件冒泡,避免同时触发视频和按钮的点击事件
  432. event.stopPropagation()
  433. if (videoPlaying.value) {
  434. video.pause()
  435. } else {
  436. video.play()
  437. }
  438. videoPlaying.value = !videoPlaying.value
  439. }
  440. }
  441. // 监听视频播放事件
  442. const handleVideoPlay = () => {
  443. videoPlaying.value = true
  444. }
  445. // 监听视频暂停事件
  446. const handleVideoPause = () => {
  447. videoPlaying.value = false
  448. }
  449. // 控制媒体查看器视频播放
  450. const toggleViewerVideoPlay = (event) => {
  451. const video = document.querySelector('.viewer-video')
  452. if (video) {
  453. // 防止事件冒泡,避免同时触发视频和按钮的点击事件
  454. event.stopPropagation()
  455. if (viewerVideoPlaying.value) {
  456. video.pause()
  457. } else {
  458. video.play()
  459. }
  460. viewerVideoPlaying.value = !viewerVideoPlaying.value
  461. }
  462. }
  463. // 监听媒体查看器视频播放事件
  464. const handleViewerVideoPlay = () => {
  465. viewerVideoPlaying.value = true
  466. }
  467. // 监听媒体查看器视频暂停事件
  468. const handleViewerVideoPause = () => {
  469. viewerVideoPlaying.value = false
  470. }
  471. // 当前选中的案例索引(默认显示视频)
  472. const selectedCaseIndex = ref(3)
  473. // 当前选中的案例
  474. const selectedCase = computed(() => {
  475. if (selectedCaseIndex.value !== null && currentProject.value.caseStudies[selectedCaseIndex.value]) {
  476. return currentProject.value.caseStudies[selectedCaseIndex.value]
  477. }
  478. return null
  479. })
  480. // 选择案例
  481. const selectCase = (index) => {
  482. // 停止当前播放的视频
  483. const currentVideo = document.querySelector('.display-video')
  484. if (currentVideo) {
  485. currentVideo.pause()
  486. }
  487. // 添加切换动画效果
  488. selectedCaseIndex.value = index
  489. // 延迟一段时间后让新视频自动播放(如果是视频类型)
  490. setTimeout(() => {
  491. const newVideo = document.querySelector('.display-video')
  492. if (newVideo && currentProject.value.caseStudies[index].type === 'video') {
  493. newVideo.play().catch(err => {
  494. console.log('视频播放失败:', err)
  495. })
  496. }
  497. }, 300)
  498. }
  499. // 媒体查看器相关变量(保留用于全屏查看)
  500. const mediaViewerVisible = ref(false)
  501. const currentMediaItem = ref({})
  502. const currentMediaIndex = ref(0)
  503. // 新闻详情弹窗相关变量
  504. const newsDetailVisible = ref(false)
  505. const currentNews = ref({})
  506. const weixinLinkVisible = ref(false)
  507. const currentWeixinLink = ref('')
  508. // 打开新闻详情弹窗
  509. const openNewsDetail = (news) => {
  510. // 判断是否是需要打开微信链接的新闻
  511. if (news.title.includes('抗咸保供水专项行动')) {
  512. // 微信公众号链接不允许嵌入iframe,直接在新窗口打开
  513. window.open('https://mp.weixin.qq.com/s/VQnFDQXvzJPY-YPhimapGg', '_blank')
  514. } else if (news.title.includes('水利部太湖流域管理局组织开展2024年太湖流域防洪调度演练')) {
  515. // 打开2024年防汛演练的微信链接
  516. window.open('https://mp.weixin.qq.com/s/cqVi5NhpI0UtamGdRWxysg', '_blank')
  517. } else if (news.title.includes('李国英调研数字孪生太湖建设工作')) {
  518. // 打开李国英调研数字孪生太湖建设工作的微信链接
  519. window.open('https://mp.weixin.qq.com/s/DoYywW9hos4zd3ZipDzwPA', '_blank')
  520. } else if (news.title.includes('水利部组织开展2023年太湖流域防洪调度演练')) {
  521. // 打开2023年防汛演练的新闻详情
  522. currentNews.value = news
  523. newsDetailVisible.value = true
  524. } else if (news.title.includes('水利部太湖流域管理局组织开展2025年太湖流域防洪调度演练')) {
  525. // 打开2025年防汛演练的新闻详情
  526. currentNews.value = news
  527. newsDetailVisible.value = true
  528. } else {
  529. currentNews.value = news
  530. newsDetailVisible.value = true
  531. }
  532. }
  533. // 关闭新闻详情弹窗
  534. const closeNewsDetail = () => {
  535. newsDetailVisible.value = false
  536. currentNews.value = {}
  537. }
  538. // 关闭微信链接弹窗
  539. const closeWeixinLink = () => {
  540. weixinLinkVisible.value = false
  541. currentWeixinLink.value = ''
  542. }
  543. // 打开媒体查看器
  544. const openMediaViewer = (caseItem, index) => {
  545. currentMediaItem.value = caseItem
  546. currentMediaIndex.value = index
  547. mediaViewerVisible.value = true
  548. }
  549. // 关闭媒体查看器
  550. const closeMediaViewer = () => {
  551. mediaViewerVisible.value = false
  552. currentMediaItem.value = {}
  553. currentMediaIndex.value = 0
  554. }
  555. // 切换到上一个媒体
  556. const previousMedia = () => {
  557. if (currentMediaIndex.value > 0) {
  558. currentMediaIndex.value--
  559. currentMediaItem.value = currentProject.value.caseStudies[currentMediaIndex.value]
  560. }
  561. }
  562. // 切换到下一个媒体
  563. const nextMedia = () => {
  564. if (currentMediaIndex.value < currentProject.value.caseStudies.length - 1) {
  565. currentMediaIndex.value++
  566. currentMediaItem.value = currentProject.value.caseStudies[currentMediaIndex.value]
  567. }
  568. }
  569. const route = useRoute()
  570. const router = useRouter()
  571. // 当前项目数据
  572. const currentProject = ref(allProjects[0])
  573. // 页面加载时获取项目数据
  574. onMounted(() => {
  575. const projectId = route.params.projectId || 'tai-pu-river'
  576. const project = getProjectById(projectId)
  577. if (project) {
  578. currentProject.value = project
  579. }
  580. })
  581. // 获取案例图标
  582. const getCaseIcon = (index) => {
  583. const icons = ['📱', '💻', '📊', '🔍', '🚀', '🌐']
  584. return icons[index % icons.length]
  585. }
  586. // 项目资料分类切换
  587. const activeMaterialTab = ref('all')
  588. // 过滤后的项目资料
  589. const filteredMaterials = computed(() => {
  590. if (activeMaterialTab.value === 'all') {
  591. return currentProject.value.materials
  592. } else {
  593. return currentProject.value.materials.filter(m => m.category === activeMaterialTab.value)
  594. }
  595. })
  596. // 标签页点击事件
  597. const handleTabClick = (tab) => {
  598. activeMaterialTab.value = tab.props.name
  599. }
  600. // 下载项目资料
  601. const downloadMaterial = (material) => {
  602. // 模拟下载功能
  603. console.log('开始下载文件:', material.name)
  604. // 这里可以添加实际的下载逻辑,例如:
  605. // 1. 调用后端接口获取文件下载地址
  606. // 2. 创建下载链接并触发点击
  607. // 3. 显示下载进度和状态
  608. // 模拟下载成功提示
  609. ElMessage.success(`正在下载: ${material.name}`)
  610. }
  611. // 返回首页
  612. const goToHome = () => {
  613. router.push('/')
  614. }
  615. </script>
  616. <style scoped>
  617. .project-cases {
  618. width: 100%;
  619. height: 100%;
  620. display: flex;
  621. flex-direction: column;
  622. font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
  623. overflow: hidden;
  624. background: #f8f9fa;
  625. }
  626. /* 主要内容区域 */
  627. .main-content {
  628. flex: 1;
  629. width: 100%;
  630. padding: 2rem 0;
  631. overflow-y: auto;
  632. height: calc(100% - 160px);
  633. display: flex;
  634. flex-direction: column;
  635. align-items: center;
  636. }
  637. .case-content {
  638. width: 100%;
  639. max-width: 1200px;
  640. padding: 0 1rem;
  641. }
  642. /* 项目封面区域 */
  643. .project-header {
  644. background: white;
  645. border-radius: 16px;
  646. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  647. overflow: hidden;
  648. margin-bottom: 2rem;
  649. }
  650. .project-cover {
  651. width: 100%;
  652. height: 350px;
  653. overflow: hidden;
  654. }
  655. .project-cover-image {
  656. width: 100%;
  657. height: 100%;
  658. object-fit: cover;
  659. }
  660. .project-info {
  661. padding: 2rem;
  662. }
  663. .project-title {
  664. font-size: 2.5rem;
  665. color: #2c3e50;
  666. margin-bottom: 1rem;
  667. font-weight: 700;
  668. line-height: 1.2;
  669. }
  670. .project-subtitle {
  671. font-size: 1.25rem;
  672. color: #7f8c8d;
  673. margin-bottom: 2rem;
  674. line-height: 1.6;
  675. }
  676. .project-meta {
  677. display: flex;
  678. gap: 2rem;
  679. margin-bottom: 1.5rem;
  680. flex-wrap: wrap;
  681. }
  682. .meta-item {
  683. display: flex;
  684. align-items: center;
  685. gap: 0.5rem;
  686. font-size: 0.875rem;
  687. color: #7f8c8d;
  688. }
  689. .project-tags {
  690. display: flex;
  691. gap: 0.75rem;
  692. flex-wrap: wrap;
  693. }
  694. .project-tag {
  695. background: #f0f5ff;
  696. color: #326ee2;
  697. padding: 0.5rem 1rem;
  698. border-radius: 20px;
  699. font-size: 0.875rem;
  700. font-weight: 500;
  701. }
  702. /* 章节样式 */
  703. .section-title {
  704. font-size: 1.75rem;
  705. color: #2c3e50;
  706. margin-bottom: 2rem;
  707. font-weight: 600;
  708. line-height: 1.4;
  709. text-align: center;
  710. position: relative;
  711. }
  712. .section-title::after {
  713. content: '';
  714. position: absolute;
  715. bottom: -0.5rem;
  716. left: 50%;
  717. transform: translateX(-50%);
  718. width: 60px;
  719. height: 4px;
  720. background: #326ee2;
  721. border-radius: 2px;
  722. }
  723. /* 项目概览 */
  724. .project-overview {
  725. background: white;
  726. padding: 1.5rem 2rem 2rem 2rem;
  727. border-radius: 16px;
  728. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  729. margin-bottom: 2rem;
  730. }
  731. .overview-text {
  732. font-size: 1.4rem;
  733. color: #34495e;
  734. line-height: 1.8;
  735. text-align: left;
  736. text-indent: 2em;
  737. max-width: 1000px;
  738. margin: 0 auto;
  739. }
  740. /* 项目资料 */
  741. .project-materials {
  742. margin-bottom: 2rem;
  743. }
  744. .material-category {
  745. margin-bottom: 2rem;
  746. }
  747. .category-title {
  748. font-size: 1.25rem;
  749. color: #2c3e50;
  750. margin-bottom: 1.5rem;
  751. font-weight: 600;
  752. line-height: 1.4;
  753. display: flex;
  754. align-items: center;
  755. gap: 0.5rem;
  756. padding: 0.5rem 1rem;
  757. background: #f8f9fa;
  758. border-left: 4px solid #326ee2;
  759. border-radius: 4px;
  760. }
  761. .materials-list {
  762. background: white;
  763. border-radius: 16px;
  764. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  765. overflow: hidden;
  766. }
  767. .material-item {
  768. display: flex;
  769. align-items: center;
  770. padding: 1.5rem 2rem;
  771. border-bottom: 1px solid #f0f0f0;
  772. transition: all 0.3s ease;
  773. }
  774. .material-item:last-child {
  775. border-bottom: none;
  776. }
  777. .material-item:hover {
  778. background-color: #f8f9fa;
  779. transform: translateX(5px);
  780. }
  781. .material-icon {
  782. font-size: 2rem;
  783. color: #326ee2;
  784. min-width: 60px;
  785. text-align: center;
  786. margin-right: 1.5rem;
  787. }
  788. .material-info {
  789. flex: 1;
  790. }
  791. .material-name {
  792. font-size: 1.1rem;
  793. color: #2c3e50;
  794. font-weight: 600;
  795. margin-bottom: 0.5rem;
  796. line-height: 1.4;
  797. }
  798. .material-meta {
  799. display: flex;
  800. gap: 1.5rem;
  801. margin-bottom: 0.5rem;
  802. flex-wrap: wrap;
  803. }
  804. .material-meta .meta-item {
  805. display: flex;
  806. align-items: center;
  807. gap: 0.3rem;
  808. font-size: 0.8rem;
  809. color: #7f8c8d;
  810. }
  811. .material-description {
  812. font-size: 0.875rem;
  813. color: #7f8c8d;
  814. line-height: 1.6;
  815. }
  816. .material-actions {
  817. margin-left: 1.5rem;
  818. }
  819. /* 项目资料标签页样式 */
  820. .materials-tabs {
  821. margin-bottom: 1.5rem;
  822. }
  823. .materials-tabs :deep(.el-tabs__header) {
  824. margin-bottom: 1rem;
  825. }
  826. .materials-tabs :deep(.el-tabs__nav-wrap) {
  827. margin-bottom: 0;
  828. }
  829. .materials-tabs :deep(.el-tabs__nav) {
  830. border-bottom: 1px solid #e4e7ed;
  831. }
  832. .materials-tabs :deep(.el-tabs__item) {
  833. border-bottom: 2px solid transparent;
  834. color: #606266;
  835. font-size: 1rem;
  836. font-weight: 500;
  837. padding: 0.75rem 1.5rem;
  838. margin-right: 1rem;
  839. }
  840. .materials-tabs :deep(.el-tabs__item:hover) {
  841. color: #326ee2;
  842. }
  843. .materials-tabs :deep(.el-tabs__item.is-active) {
  844. color: #326ee2;
  845. border-bottom-color: #326ee2;
  846. }
  847. .materials-tabs :deep(.el-tabs__nav-wrap::after) {
  848. display: none;
  849. }
  850. /* 技术亮点 */
  851. .technical-highlights {
  852. background: white;
  853. padding: 2rem;
  854. border-radius: 16px;
  855. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  856. margin-bottom: 2rem;
  857. }
  858. /* 横向手风琴容器 */
  859. .horizontal-accordion-container {
  860. width: 100%;
  861. height: 400px;
  862. display: flex;
  863. border: 1px solid #e6e6e6;
  864. border-radius: 8px;
  865. overflow: hidden;
  866. margin-top: 1.5rem;
  867. background: #fff;
  868. }
  869. /* 单个手风琴项 */
  870. .accordion-item {
  871. flex: 1;
  872. min-width: 150px;
  873. max-width: 150px;
  874. background-size: cover;
  875. background-position: center;
  876. background-repeat: no-repeat;
  877. border-right: 1px solid rgba(255, 255, 255, 0.2);
  878. transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1);
  879. position: relative;
  880. overflow: hidden;
  881. cursor: pointer;
  882. }
  883. /* 为每个手风琴项设置不同的背景图片 */
  884. .accordion-item:nth-child(1) {
  885. background-image: url('@/assets/images/太浦河全景2.png');
  886. }
  887. .accordion-item:nth-child(2) {
  888. background-image: url('@/assets/images/四预.png');
  889. }
  890. .accordion-item:nth-child(3) {
  891. background-image: url('@/assets/images/历史洪水.png');
  892. }
  893. .accordion-item:nth-child(4) {
  894. background-image: url('@/assets/images/抗咸保供.png');
  895. }
  896. /* 背景图片增强文字可读性 */
  897. .accordion-item::before {
  898. content: '';
  899. position: absolute;
  900. top: 0;
  901. left: 0;
  902. right: 0;
  903. bottom: 0;
  904. background: linear-gradient(to right, rgba(0, 0, 0, 1), transparent); /* 渐变黑色到透明,左侧完全不透明 */
  905. transition: all 0.3s ease;
  906. }
  907. .accordion-item:last-child {
  908. border-right: none;
  909. }
  910. /* 手风琴项激活状态(展开) */
  911. .accordion-item.active {
  912. flex: 4;
  913. max-width: none;
  914. }
  915. /* 手风琴头部 */
  916. .accordion-header {
  917. height: 100%;
  918. display: flex;
  919. flex-direction: column;
  920. align-items: center;
  921. justify-content: center;
  922. padding: 1.5rem;
  923. text-align: center;
  924. transition: all 0.3s ease;
  925. color: white;
  926. font-weight: 500;
  927. position: relative;
  928. z-index: 1;
  929. }
  930. .accordion-item:hover .accordion-header {
  931. background: rgba(255, 255, 255, 0.1);
  932. color: white;
  933. }
  934. .accordion-item.active .accordion-header {
  935. opacity: 0;
  936. visibility: hidden;
  937. transform: translateX(-100%);
  938. }
  939. .collapse-icon {
  940. color: white;
  941. font-size: 1.5rem;
  942. margin-bottom: 1rem;
  943. transition: transform 0.3s ease;
  944. }
  945. .accordion-item:hover .collapse-icon {
  946. transform: scale(1.1);
  947. }
  948. /* 手风琴内容 */
  949. .accordion-content {
  950. position: absolute;
  951. top: 0;
  952. left: 150px;
  953. height: 100%;
  954. width: calc(100% - 150px);
  955. padding: 2rem;
  956. opacity: 0;
  957. visibility: hidden;
  958. transition: all 0.4s cubic-bezier(0.645, 0.045, 0.355, 1);
  959. overflow-y: auto;
  960. }
  961. .accordion-item.active .accordion-content {
  962. left: 0;
  963. width: 100%;
  964. opacity: 1;
  965. visibility: visible;
  966. }
  967. .collapse-description {
  968. font-size: 1rem;
  969. color: white;
  970. line-height: 1.8;
  971. margin-bottom: 1.5rem;
  972. font-weight: 400;
  973. }
  974. .collapse-features {
  975. list-style: none;
  976. padding: 0;
  977. margin: 0;
  978. }
  979. .collapse-features li {
  980. display: flex;
  981. align-items: flex-start;
  982. gap: 0.75rem;
  983. font-size: 0.95rem;
  984. color: white;
  985. padding: 0.75rem 0;
  986. border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  987. line-height: 1.6;
  988. }
  989. .collapse-features li:last-child {
  990. border-bottom: none;
  991. }
  992. .feature-icon {
  993. color: #28a745;
  994. font-size: 0.9rem;
  995. margin-top: 0.2rem;
  996. flex-shrink: 0;
  997. }
  998. /* 项目成果 */
  999. .project-achievements {
  1000. margin-bottom: 2rem;
  1001. }
  1002. .news-cards-container {
  1003. display: flex;
  1004. gap: 2rem;
  1005. overflow-x: auto;
  1006. padding: 1rem 0;
  1007. scroll-behavior: smooth;
  1008. -webkit-overflow-scrolling: touch;
  1009. }
  1010. .news-cards-container::-webkit-scrollbar {
  1011. height: 8px;
  1012. }
  1013. .news-cards-container::-webkit-scrollbar-track {
  1014. background: #f1f1f1;
  1015. border-radius: 10px;
  1016. }
  1017. .news-cards-container::-webkit-scrollbar-thumb {
  1018. background: #326ee2;
  1019. border-radius: 10px;
  1020. }
  1021. .news-cards-container::-webkit-scrollbar-thumb:hover {
  1022. background: #64b5f6;
  1023. }
  1024. .news-card {
  1025. background: white;
  1026. border-radius: 16px;
  1027. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  1028. transition: all 0.3s ease;
  1029. cursor: pointer;
  1030. position: relative;
  1031. overflow: hidden;
  1032. min-width: 320px;
  1033. max-width: 320px;
  1034. display: flex;
  1035. flex-direction: column;
  1036. }
  1037. .news-card:hover {
  1038. transform: translateY(-10px);
  1039. box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);
  1040. }
  1041. .news-card-image {
  1042. position: relative;
  1043. width: 100%;
  1044. height: 180px;
  1045. overflow: hidden;
  1046. }
  1047. .card-image {
  1048. width: 100%;
  1049. height: 100%;
  1050. object-fit: cover;
  1051. transition: transform 0.3s ease;
  1052. }
  1053. .news-card:hover .card-image {
  1054. transform: scale(1.05);
  1055. }
  1056. .news-category {
  1057. position: absolute;
  1058. top: 1rem;
  1059. left: 1rem;
  1060. background: rgba(50, 110, 226, 0.9);
  1061. color: white;
  1062. padding: 0.25rem 0.75rem;
  1063. border-radius: 12px;
  1064. font-size: 0.75rem;
  1065. font-weight: 600;
  1066. text-transform: uppercase;
  1067. letter-spacing: 0.5px;
  1068. z-index: 1;
  1069. }
  1070. .news-card-content {
  1071. padding: 1.5rem;
  1072. flex: 1;
  1073. display: flex;
  1074. flex-direction: column;
  1075. }
  1076. .news-date {
  1077. font-size: 0.875rem;
  1078. color: #7f8c8d;
  1079. font-weight: 500;
  1080. margin-bottom: 0.75rem;
  1081. }
  1082. .news-title {
  1083. font-size: 1.25rem;
  1084. color: #2c3e50;
  1085. margin-bottom: 1rem;
  1086. font-weight: 600;
  1087. line-height: 1.4;
  1088. display: -webkit-box;
  1089. -webkit-line-clamp: 2;
  1090. -webkit-box-orient: vertical;
  1091. overflow: hidden;
  1092. }
  1093. .news-summary {
  1094. font-size: 0.875rem;
  1095. color: #7f8c8d;
  1096. line-height: 1.6;
  1097. margin-bottom: 1.5rem;
  1098. flex: 1;
  1099. display: -webkit-box;
  1100. -webkit-line-clamp: 3;
  1101. -webkit-box-orient: vertical;
  1102. overflow: hidden;
  1103. }
  1104. .news-footer {
  1105. display: flex;
  1106. align-items: center;
  1107. gap: 0.5rem;
  1108. }
  1109. .read-more {
  1110. font-size: 0.875rem;
  1111. color: #326ee2;
  1112. font-weight: 500;
  1113. transition: all 0.3s ease;
  1114. display: flex;
  1115. align-items: center;
  1116. gap: 0.5rem;
  1117. }
  1118. .news-card:hover .read-more {
  1119. color: #64b5f6;
  1120. transform: translateX(5px);
  1121. }
  1122. /* 应用成效 */
  1123. .application-effects {
  1124. margin-bottom: 2rem;
  1125. }
  1126. .news-list-container {
  1127. background: white;
  1128. border-radius: 16px;
  1129. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  1130. overflow: hidden;
  1131. }
  1132. .news-list-item {
  1133. display: flex;
  1134. gap: 2rem;
  1135. padding: 1.5rem 2rem;
  1136. border-bottom: 1px solid #f0f0f0;
  1137. transition: all 0.3s ease;
  1138. cursor: pointer;
  1139. align-items: center;
  1140. }
  1141. .news-list-item:last-child {
  1142. border-bottom: none;
  1143. }
  1144. .news-list-item:hover {
  1145. background: #f8f9fa;
  1146. transform: translateX(5px);
  1147. }
  1148. .news-list-content {
  1149. flex: 1;
  1150. }
  1151. .news-list-header {
  1152. display: flex;
  1153. gap: 1rem;
  1154. margin-bottom: 0.75rem;
  1155. align-items: center;
  1156. }
  1157. .news-list-category {
  1158. background: #e3f2fd;
  1159. color: #1565c0;
  1160. padding: 0.25rem 0.75rem;
  1161. border-radius: 12px;
  1162. font-size: 0.75rem;
  1163. font-weight: 600;
  1164. text-transform: uppercase;
  1165. letter-spacing: 0.5px;
  1166. }
  1167. .news-list-date {
  1168. font-size: 0.875rem;
  1169. color: #7f8c8d;
  1170. font-weight: 500;
  1171. }
  1172. .news-list-title {
  1173. font-size: 1.25rem;
  1174. color: #2c3e50;
  1175. margin-bottom: 0.75rem;
  1176. font-weight: 600;
  1177. line-height: 1.4;
  1178. }
  1179. .news-list-summary {
  1180. font-size: 0.875rem;
  1181. color: #7f8c8d;
  1182. line-height: 1.6;
  1183. margin-bottom: 1rem;
  1184. display: -webkit-box;
  1185. -webkit-line-clamp: 2;
  1186. -webkit-box-orient: vertical;
  1187. overflow: hidden;
  1188. }
  1189. .news-list-footer {
  1190. display: flex;
  1191. align-items: center;
  1192. gap: 0.5rem;
  1193. }
  1194. .news-list-image {
  1195. width: 200px;
  1196. height: 120px;
  1197. border-radius: 8px;
  1198. overflow: hidden;
  1199. flex-shrink: 0;
  1200. }
  1201. .list-image {
  1202. width: 100%;
  1203. height: 100%;
  1204. object-fit: cover;
  1205. transition: transform 0.3s ease;
  1206. }
  1207. .news-list-item:hover .list-image {
  1208. transform: scale(1.05);
  1209. }
  1210. /* 新闻详情弹窗样式 */
  1211. .news-detail-dialog :deep(.el-dialog__header) {
  1212. background: #326ee2;
  1213. color: white;
  1214. padding: 1.5rem 2rem;
  1215. }
  1216. .news-detail-dialog :deep(.el-dialog__title) {
  1217. color: white;
  1218. font-size: 1.25rem;
  1219. font-weight: 600;
  1220. }
  1221. .news-detail-dialog :deep(.el-dialog__headerbtn) {
  1222. top: 1.5rem;
  1223. }
  1224. .news-detail-dialog :deep(.el-dialog__headerbtn .el-dialog__close) {
  1225. color: white;
  1226. font-size: 1.5rem;
  1227. }
  1228. .news-detail-content {
  1229. padding: 1rem;
  1230. }
  1231. .news-detail-meta {
  1232. display: flex;
  1233. justify-content: space-between;
  1234. align-items: center;
  1235. margin-bottom: 2rem;
  1236. padding-bottom: 1.5rem;
  1237. border-bottom: 1px solid #f0f5ff;
  1238. flex-wrap: wrap;
  1239. gap: 1rem;
  1240. }
  1241. .news-detail-category {
  1242. background: #326ee2;
  1243. color: white;
  1244. padding: 0.5rem 1rem;
  1245. border-radius: 12px;
  1246. font-size: 0.875rem;
  1247. font-weight: 600;
  1248. text-transform: uppercase;
  1249. letter-spacing: 0.5px;
  1250. }
  1251. .news-detail-date {
  1252. font-size: 0.875rem;
  1253. color: #7f8c8d;
  1254. font-weight: 500;
  1255. }
  1256. .news-detail-author {
  1257. font-size: 0.875rem;
  1258. color: #326ee2;
  1259. font-weight: 500;
  1260. }
  1261. .news-detail-body {
  1262. margin-bottom: 2rem;
  1263. }
  1264. .news-detail-content {
  1265. font-size: 1rem;
  1266. color: #34495e;
  1267. line-height: 1.8;
  1268. margin-bottom: 2rem;
  1269. padding: 1.5rem;
  1270. background: #f8f9fa;
  1271. border-radius: 8px;
  1272. }
  1273. .news-detail-highlight {
  1274. display: flex;
  1275. flex-direction: column;
  1276. align-items: center;
  1277. padding: 2rem;
  1278. background: white;
  1279. border-radius: 12px;
  1280. box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
  1281. }
  1282. .highlight-number {
  1283. font-size: 3rem;
  1284. color: #326ee2;
  1285. font-weight: 700;
  1286. margin-bottom: 1rem;
  1287. }
  1288. .highlight-label {
  1289. font-size: 1rem;
  1290. color: #7f8c8d;
  1291. line-height: 1.6;
  1292. text-align: center;
  1293. }
  1294. .news-detail-footer {
  1295. text-align: right;
  1296. }
  1297. /* 案例展示 */
  1298. .case-study {
  1299. background: #f8f9fa;
  1300. padding: 1.5rem 1.5rem 1.5rem;
  1301. border-radius: 16px;
  1302. box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
  1303. margin-bottom: 3rem;
  1304. min-height: 700px; /* 调整最小高度 */
  1305. }
  1306. .case-viewer-container {
  1307. display: flex;
  1308. gap: 1rem;
  1309. margin-top: 0.5rem;
  1310. height: calc(100% - 50px); /* 计算高度以铺满卡片 */
  1311. align-items: flex-start;
  1312. }
  1313. /* 媒体展示区域 */
  1314. .media-display {
  1315. width: 100%; /* 全屏宽度 */
  1316. height: 100%;
  1317. display: flex;
  1318. flex-direction: column;
  1319. justify-content: center;
  1320. align-items: center;
  1321. background: transparent;
  1322. padding: 0;
  1323. overflow: hidden;
  1324. }
  1325. .media-container {
  1326. width: 100%;
  1327. height: 100%;
  1328. display: flex;
  1329. flex-direction: column;
  1330. align-items: center;
  1331. }
  1332. .media-content {
  1333. width: 100%;
  1334. height: 100%;
  1335. background: transparent;
  1336. overflow: hidden;
  1337. position: relative;
  1338. border-radius: 12px;
  1339. }
  1340. .image-display {
  1341. width: 100%;
  1342. height: 100%;
  1343. display: flex;
  1344. justify-content: center;
  1345. align-items: center;
  1346. background: white;
  1347. overflow: hidden;
  1348. border-radius: 12px;
  1349. }
  1350. .display-image {
  1351. width: 100%;
  1352. height: 100%;
  1353. object-fit: cover; /* 覆盖整个区域,可能会裁剪 */
  1354. border-radius: 12px;
  1355. }
  1356. .video-display {
  1357. width: 100%;
  1358. height: 100%;
  1359. display: flex;
  1360. justify-content: center;
  1361. align-items: center;
  1362. background: white;
  1363. overflow: hidden;
  1364. border-radius: 12px;
  1365. }
  1366. .display-video {
  1367. width: 100%;
  1368. height: 100%;
  1369. object-fit: cover; /* 覆盖整个区域,可能会裁剪 */
  1370. border-radius: 12px;
  1371. }
  1372. /* 视频播放按钮 */
  1373. .video-play-button {
  1374. position: absolute;
  1375. top: 50%;
  1376. left: 50%;
  1377. transform: translate(-50%, -50%);
  1378. width: 160px;
  1379. height: 160px;
  1380. background: rgba(0, 0, 0, 0.7);
  1381. border-radius: 50%;
  1382. display: flex;
  1383. justify-content: center;
  1384. align-items: center;
  1385. cursor: pointer;
  1386. transition: all 0.3s ease;
  1387. z-index: 10;
  1388. box-shadow: 0 0 30px rgba(0, 0, 0, 0.5);
  1389. }
  1390. .video-play-button:hover {
  1391. background: rgba(0, 0, 0, 0.85);
  1392. transform: translate(-50%, -50%) scale(1.15);
  1393. box-shadow: 0 0 40px rgba(0, 0, 0, 0.7);
  1394. }
  1395. .play-icon {
  1396. font-size: 5rem;
  1397. color: white;
  1398. margin-left: 10px;
  1399. text-shadow: 0 0 10px rgba(255, 255, 255, 0.5);
  1400. }
  1401. .media-info {
  1402. background: white;
  1403. border-radius: 8px;
  1404. padding: 1.5rem;
  1405. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
  1406. border: 1px solid #e9ecef;
  1407. }
  1408. .media-title {
  1409. font-size: 1.5rem;
  1410. color: #2c3e50;
  1411. margin-bottom: 1rem;
  1412. font-weight: 600;
  1413. line-height: 1.4;
  1414. padding-bottom: 0.75rem;
  1415. border-bottom: 2px solid #326ee2;
  1416. }
  1417. .media-description {
  1418. font-size: 1.1rem;
  1419. color: #34495e;
  1420. line-height: 1.8;
  1421. }
  1422. /* 未选择状态 */
  1423. .no-selection {
  1424. text-align: center;
  1425. color: #7f8c8d;
  1426. }
  1427. .no-selection-icon {
  1428. font-size: 4rem;
  1429. color: #dee2e6;
  1430. margin-bottom: 1rem;
  1431. }
  1432. .no-selection p {
  1433. font-size: 1.1rem;
  1434. line-height: 1.6;
  1435. }
  1436. /* 媒体查看器样式 */
  1437. .media-viewer-dialog :deep(.el-dialog__header) {
  1438. background: #326ee2;
  1439. color: white;
  1440. padding: 1.5rem 2rem;
  1441. }
  1442. .media-viewer-dialog :deep(.el-dialog__title) {
  1443. color: white;
  1444. font-size: 1.25rem;
  1445. font-weight: 600;
  1446. }
  1447. .media-viewer-dialog :deep(.el-dialog__headerbtn) {
  1448. top: 1.5rem;
  1449. }
  1450. .media-viewer-dialog :deep(.el-dialog__headerbtn .el-dialog__close) {
  1451. color: white;
  1452. font-size: 1.5rem;
  1453. }
  1454. .media-viewer-content {
  1455. padding: 1rem;
  1456. }
  1457. .image-viewer {
  1458. display: flex;
  1459. justify-content: center;
  1460. align-items: center;
  1461. min-height: 400px;
  1462. margin-bottom: 1.5rem;
  1463. }
  1464. .viewer-image {
  1465. max-width: 100%;
  1466. max-height: 600px;
  1467. object-fit: contain;
  1468. cursor: pointer;
  1469. transition: transform 0.3s ease;
  1470. }
  1471. .viewer-image:hover {
  1472. transform: scale(1.02);
  1473. }
  1474. .video-viewer {
  1475. display: flex;
  1476. justify-content: center;
  1477. align-items: center;
  1478. min-height: 400px;
  1479. margin-bottom: 1.5rem;
  1480. }
  1481. .viewer-video {
  1482. max-width: 100%;
  1483. max-height: 600px;
  1484. object-fit: contain;
  1485. border-radius: 8px;
  1486. }
  1487. .media-description {
  1488. font-size: 1rem;
  1489. color: #34495e;
  1490. line-height: 1.6;
  1491. margin-bottom: 2rem;
  1492. padding: 1.5rem;
  1493. background: #f8f9fa;
  1494. border-radius: 8px;
  1495. text-align: center;
  1496. }
  1497. .media-navigation {
  1498. display: flex;
  1499. justify-content: center;
  1500. align-items: center;
  1501. gap: 1rem;
  1502. margin-top: 2rem;
  1503. }
  1504. .media-navigation .el-button {
  1505. min-width: 100px;
  1506. border-radius: 20px;
  1507. }
  1508. .media-index {
  1509. font-size: 1rem;
  1510. color: #2c3e50;
  1511. font-weight: 500;
  1512. padding: 0 1rem;
  1513. }
  1514. /* 返回按钮 */
  1515. .back-button-container {
  1516. text-align: center;
  1517. margin-top: 3rem;
  1518. }
  1519. /* 页面底部 */
  1520. .page-footer {
  1521. background: white;
  1522. color: #326ee2;
  1523. text-align: center;
  1524. padding: 1.5rem 0;
  1525. height: 20px;
  1526. display: flex;
  1527. align-items: center;
  1528. justify-content: center;
  1529. border-top: 1px solid #f0f5ff;
  1530. }
  1531. .page-footer p {
  1532. margin: 0;
  1533. font-size: 0.875rem;
  1534. font-weight: 400;
  1535. line-height: 1.5;
  1536. }
  1537. /* 响应式设计 */
  1538. @media (max-width: 768px) {
  1539. .project-cover {
  1540. height: 250px;
  1541. }
  1542. .project-info {
  1543. padding: 2rem;
  1544. }
  1545. .project-title {
  1546. font-size: 2rem;
  1547. }
  1548. .project-subtitle {
  1549. font-size: 1rem;
  1550. }
  1551. .project-meta {
  1552. gap: 1.5rem;
  1553. }
  1554. .project-overview,
  1555. .technical-highlights,
  1556. .case-study {
  1557. padding: 2rem;
  1558. }
  1559. .features-grid,
  1560. .case-study-grid {
  1561. grid-template-columns: 1fr;
  1562. }
  1563. .achievements-grid {
  1564. grid-template-columns: repeat(2, 1fr);
  1565. }
  1566. /* 手风琴组件响应式调整 */
  1567. .horizontal-accordion-container {
  1568. height: 300px;
  1569. }
  1570. .accordion-item {
  1571. min-width: 100px;
  1572. max-width: 100px;
  1573. }
  1574. .accordion-header {
  1575. padding: 1rem;
  1576. font-size: 0.9rem;
  1577. }
  1578. .collapse-icon {
  1579. font-size: 1.2rem;
  1580. margin-bottom: 0.5rem;
  1581. }
  1582. .accordion-content {
  1583. left: 100px;
  1584. width: calc(100% - 100px);
  1585. padding: 1.5rem;
  1586. }
  1587. .collapse-description {
  1588. font-size: 0.9rem;
  1589. margin-bottom: 1rem;
  1590. }
  1591. .collapse-features li {
  1592. font-size: 0.85rem;
  1593. padding: 0.5rem 0;
  1594. }
  1595. }
  1596. @media (max-width: 480px) {
  1597. .project-cover {
  1598. height: 200px;
  1599. }
  1600. .project-info {
  1601. padding: 1.5rem;
  1602. }
  1603. .project-title {
  1604. font-size: 1.5rem;
  1605. }
  1606. .project-subtitle {
  1607. font-size: 0.875rem;
  1608. }
  1609. .project-meta {
  1610. gap: 1rem;
  1611. flex-direction: column;
  1612. }
  1613. .project-overview,
  1614. .technical-highlights,
  1615. .case-study {
  1616. padding: 1.5rem;
  1617. }
  1618. .section-title {
  1619. font-size: 1.5rem;
  1620. }
  1621. .achievements-grid {
  1622. grid-template-columns: 1fr;
  1623. }
  1624. /* 手风琴组件在小屏幕上垂直排列 */
  1625. .horizontal-accordion-container {
  1626. height: auto;
  1627. flex-direction: column;
  1628. border: none;
  1629. }
  1630. .accordion-item {
  1631. min-width: 100%;
  1632. max-width: 100%;
  1633. height: 60px;
  1634. border-right: none;
  1635. border-bottom: 1px solid rgba(255, 255, 255, 0.2);
  1636. }
  1637. .accordion-item:nth-child(1),
  1638. .accordion-item:nth-child(2),
  1639. .accordion-item:nth-child(3),
  1640. .accordion-item:nth-child(4) {
  1641. background-size: cover;
  1642. background-position: center;
  1643. }
  1644. .accordion-header {
  1645. flex-direction: row;
  1646. padding: 1rem;
  1647. font-size: 1rem;
  1648. text-align: left;
  1649. }
  1650. .collapse-icon {
  1651. font-size: 1.2rem;
  1652. margin-bottom: 0;
  1653. margin-right: 1rem;
  1654. }
  1655. .accordion-content {
  1656. position: relative;
  1657. top: 0;
  1658. left: 0;
  1659. width: 100%;
  1660. height: auto;
  1661. padding: 1rem;
  1662. opacity: 0;
  1663. visibility: hidden;
  1664. max-height: 0;
  1665. overflow: hidden;
  1666. }
  1667. .accordion-item.active {
  1668. flex: none;
  1669. height: auto;
  1670. max-width: 100%;
  1671. }
  1672. .accordion-item.active .accordion-content {
  1673. opacity: 1;
  1674. visibility: visible;
  1675. max-height: 500px;
  1676. }
  1677. .accordion-item.active .accordion-header {
  1678. opacity: 1;
  1679. visibility: visible;
  1680. transform: none;
  1681. }
  1682. .collapse-description {
  1683. font-size: 0.9rem;
  1684. margin-bottom: 1rem;
  1685. }
  1686. .collapse-features li {
  1687. font-size: 0.85rem;
  1688. padding: 0.5rem 0;
  1689. }
  1690. }
  1691. </style>