detail-camera.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844
  1. <template>
  2. <view class="container">
  3. <!-- 设备头部信息区域 -->
  4. <view class="device-header">
  5. <view class="device-info-row">
  6. <text class="device-name">{{ deviceInfo.name }}</text>
  7. <view
  8. class="status-tag"
  9. :class="deviceInfo.status === 'online' ? 'status-online' : 'status-offline'"
  10. >
  11. <text class="status-dot" :class="{'offline-dot': deviceInfo.status === 'offline'}"></text>
  12. {{ deviceInfo.status === 'online' ? '在线' : '离线' }}
  13. </view>
  14. </view>
  15. <view class="device-meta-row">
  16. <view class="device-meta-item">
  17. <text class="meta-label">设备编号:</text>
  18. <text class="meta-value">{{ deviceInfo.deviceId }}</text>
  19. </view>
  20. <view class="device-meta-item">
  21. <text class="meta-label">安装位置:</text>
  22. <text class="meta-value">{{ deviceInfo.location }}</text>
  23. </view>
  24. <view class="device-meta-item">
  25. <text class="meta-label">最近更新:</text>
  26. <text class="meta-value">{{ deviceInfo.lastUpdate }}</text>
  27. </view>
  28. </view>
  29. </view>
  30. <!-- 视频预览区域 -->
  31. <view class="video-section">
  32. <view class="video-container">
  33. <image v-if="!isPlaying" src="/static/images/video-placeholder.jpg" mode="aspectFill" class="video-placeholder"></image>
  34. <video
  35. v-else
  36. id="videoPlayer"
  37. :src="deviceInfo.streamUrl"
  38. class="video-player"
  39. object-fit="cover"
  40. autoplay
  41. :controls="false"
  42. :show-center-play-btn="false"
  43. :show-fullscreen-btn="false"
  44. :show-play-btn="false"
  45. :enable-progress-gesture="false"
  46. @error="handleVideoError"
  47. ></video>
  48. <!-- 视频控制层 -->
  49. <view class="video-controls">
  50. <view class="control-row top-controls">
  51. <view class="signal-indicator">
  52. <text class="signal-icon iconfont icon-signal"></text>
  53. <text class="signal-text">信号良好</text>
  54. </view>
  55. <view class="video-time">{{ currentTime }}</view>
  56. </view>
  57. <view class="control-row center-controls">
  58. <view class="center-button" @click="togglePlayState">
  59. <view class="play-icon" v-if="!isPlaying">
  60. <text class="iconfont icon-play-fill"></text>
  61. </view>
  62. <view class="pause-icon" v-else>
  63. <text class="iconfont icon-pause-fill"></text>
  64. </view>
  65. </view>
  66. </view>
  67. <view class="control-row bottom-controls">
  68. <view class="control-button" @click="toggleMute">
  69. <text :class="isMuted ? 'iconfont icon-volume-off-fill' : 'iconfont icon-volume-up-fill'"></text>
  70. </view>
  71. <view class="control-button" @click="takeScreenshot">
  72. <text class="iconfont icon-camera-fill"></text>
  73. </view>
  74. <view class="control-button" @click="toggleRecording">
  75. <text :class="isRecording ? 'iconfont icon-stop-fill' : 'iconfont icon-record-fill'"></text>
  76. </view>
  77. <view class="control-button" @click="toggleFullscreen">
  78. <text class="iconfont icon-fullscreen"></text>
  79. </view>
  80. </view>
  81. </view>
  82. </view>
  83. </view>
  84. <!-- 云台控制区域 -->
  85. <view class="ptz-section">
  86. <view class="section-title">云台控制</view>
  87. <view class="ptz-container">
  88. <view class="ptz-controls">
  89. <!-- 上方向箭头 -->
  90. <view class="ptz-arrow ptz-up" @touchstart="controlPTZ('up', true)" @touchend="controlPTZ('up', false)">
  91. <text class="iconfont icon-arrow-up-fill"></text>
  92. </view>
  93. <!-- 左上方向箭头 -->
  94. <view class="ptz-arrow ptz-left-up" @touchstart="controlPTZ('leftUp', true)" @touchend="controlPTZ('leftUp', false)">
  95. <text class="iconfont icon-arrow-left-up"></text>
  96. </view>
  97. <!-- 右上方向箭头 -->
  98. <view class="ptz-arrow ptz-right-up" @touchstart="controlPTZ('rightUp', true)" @touchend="controlPTZ('rightUp', false)">
  99. <text class="iconfont icon-arrow-right-up"></text>
  100. </view>
  101. <!-- 左方向箭头 -->
  102. <view class="ptz-arrow ptz-left" @touchstart="controlPTZ('left', true)" @touchend="controlPTZ('left', false)">
  103. <text class="iconfont icon-arrow-left-fill"></text>
  104. </view>
  105. <!-- 中心点 -->
  106. <view class="ptz-center">
  107. <text class="iconfont icon-dot"></text>
  108. </view>
  109. <!-- 右方向箭头 -->
  110. <view class="ptz-arrow ptz-right" @touchstart="controlPTZ('right', true)" @touchend="controlPTZ('right', false)">
  111. <text class="iconfont icon-arrow-right-fill"></text>
  112. </view>
  113. <!-- 左下方向箭头 -->
  114. <view class="ptz-arrow ptz-left-down" @touchstart="controlPTZ('leftDown', true)" @touchend="controlPTZ('leftDown', false)">
  115. <text class="iconfont icon-arrow-left-down"></text>
  116. </view>
  117. <!-- 下方向箭头 -->
  118. <view class="ptz-arrow ptz-down" @touchstart="controlPTZ('down', true)" @touchend="controlPTZ('down', false)">
  119. <text class="iconfont icon-arrow-down-fill"></text>
  120. </view>
  121. <!-- 右下方向箭头 -->
  122. <view class="ptz-arrow ptz-right-down" @touchstart="controlPTZ('rightDown', true)" @touchend="controlPTZ('rightDown', false)">
  123. <text class="iconfont icon-arrow-right-down"></text>
  124. </view>
  125. </view>
  126. <!-- 变焦控制 -->
  127. <view class="zoom-controls">
  128. <view class="zoom-button zoom-in" @touchstart="controlZoom('in', true)" @touchend="controlZoom('in', false)">
  129. <text class="iconfont icon-plus"></text>
  130. </view>
  131. <view class="zoom-label">
  132. <text>变焦</text>
  133. </view>
  134. <view class="zoom-button zoom-out" @touchstart="controlZoom('out', true)" @touchend="controlZoom('out', false)">
  135. <text class="iconfont icon-minus"></text>
  136. </view>
  137. </view>
  138. </view>
  139. </view>
  140. <!-- 快捷功能按钮 -->
  141. <view class="quick-actions">
  142. <view class="action-button" @click="takeScreenshot">
  143. <view class="action-icon">
  144. <text class="iconfont icon-camera-fill"></text>
  145. </view>
  146. <text class="action-text">截图保存</text>
  147. </view>
  148. <view class="action-button" @click="toggleRecording">
  149. <view class="action-icon">
  150. <text :class="isRecording ? 'iconfont icon-stop-fill' : 'iconfont icon-record-fill'"></text>
  151. </view>
  152. <text class="action-text">{{ isRecording ? '停止录像' : '开始录像' }}</text>
  153. </view>
  154. <view class="action-button" @click="copyStreamUrl">
  155. <view class="action-icon">
  156. <text class="iconfont icon-copy"></text>
  157. </view>
  158. <text class="action-text">复制流地址</text>
  159. </view>
  160. <view class="action-button" @click="toggleVoiceIntercom">
  161. <view class="action-icon">
  162. <text :class="isVoiceActive ? 'iconfont icon-mic-fill' : 'iconfont icon-mic'"></text>
  163. </view>
  164. <text class="action-text">语音对讲</text>
  165. </view>
  166. </view>
  167. <!-- 历史视频/录像入口 -->
  168. <view class="history-section">
  169. <view class="section-title">历史录像</view>
  170. <view class="history-list">
  171. <view
  172. v-for="(item, index) in recordHistory"
  173. :key="index"
  174. class="history-item"
  175. @click="playHistoryVideo(item)"
  176. >
  177. <view class="history-icon">
  178. <text class="iconfont icon-video-history"></text>
  179. </view>
  180. <view class="history-info">
  181. <text class="history-time">{{ item.startTime }}</text>
  182. <text class="history-duration">时长: {{ item.duration }}</text>
  183. </view>
  184. <view class="history-action">
  185. <text class="iconfont icon-play"></text>
  186. </view>
  187. </view>
  188. </view>
  189. </view>
  190. </view>
  191. </template>
  192. <script>
  193. export default {
  194. data() {
  195. return {
  196. deviceInfo: {
  197. deviceId: 'DEV1001',
  198. name: '监控设备-1',
  199. status: 'online',
  200. location: '西区B2地块',
  201. lastUpdate: '5分钟前',
  202. streamUrl: 'https://demo-rtsp-server-2h4n.onrender.com/stream.mp4',
  203. alertCount: 2
  204. },
  205. isPlaying: false,
  206. isMuted: false,
  207. isRecording: false,
  208. isFullscreen: false,
  209. isVoiceActive: false,
  210. currentTime: '14:30:25',
  211. // 模拟历史录像数据
  212. recordHistory: [
  213. { id: 1, startTime: '今天 12:30', duration: '00:15:30', url: '' },
  214. { id: 2, startTime: '今天 10:15', duration: '00:05:22', url: '' },
  215. { id: 3, startTime: '昨天 18:45', duration: '00:30:10', url: '' },
  216. { id: 4, startTime: '昨天 14:20', duration: '00:10:05', url: '' }
  217. ],
  218. videoContext: null
  219. }
  220. },
  221. onReady() {
  222. // 获取视频实例
  223. this.videoContext = uni.createVideoContext('videoPlayer')
  224. // 设置页面标题
  225. uni.setNavigationBarTitle({
  226. title: this.deviceInfo.name
  227. })
  228. // 模拟更新时间
  229. this.startTimeUpdate()
  230. },
  231. onLoad(options) {
  232. // 如果有传入设备ID,则获取设备信息
  233. if (options && options.id) {
  234. this.fetchDeviceInfo(options.id)
  235. }
  236. },
  237. methods: {
  238. // 获取设备信息
  239. fetchDeviceInfo(deviceId) {
  240. // 这里应该是API请求,暂时用模拟数据
  241. console.log('获取设备信息:', deviceId)
  242. // 模拟异步获取数据
  243. setTimeout(() => {
  244. // 实际应该是API请求结果
  245. }, 500)
  246. },
  247. // 播放/暂停切换
  248. togglePlayState() {
  249. this.isPlaying = !this.isPlaying
  250. if (this.isPlaying) {
  251. setTimeout(() => {
  252. this.videoContext && this.videoContext.play()
  253. }, 300)
  254. } else {
  255. this.videoContext && this.videoContext.pause()
  256. }
  257. },
  258. // 静音切换
  259. toggleMute() {
  260. this.isMuted = !this.isMuted
  261. if (this.videoContext) {
  262. if (this.isMuted) {
  263. this.videoContext.mute()
  264. } else {
  265. this.videoContext.unmute()
  266. }
  267. }
  268. },
  269. // 录像切换
  270. toggleRecording() {
  271. this.isRecording = !this.isRecording
  272. if (this.isRecording) {
  273. // 模拟开始录像
  274. uni.showToast({
  275. title: '开始录像',
  276. icon: 'none'
  277. })
  278. } else {
  279. // 模拟结束录像并保存
  280. uni.showToast({
  281. title: '录像已保存',
  282. icon: 'success'
  283. })
  284. }
  285. },
  286. // 全屏切换
  287. toggleFullscreen() {
  288. if (this.videoContext) {
  289. if (!this.isFullscreen) {
  290. this.videoContext.requestFullScreen()
  291. } else {
  292. this.videoContext.exitFullScreen()
  293. }
  294. this.isFullscreen = !this.isFullscreen
  295. }
  296. },
  297. // 截图
  298. takeScreenshot() {
  299. // 模拟截图功能
  300. uni.showLoading({
  301. title: '截图中...'
  302. })
  303. setTimeout(() => {
  304. uni.hideLoading()
  305. uni.showToast({
  306. title: '截图已保存',
  307. icon: 'success'
  308. })
  309. }, 1000)
  310. },
  311. // 复制流地址
  312. copyStreamUrl() {
  313. uni.setClipboardData({
  314. data: this.deviceInfo.streamUrl,
  315. success: () => {
  316. uni.showToast({
  317. title: '地址已复制',
  318. icon: 'success'
  319. })
  320. }
  321. })
  322. },
  323. // 语音对讲
  324. toggleVoiceIntercom() {
  325. this.isVoiceActive = !this.isVoiceActive
  326. if (this.isVoiceActive) {
  327. uni.showToast({
  328. title: '语音对讲已开启',
  329. icon: 'none'
  330. })
  331. } else {
  332. uni.showToast({
  333. title: '语音对讲已关闭',
  334. icon: 'none'
  335. })
  336. }
  337. },
  338. // 云台控制
  339. controlPTZ(direction, isStart) {
  340. // 模拟云台控制
  341. if (isStart) {
  342. console.log(`开始控制云台: ${direction}`)
  343. uni.vibrateShort() // 短震动反馈
  344. } else {
  345. console.log(`停止控制云台: ${direction}`)
  346. }
  347. },
  348. // 变焦控制
  349. controlZoom(type, isStart) {
  350. // 模拟变焦控制
  351. if (isStart) {
  352. console.log(`开始控制变焦: ${type}`)
  353. uni.vibrateShort() // 短震动反馈
  354. } else {
  355. console.log(`停止控制变焦: ${type}`)
  356. }
  357. },
  358. // 播放历史视频
  359. playHistoryVideo(item) {
  360. uni.showToast({
  361. title: `播放录像: ${item.startTime}`,
  362. icon: 'none'
  363. })
  364. },
  365. // 处理视频错误
  366. handleVideoError(e) {
  367. console.error('视频播放错误:', e)
  368. uni.showToast({
  369. title: '视频播放出错,请稍后再试',
  370. icon: 'none'
  371. })
  372. },
  373. // 更新时间
  374. startTimeUpdate() {
  375. // 模拟时间更新
  376. setInterval(() => {
  377. const now = new Date()
  378. const hours = String(now.getHours()).padStart(2, '0')
  379. const minutes = String(now.getMinutes()).padStart(2, '0')
  380. const seconds = String(now.getSeconds()).padStart(2, '0')
  381. this.currentTime = `${hours}:${minutes}:${seconds}`
  382. }, 1000)
  383. }
  384. }
  385. }
  386. </script>
  387. <style>
  388. /* 基础样式 */
  389. .container {
  390. display: flex;
  391. flex-direction: column;
  392. min-height: 100vh;
  393. background-color: #F9FCFA;
  394. padding-bottom: 30rpx;
  395. }
  396. /* 设备头部样式 */
  397. .device-header {
  398. background-color: #FFFFFF;
  399. border-radius: 20rpx;
  400. padding: 26rpx 30rpx;
  401. margin: 20rpx 30rpx;
  402. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
  403. }
  404. .device-info-row {
  405. display: flex;
  406. justify-content: space-between;
  407. align-items: center;
  408. margin-bottom: 16rpx;
  409. }
  410. .device-name {
  411. font-size: 34rpx;
  412. color: #333333;
  413. font-weight: 600;
  414. }
  415. .status-tag {
  416. padding: 6rpx 16rpx 6rpx 32rpx;
  417. border-radius: 8rpx;
  418. font-size: 24rpx;
  419. font-weight: 500;
  420. flex-shrink: 0;
  421. border: 1rpx solid;
  422. position: relative;
  423. overflow: hidden;
  424. }
  425. .status-online {
  426. background-color: rgba(76, 175, 80, 0.1);
  427. color: #4CAF50;
  428. border-color: rgba(76, 175, 80, 0.3);
  429. }
  430. .status-offline {
  431. background-color: rgba(245, 108, 108, 0.1);
  432. color: #F56C6C;
  433. border-color: rgba(245, 108, 108, 0.3);
  434. padding-left: 32rpx;
  435. }
  436. .status-dot {
  437. position: absolute;
  438. width: 8rpx;
  439. height: 8rpx;
  440. background-color: #4CAF50;
  441. border-radius: 50%;
  442. top: 50%;
  443. left: 16rpx;
  444. transform: translateY(-50%);
  445. box-shadow: 0 0 4rpx rgba(76, 175, 80, 0.8);
  446. animation: blink 1.5s infinite;
  447. display: inline-block;
  448. }
  449. .status-dot.offline-dot {
  450. background-color: #F56C6C;
  451. box-shadow: 0 0 4rpx rgba(245, 108, 108, 0.8);
  452. }
  453. @keyframes blink {
  454. 0% {
  455. opacity: 0.4;
  456. }
  457. 50% {
  458. opacity: 1;
  459. }
  460. 100% {
  461. opacity: 0.4;
  462. }
  463. }
  464. .device-meta-row {
  465. display: flex;
  466. flex-direction: column;
  467. }
  468. .device-meta-item {
  469. display: flex;
  470. font-size: 26rpx;
  471. margin-top: 8rpx;
  472. color: #666666;
  473. }
  474. .meta-label {
  475. color: #999999;
  476. min-width: 140rpx;
  477. }
  478. .meta-value {
  479. color: #666666;
  480. flex: 1;
  481. }
  482. /* 视频预览区域 */
  483. .video-section {
  484. margin: 0 30rpx 20rpx;
  485. }
  486. .video-container {
  487. position: relative;
  488. width: 100%;
  489. height: 400rpx;
  490. background-color: #000000;
  491. border-radius: 16rpx;
  492. overflow: hidden;
  493. box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.1);
  494. }
  495. .video-player, .video-placeholder {
  496. width: 100%;
  497. height: 100%;
  498. }
  499. .video-controls {
  500. position: absolute;
  501. top: 0;
  502. left: 0;
  503. width: 100%;
  504. height: 100%;
  505. display: flex;
  506. flex-direction: column;
  507. justify-content: space-between;
  508. padding: 20rpx;
  509. box-sizing: border-box;
  510. background: linear-gradient(to bottom, rgba(0,0,0,0.3) 0%, rgba(0,0,0,0) 30%, rgba(0,0,0,0) 70%, rgba(0,0,0,0.3) 100%);
  511. }
  512. .control-row {
  513. display: flex;
  514. justify-content: space-between;
  515. align-items: center;
  516. width: 100%;
  517. }
  518. .top-controls {
  519. height: 60rpx;
  520. }
  521. .center-controls {
  522. height: 120rpx;
  523. justify-content: center;
  524. }
  525. .bottom-controls {
  526. height: 60rpx;
  527. justify-content: flex-end;
  528. }
  529. .signal-indicator {
  530. display: flex;
  531. align-items: center;
  532. color: #FFFFFF;
  533. font-size: 24rpx;
  534. }
  535. .signal-icon {
  536. margin-right: 8rpx;
  537. }
  538. .video-time {
  539. color: #FFFFFF;
  540. font-size: 24rpx;
  541. }
  542. .center-button {
  543. width: 80rpx;
  544. height: 80rpx;
  545. border-radius: 50%;
  546. background-color: rgba(255, 255, 255, 0.2);
  547. display: flex;
  548. align-items: center;
  549. justify-content: center;
  550. color: #FFFFFF;
  551. font-size: 40rpx;
  552. }
  553. .control-button {
  554. width: 60rpx;
  555. height: 60rpx;
  556. display: flex;
  557. align-items: center;
  558. justify-content: center;
  559. color: #FFFFFF;
  560. font-size: 36rpx;
  561. margin-left: 20rpx;
  562. }
  563. /* 云台控制区域 */
  564. .ptz-section {
  565. margin: 0 30rpx 20rpx;
  566. background-color: #FFFFFF;
  567. border-radius: 20rpx;
  568. padding: 20rpx;
  569. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
  570. }
  571. .section-title {
  572. font-size: 30rpx;
  573. font-weight: 600;
  574. color: #333333;
  575. margin-bottom: 20rpx;
  576. padding: 0 10rpx;
  577. }
  578. .ptz-container {
  579. display: flex;
  580. justify-content: space-between;
  581. align-items: center;
  582. }
  583. .ptz-controls {
  584. display: grid;
  585. grid-template-columns: repeat(3, 80rpx);
  586. grid-template-rows: repeat(3, 80rpx);
  587. gap: 10rpx;
  588. width: 260rpx;
  589. height: 260rpx;
  590. }
  591. .ptz-arrow {
  592. display: flex;
  593. align-items: center;
  594. justify-content: center;
  595. background-color: #F0F9F0;
  596. border-radius: 50%;
  597. color: #4CAF50;
  598. font-size: 36rpx;
  599. transition: all 0.2s;
  600. }
  601. .ptz-arrow:active {
  602. background-color: #4CAF50;
  603. color: #FFFFFF;
  604. }
  605. .ptz-up { grid-column: 2; grid-row: 1; }
  606. .ptz-left-up { grid-column: 1; grid-row: 1; }
  607. .ptz-right-up { grid-column: 3; grid-row: 1; }
  608. .ptz-left { grid-column: 1; grid-row: 2; }
  609. .ptz-center {
  610. grid-column: 2;
  611. grid-row: 2;
  612. display: flex;
  613. align-items: center;
  614. justify-content: center;
  615. color: #4CAF50;
  616. font-size: 30rpx;
  617. }
  618. .ptz-right { grid-column: 3; grid-row: 2; }
  619. .ptz-left-down { grid-column: 1; grid-row: 3; }
  620. .ptz-down { grid-column: 2; grid-row: 3; }
  621. .ptz-right-down { grid-column: 3; grid-row: 3; }
  622. .zoom-controls {
  623. display: flex;
  624. flex-direction: column;
  625. align-items: center;
  626. margin-right: 20rpx;
  627. }
  628. .zoom-button {
  629. width: 60rpx;
  630. height: 60rpx;
  631. border-radius: 50%;
  632. background-color: #F0F9F0;
  633. display: flex;
  634. align-items: center;
  635. justify-content: center;
  636. color: #4CAF50;
  637. font-size: 36rpx;
  638. margin: 10rpx 0;
  639. transition: all 0.2s;
  640. }
  641. .zoom-button:active {
  642. background-color: #4CAF50;
  643. color: #FFFFFF;
  644. }
  645. .zoom-label {
  646. font-size: 24rpx;
  647. color: #999999;
  648. margin: 5rpx 0;
  649. }
  650. /* 快捷功能按钮 */
  651. .quick-actions {
  652. display: flex;
  653. justify-content: space-between;
  654. margin: 0 30rpx 30rpx;
  655. background-color: #FFFFFF;
  656. border-radius: 20rpx;
  657. padding: 30rpx;
  658. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
  659. }
  660. .action-button {
  661. display: flex;
  662. flex-direction: column;
  663. align-items: center;
  664. width: 120rpx;
  665. }
  666. .action-icon {
  667. width: 80rpx;
  668. height: 80rpx;
  669. border-radius: 50%;
  670. background-color: #F0F9F0;
  671. display: flex;
  672. align-items: center;
  673. justify-content: center;
  674. color: #4CAF50;
  675. font-size: 36rpx;
  676. margin-bottom: 10rpx;
  677. }
  678. .action-text {
  679. font-size: 24rpx;
  680. color: #666666;
  681. text-align: center;
  682. }
  683. /* 历史视频/录像区域 */
  684. .history-section {
  685. margin: 0 30rpx;
  686. background-color: #FFFFFF;
  687. border-radius: 20rpx;
  688. padding: 20rpx;
  689. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
  690. }
  691. .history-list {
  692. display: flex;
  693. flex-direction: column;
  694. }
  695. .history-item {
  696. display: flex;
  697. align-items: center;
  698. padding: 20rpx 10rpx;
  699. border-bottom: 1rpx solid #F5F5F5;
  700. }
  701. .history-item:last-child {
  702. border-bottom: none;
  703. }
  704. .history-icon {
  705. width: 60rpx;
  706. height: 60rpx;
  707. border-radius: 8rpx;
  708. background-color: #F0F9F0;
  709. display: flex;
  710. align-items: center;
  711. justify-content: center;
  712. color: #4CAF50;
  713. font-size: 30rpx;
  714. margin-right: 16rpx;
  715. }
  716. .history-info {
  717. flex: 1;
  718. display: flex;
  719. flex-direction: column;
  720. }
  721. .history-time {
  722. font-size: 28rpx;
  723. color: #333333;
  724. margin-bottom: 6rpx;
  725. }
  726. .history-duration {
  727. font-size: 24rpx;
  728. color: #999999;
  729. }
  730. .history-action {
  731. width: 60rpx;
  732. height: 60rpx;
  733. display: flex;
  734. align-items: center;
  735. justify-content: center;
  736. color: #4CAF50;
  737. font-size: 30rpx;
  738. }
  739. </style>