| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- <template>
- <div class="panel-box risk-warning">
- <div class="corner-deco tl"></div><div class="corner-deco tr"></div>
- <div class="corner-deco bl"></div><div class="corner-deco br"></div>
- <div class="panel-title">实验室实时风险预警</div>
- <div class="panel-content">
- <div class="warning-header">
- <div class="warning-count">{{ totalMonth }}</div>
- <div class="warning-count-label">本月预警响应总数</div>
- <div class="warning-sub-counts">
- <span class="sub-count emergency">应急 {{ emergencyCount }}</span>
- <span class="sub-count violation">违规带离 {{ violationCount }}</span>
- </div>
- </div>
- <div class="warning-scroll-wrap" ref="scrollWrap" @mouseenter="paused=true" @mouseleave="paused=false">
- <div ref="scrollInner" class="warning-scroll-inner">
- <div class="warning-item" v-for="(item, idx) in displayList" :key="idx">
- <div class="w-lab">{{ item.lab }}</div>
- <div class="w-sensor" :style="{ color: categoryColor(item.category) }">异常: {{ item.type }}</div>
- <div class="w-time">{{ item.time }}</div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </template>
- <script>
- import { getRiskWarningList } from '@/api'
- export default {
- name: 'RiskWarning',
- data() {
- return {
- totalMonth: 0,
- emergencyCount: 0,
- violationCount: 0,
- list: [],
- paused: false,
- scrollTimer: null,
- scrollY: 0,
- timer: null
- }
- },
- computed: {
- displayList() {
- if(this.list[2]){
- return [...this.list, ...this.list]
- }else{
- return [...this.list]
- }
- }
- },
- async mounted() {
- await this.loadData()
- this.timer = setInterval(this.loadData, 5 * 60 * 1000)
- },
- beforeDestroy() {
- if (this.scrollTimer) cancelAnimationFrame(this.scrollTimer)
- clearInterval(this.timer)
- },
- methods: {
- async loadData() {
- try {
- const res = await getRiskWarningList()
- this.totalMonth = res.data.totalMonth
- this.emergencyCount = res.data.emergencyCount
- this.violationCount = res.data.violationCount
- this.list = res.data.list
- if (!this.scrollTimer) {
- this.$nextTick(() => setTimeout(() => this.startScroll(), 500))
- }
- } catch (e) {
- console.error('RiskWarning:', e)
- }
- },
- categoryColor(category) {
- if (category === '应急预警') return '#FFB020'
- if (category === '违规带离') return '#A78BFA'
- return '#FFB020'
- },
- startScroll() {
- const wrap = this.$refs.scrollWrap
- const inner = this.$refs.scrollInner
- if (!wrap || !inner) return
- const halfHeight = inner.scrollHeight / 2
- if (halfHeight <= wrap.offsetHeight) return
- const speed = 0.4
- this.scrollY = 0
- const step = () => {
- if (!this.paused) {
- this.scrollY += speed
- if (this.scrollY >= halfHeight) this.scrollY -= halfHeight
- inner.style.transform = `translateY(${-this.scrollY}px)`
- }
- this.scrollTimer = requestAnimationFrame(step)
- }
- this.scrollTimer = requestAnimationFrame(step)
- }
- }
- }
- </script>
- <style lang="scss" scoped>
- .risk-warning {
- display: flex;
- flex-direction: column;
- }
- .warning-header {
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 10px;
- flex-shrink: 0;
- }
- .warning-count {
- font-size: 28px;
- font-weight: 700;
- color: $accent-orange;
- font-family: 'Courier New', monospace;
- text-shadow: 0 0 10px rgba(255,176,32,0.4);
- animation: warnCountGlow 2s ease-in-out infinite;
- }
- @keyframes warnCountGlow {
- 0%, 100% { text-shadow: 0 0 8px rgba(255,176,32,0.3); }
- 50% { text-shadow: 0 0 20px rgba(255,176,32,0.6), 0 0 40px rgba(255,176,32,0.2); }
- }
- .warning-count-label {
- font-size: 12px;
- color: $text-secondary;
- }
- .warning-sub-counts {
- margin-left: auto;
- display: flex;
- gap: 8px;
- }
- .sub-count {
- font-size: 11px;
- padding: 2px 8px;
- border-radius: 10px;
- &.emergency {
- color: #FFB020;
- background: rgba(255, 176, 32, 0.1);
- border: 1px solid rgba(255, 176, 32, 0.3);
- }
- &.violation {
- color: #A78BFA;
- background: rgba(167, 139, 250, 0.1);
- border: 1px solid rgba(167, 139, 250, 0.3);
- }
- }
- .warning-sub-counts {
- margin-left: auto;
- display: flex;
- gap: 8px;
- }
- .sub-count {
- font-size: 11px;
- padding: 2px 8px;
- border-radius: 10px;
- &.emergency {
- color: #FFB020;
- background: rgba(255, 176, 32, 0.12);
- border: 1px solid rgba(255, 176, 32, 0.3);
- }
- &.violation {
- color: #A78BFA;
- background: rgba(167, 139, 250, 0.12);
- border: 1px solid rgba(167, 139, 250, 0.3);
- }
- }
- .warning-scroll-wrap {
- flex: 1;
- overflow: hidden;
- min-height: 0;
- }
- .null-data-text{
- text-align: center;
- line-height:130px;
- }
- .warning-item {
- padding: 8px 12px;
- margin-bottom: 6px;
- border-radius: 4px;
- background: rgba(255,176,32,0.04);
- border-left: 3px solid $accent-orange;
- transition: all 0.3s ease;
- animation: warnBorderPulse 3s ease-in-out infinite;
- &:hover {
- background: rgba(255,176,32,0.08);
- transform: translateX(3px);
- }
- }
- @keyframes warnBorderPulse {
- 0%, 100% { border-left-color: rgba(255,176,32,0.6); }
- 50% { border-left-color: $accent-orange; box-shadow: -2px 0 8px rgba(255,176,32,0.15); }
- }
- .w-lab {
- font-size: 13px;
- font-weight: 600;
- color: #fff;
- }
- .w-sensor {
- font-size: 12px;
- color: $accent-orange;
- margin: 2px 0;
- }
- .w-time {
- font-size: 11px;
- color: $text-secondary;
- margin-top: 4px;
- }
- </style>
|